포스트

2022-12-13-TIL

2022-12-13-TIL

Today I Learned

함수형과 객체지향 패러다임의 차이, Spring Boot 프로젝트의 계층 구조, 그리고 XSS 방어를 정규식으로 할 수 있는지를 찾아봤다.


Functional vs Object-Oriented Programming

두 패러다임은 상태를 다루는 방식에서 가장 크게 갈린다. 객체지향은 상태와 그 상태를 바꾸는 메서드를 객체 안에 함께 두고, 객체 사이의 메시지로 프로그램을 구성한다. 함수형은 불변 데이터와 부수 효과가 없는 순수 함수를 기본 단위로 삼고, 함수를 조합해 데이터를 변환한다.

관점객체지향함수형
기본 단위객체(상태 + 행동)함수
상태객체 내부에서 변경불변 데이터, 새 값을 만든다
재사용상속, 합성, 다형성고차 함수, 함수 합성
동시성공유 상태 동기화가 필요공유 가변 상태가 적어 유리

링크된 글들도 대체로 어느 쪽이 우월하다기보다 문제에 따라 섞어 쓰는 것을 권한다. 자바도 8부터 람다와 Stream API로 함수형 스타일을 일부 받아들였으므로, 도메인 모델은 객체로 표현하고 컬렉션 변환은 함수형으로 쓰는 식의 조합이 자연스럽다.

  • https://circleci.com/blog/functional-vs-object-oriented-programming/?utm_source=google&utm_medium=sem&utm_campaign=sem-google-dg–japac-en-dsa-maxConv-auth-brand&utm_term=g_-c__dsa&utm_content=&gclid=CjwKCAiAv9ucBhBXEiwA6N8nYOKMhZ6039bXtCtVSONIFXIA-PS9ONRC8Wjj9az_kqAnkcj5alQX-RoCFkYQAvD_BwE
  • https://medium.com/@shaistha24/functional-programming-vs-object-oriented-programming-oop-which-is-better-82172e53a526
  • https://www.educative.io/blog/functional-programming-vs-oop
  • https://www.educba.com/functional-programming-vs-oop/
  • https://circleci.com/blog/functional-vs-object-oriented-programming/
  • https://www.geeksforgeeks.org/difference-between-functional-programming-and-object-oriented-programming/
  • https://www.imaginarycloud.com/blog/functional-programming-vs-oop/
  • https://www.techtarget.com/searchapparchitecture/tip/Functional-vs-object-oriented-programming-The-basics

Spring Boot Layered Architecture

Spring Boot 애플리케이션은 보통 Controller(프레젠테이션), Service(비즈니스 로직), Repository(데이터 접근) 계층으로 나눈다. 각 계층은 바로 아래 계층에만 의존하고, 계층 사이에서는 엔티티 대신 DTO를 주고받아 API 형식과 도메인 모델을 분리하는 경우가 많다.

계층형 구조는 이해하기 쉽지만, 서비스가 비대해지고 도메인 객체가 데이터만 가진 빈 껍데기가 되기 쉽다는 비판이 있다. Baeldung의 클린 아키텍처 글은 유스케이스와 엔티티를 프레임워크로부터 떼어 내고 의존 방향을 안쪽으로 향하게 하는 구조를 소개하며, Stack Overflow 토론들은 계층을 지키면서도 도메인 객체에 행동을 두는 방법을 다룬다.

  • https://studygyaan.com/spring-boot/spring-boot-project-folder-structure-and-best-practices
  • https://www.baeldung.com/spring-boot-clean-architecture
  • https://www.geeksforgeeks.org/spring-boot-architecture/
  • https://www.javatpoint.com/spring-boot-architecture
  • https://www.javaguides.net/2020/07/spring-boot-project-architecture.html
  • https://stackoverflow.com/questions/23308241/best-practice-multi-layer-architecture-and-dtos
  • https://stackoverflow.com/questions/34429832/how-to-use-layered-architecture-of-spring-and-still-follow-object-oriented-struc
  • https://climbtheladder.com/10-web-application-architecture-best-practices/

Spring Boot Best Practices

생성자 주입 사용, 설정을 application.yml과 프로파일로 분리, 전역 예외 처리(@ControllerAdvice), 로깅 설정 같은 기본 습관을 모아 둔 글이다.

  • https://www.springcloud.io/post/2022-08/springboot-best-practices/#gsc.tab=0

XSS Prevention in Spring Boot

XSS(Cross-Site Scripting)는 공격자가 넣은 스크립트가 다른 사용자의 브라우저에서 실행되는 취약점이다. OWASP XSS Prevention Cheat Sheet는 모든 변수를 검증한 뒤 출력되는 위치(HTML 본문, 속성, JavaScript, URL)에 맞게 이스케이프하거나 새니타이즈하라고 정리한다. 즉 방어의 중심은 입력 필터보다 출력 인코딩이다.

Spring Boot에서 쓰는 방법은 대략 세 가지다.

  • 템플릿 엔진의 자동 이스케이프(Thymeleaf의 th:text 등)를 그대로 쓴다.
  • JSON API라면 Jackson 직렬화 단계에서 HTML 특수문자를 이스케이프하도록 설정한다.
  • 리치 텍스트처럼 HTML을 허용해야 하면 허용 목록 기반 새니타이저(OWASP Java HTML Sanitizer 등)를 쓴다.

정규식으로 <script> 같은 패턴을 걸러 내는 방식은 OWASP의 XSS Filter Evasion Cheat Sheet가 보여 주듯 대소문자 변형, 인코딩, 이벤트 핸들러 속성 등으로 쉽게 우회된다. 아래 Stack Overflow, Security StackExchange 답변들도 정규식 블랙리스트를 주 방어 수단으로 쓰지 말라고 답한다.

  • https://www.baeldung.com/spring-prevent-xss
  • http://blog.plura.io/?p=7614
  • https://www.stackhawk.com/blog/java-xss/
  • https://stackoverflow.com/questions/50762964/escaping-characters-to-avoid-xss-in-java
  • https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html
  • https://security.stackexchange.com/questions/215005/xss-mitigation-using-regex
  • https://www.quora.com/What-is-a-good-regular-expression-for-preventing-XSS-attacks-in-JavaScript
  • https://gist.github.com/VAEAZE/e802ff02c67557ec6185

Java Regular Expression

자바 정규식은 java.util.regex의 Pattern과 Matcher로 다룬다. Pattern.compile()은 비용이 있으므로 반복해서 쓰는 패턴은 상수로 한 번만 컴파일해 두는 것이 좋다. String.matches(), replaceAll()은 호출할 때마다 내부에서 패턴을 컴파일한다. regexr.com 같은 도구로 패턴을 먼저 시험해 보면 디버깅이 쉽다.

1
2
3
private static final Pattern EMAIL = Pattern.compile("^[\\w.+-]+@[\\w-]+\\.[\\w.]+$");

boolean valid = EMAIL.matcher(input).matches();
  • https://www.w3schools.com/java/java_regex.asp
  • https://www.digitalocean.com/community/tutorials/regular-expression-in-java-regex-example
  • https://coding-factory.tistory.com/529
  • https://coding-factory.tistory.com/819
  • https://regexr.com/
  • https://stackoverflow.com/questions/24723/best-regex-to-catch-xss-cross-site-scripting-attack-in-java
  • https://stackoverflow.com/questions/35421796/preventing-xss-using-regex

Java String to Stream

문자열을 스트림으로 다루려면 String.chars()로 IntStream을 얻는다. 요소가 char가 아니라 int이므로 다시 문자로 다루려면 mapToObj(c -> (char) c)로 변환해야 한다. 이모지처럼 보충 문자(surrogate pair)가 섞일 수 있으면 codePoints()를 쓰는 편이 정확하다.

1
2
3
4
String result = "hello".chars()
        .filter(Character::isLetter)
        .mapToObj(c -> String.valueOf((char) c).toUpperCase())
        .collect(Collectors.joining());
  • https://stackoverflow.com/questions/31977356/using-streams-to-manipulate-a-string
  • https://futurecreator.github.io/2018/08/26/java-8-streams/
  • https://ryan-han.com/post/dev/java-stream/
  • https://www.programcreek.com/2014/01/create-stream-in-java-8/
이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다