2022-05-09-TIL
Today I Learned
프로젝트 구조
Spring Boot는 특정 디렉터리 구조를 강제하지 않는다. 다만 Spring Boot 문서는 @SpringBootApplication이 붙은 메인 클래스를 다른 클래스들보다 위의 루트 패키지에 두라고 권한다. 컴포넌트 스캔과 엔티티 스캔이 메인 클래스의 패키지부터 하위로 진행되기 때문이다.
그 아래는 크게 두 방식이 있다. controller, service, repository처럼 계층별로 나누는 방식과, member, order처럼 도메인별로 나누고 그 안에 계층을 두는 방식이다. 링크된 cheese10yun의 가이드와 Baeldung 글은 도메인별 구조를 권하는데, 기능 하나와 관련된 코드가 한 패키지에 모이고 패키지 간 의존을 관리하기 쉬워서다. 공통 설정, 예외 처리, 유틸은 global이나 common 패키지로 따로 둔다.
1
2
3
4
5
6
7
8
com.example.shop
├── ShopApplication.java
├── member
│ ├── api (컨트롤러, 요청/응답 DTO)
│ ├── application(서비스)
│ └── domain (엔티티, 리포지토리)
├── order
└── global (설정, 공통 예외, 유틸)
- https://www.geeksforgeeks.org/spring-boot-code-structure/#:~:text=There%20is%20no%20specific%20layout,divide%20the%20project%20into%20modules.
- https://velog.io/@recordsbeat/%ED%8C%A8%ED%82%A4%EC%A7%80-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC
- https://medium.com/the-resonant-web/spring-boot-2-0-project-structure-and-best-practices-part-2-7137bdcba7d3
- https://www.baeldung.com/spring-boot-package-structure
- https://cheese10yun.github.io/spring-guide-directory/
Package Naming
Java 언어 명세의 관례에 따르면 패키지 이름은 모두 소문자로 쓰고, 소유한 도메인을 뒤집어 접두어로 삼는다(com.example.shop). 단어를 구분하고 싶어도 카멜 케이스나 하이픈은 쓰지 않는다. 도메인에 하이픈이 있거나 자바 키워드와 겹치는 등 유효한 식별자가 아닌 경우에만 밑줄로 바꾸고, 보통은 orderhistory처럼 붙여 쓰거나 패키지를 한 단계 더 나눈다.
enum을 어디에 둘지에 대한 정해진 관례는 없다. 링크된 질문의 답변들도 enums 같은 종류별 패키지보다, 그 enum을 쓰는 도메인 클래스와 같은 패키지에 두는 편을 권한다.
- https://stackoverflow.com/questions/3179216/what-is-the-convention-for-word-separator-in-java-package-names
- https://stackoverflow.com/questions/36220718/is-there-a-java-convention-for-packaging-enums
static and final
정적 메서드는 재정의(override)되지 않고, 하위 클래스에 같은 시그니처의 정적 메서드를 선언하면 상위 메서드를 가리는(hide) 것일 뿐이다. 호출할 메서드는 실행 시점의 객체가 아니라 컴파일 시점의 타입으로 정해진다. 그래서 static 메서드에 final을 붙여도 다형성 측면에서 달라지는 것이 없고, 하위 클래스가 같은 시그니처의 정적 메서드를 선언하지 못하게 막는 효과만 있다. 링크된 IntelliJ 검사(inspection)는 이런 static final 메서드를 보고해 주는데, 대부분 불필요한 수식어이기 때문이다.
- https://intellij-support.jetbrains.com/hc/en-us/community/posts/206822805-What-is-the-point-of-the-static-method-declared-final-inspection-#:~:text=Reports%20methods%20declared%20final%20and,method%20with%20the%20same%20signature.
@JsonComponent
@JsonComponent는 Spring Boot가 제공하는 애너테이션으로, Jackson의 JsonSerializer/JsonDeserializer 구현 클래스에 붙이면 빈으로 등록되면서 Spring Boot가 구성한 ObjectMapper에 자동으로 등록된다. Module을 직접 만들어 등록하지 않아도 특정 타입(예: 금액, 날짜)의 JSON 표현을 애플리케이션 전체에서 바꿀 수 있다. 직렬화기와 역직렬화기를 하나의 클래스 안에 static 중첩 클래스로 함께 둘 수도 있다.
- https://www.baeldung.com/spring-boot-jsoncomponent
JVM TTL 설정
JVM은 DNS 조회 결과(호스트 이름에 대한 IP)를 일정 시간 캐시한다. AWS SDK for Java 문서는 AWS 리소스의 DNS 항목이 가끔 바뀌므로 JVM의 TTL을 5초로 설정하라고 권한다. 일부 Java 설정에서는 기본값이 JVM을 재시작할 때까지 DNS 항목을 갱신하지 않도록 되어 있어서, 로드 밸런서나 RDS 장애 조치로 IP가 바뀌면 애플리케이션이 옛 IP로 계속 접속할 수 있기 때문이다.
이 값은 시스템 속성이 아니라 보안 속성(networkaddress.cache.ttl)이라 -D 옵션으로는 설정할 수 없다. 애플리케이션 시작 시 코드로 넣거나, java.security 파일에 지정한다.
1
java.security.Security.setProperty("networkaddress.cache.ttl", "5");
java.security는 Java 8은 $JAVA_HOME/jre/lib/security/, Java 11 이상은 $JAVA_HOME/conf/security/ 아래에 있다.
- https://docs.aws.amazon.com/ko_kr/sdk-for-java/v1/developer-guide/java-dg-jvm-ttl.html
Thread Local
ThreadLocal은 스레드마다 독립된 값을 갖는 변수를 제공한다. 같은 ThreadLocal 객체에 set/get을 해도 각 스레드는 자기 값만 본다. 메서드 파라미터로 계속 넘기기 번거로운 요청 단위 정보를 담는 데 쓰이며, Spring의 SecurityContextHolder(인증 정보), TransactionSynchronizationManager(트랜잭션 리소스), RequestContextHolder, 그리고 로깅의 MDC가 모두 ThreadLocal을 기반으로 한다.
주의할 점은 스레드 풀이다. 톰캣 같은 서버는 스레드를 재사용하므로, 요청이 끝날 때 remove()로 값을 지우지 않으면 다음 요청이 이전 사용자의 값을 보게 된다. 또 @Async나 별도 스레드 풀로 작업을 넘기면 값이 전달되지 않으므로 직접 복사해야 한다.
1
2
3
4
5
6
try {
UserContext.set(user);
chain.doFilter(request, response);
} finally {
UserContext.remove();
}
- https://hwannny.tistory.com/95
- https://jenkov.com/tutorials/java-concurrency/threadlocal.html#:~:text=The%20Java%20ThreadLocal%20class%20enables,see%20each%20other’s%20ThreadLocal%20variables.
- https://jaimemin.tistory.com/2007
- https://www.baeldung.com/java-threadlocal
- https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html
- https://www.javatpoint.com/java-threadlocal-class
- https://javacan.tistory.com/entry/ThreadLocalUsage
- https://www.youtube.com/watch?v=sjMe9aecW_A
- https://techblog.woowahan.com/2667/
MySQL Sample Database
SQL 연습이나 예제 작성에 쓸 수 있는 샘플 데이터베이스다. MySQL Tutorial의 classicmodels는 고객, 주문, 주문 상세, 상품, 상품군, 직원, 사무소, 결제 테이블로 이루어진 소매 회사 데이터이고, SQL Server Tutorial은 자전거 판매점 데이터를 제공한다. 조인, 집계, 서브쿼리 예제를 실제와 비슷한 관계 구조에서 연습할 수 있다. MySQL 공식으로는 sakila, world, employees 샘플 DB도 있다.
- https://www.mysqltutorial.org/mysql-sample-database.aspx
- https://www.sqlservertutorial.net/load-sample-database/
댓글
아직 댓글이 없습니다