-
Notifications
You must be signed in to change notification settings - Fork 81
[그리디] 김하늘 Spring JPA (1차) 4, 5, 6 단계 미션 제출합니다. #202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: kimsky247-coder
Are you sure you want to change the base?
Conversation
BackFoxx
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요! 즐거운 연휴 보내셨나요?
entityManager를 사용해서 멋지게 코드를 구현해 주셔서 리뷰할 거리가 많진 않습니다.
대신 Spring과 JPA를 조금 더 심도있게 연구할 수 있도록 키워드를 몇 개 제시해 드렸어요. 한 번 연구해 보세요!
| spring.datasource.url=jdbc:h2:mem:database | ||
|
|
||
| spring.jpa.hibernate.ddl-auto=none | ||
| spring.sql.init.mode=always |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sql.init mode가 always이면서 ddl-auto 값이 none이면, 애플리케이션을 실행할 때마다 테스트 데이터가 중복으로 쌓이지 않나요? 😲
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재 H2 In-Memory 모드를 사용 중이기 때문에 서버 종료 시 데이터가 휘발되어 중복 문제가 발생하지 않고 있었습니다. 그러나 영구 저장소를 사용하게 될 경우, ddl-auto=none 상태에서 데이터 중복 적재 및 PK 충돌 오류가 발생할 수 있다는 것을 배우게 되었습니다. 이러한 문제를 방지하고, 항상 깨끗한 상태로 데이터가 초기화되도록 보장하기 위해 spring.jpa.hibernate.ddl-auto 설정을 create로 변경하였습니다.
| import java.security.Key; | ||
|
|
||
| public class JwtUtil { | ||
| private static final String SECRET_KEY = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
secretKey가 코드에 노출돼 있네요! 민감정보를 어떻게 하면 숨길 수 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드 내에 민감한 정보가 하드코딩 되어 있어 보안취약점이 존재했습니다. 해당 키를 application.properties 파일로 분리하여 관리하도록 수정했습니다. 또한, 분리된 값을 주입받기 위해 기존의 static 메서드로 구성된 JwtUtil을 스프링 빈으로 리팩토링하고, 생성자를 통해 값을 주입받아 사용하도록 구조를 개선했습니다
| import java.util.List; | ||
|
|
||
| @Service | ||
| @Transactional(readOnly = true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(readOnly = true)로 설정했을 때 내부적으로 어떤 일이 일어나나요? 현 프로젝트에서 해당 옵션을 지정했을 때 차이점이 있는지도 가능한 상세하게 분석해서 설명해 주세요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Transactional(readOnly = true)를 지정하면 스프링은 해당 트랜잭션을 조회 전용 트랜잭션으로 처리합니다.
읽기 전용 트랜잭션에서는 EntityManager의 FlushMode가 MANUAL으로 설정되어, 트랜잭션 종료 시 변경 감지에의한 flush가 발생하지 않습니다. 또한 변경 감지를 위한 스냅샷을 생성하지 않아 메모리 사요량을 줄일 수 있고, 일부 DB에서는 실행 계획 최적화나 쓰기 차단 힌트로 활용됩니다.
현재 프로젝트에서는 조회 전용 서비스에 @Transactional(readOnly = true)옵션을 적용함으로써 프로젝트에서는 의도치 않은 엔티티 변경이 DB에 반영되는 것을 방지할 수 있고, 조회 성격의 API에서 불필요한 변경 감지 및 flush 비용을 줄이는 효과가 있습니다.
하지만, 프로젝트의 트래픽이 크지 않고 엔티티 구조가 단순하기 때문에 성능적 이점보다 트랜잭션의 의도를 명확히 표현하고, 읽기/쓰기 책임을 구분한다는 설계적 의미가 크다고 생각합니다.
| } | ||
|
|
||
| public List<Time> findAll() { | ||
| return em.createQuery("SELECT t FROM Time t", Time.class) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서는 JPQL을 사용하신 이유가 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JPQL은 엔티티 객체를 대상으로 쿼리하기 때문에, DB 벤더가 바뀌거나 테이블 구조가 변경되더라도 쿼리를 수정할 필요 없는 DB 독립성을 가집니다. 또한, JPQL로 조회된 엔티티는 자동으로 영속성 컨텍스트의 관리 대상이 되므로, 추후 비즈니스 로직에서 데이터를 수정할 때 JPA의 기능을 활용할 수 있다는 장점이 있어 선택했습니다.
|
|
||
| import java.util.List; | ||
|
|
||
| @Repository |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JPA를 사용하면 리포지토리 레이어에서 @repository와 @component를 썼을 때의 차이점이 나타나기 시작합니다. 그 차이점을 한 번 연구해 보시겠어요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
두 어노테이션 모두 빈으로 등록된다는 점은 같지만, @repository에는 예외 변환 기능이 포함되어 있다는 차이가 있습니다. JPA를 사용할 경우 JPA 구현체 고유 예외가 발생하는데, @repository는 JPA 구현체가 던지는 예외를 스프링 공통 예외로 자동 변환해줍니다. 이로 인해 서비스 레이어는 하부 데이터 접근 기술에 의존하지 않고, DB 기술 변경 시에도 예외 처리 코드를 수정하지 않아도 되는 장점이 있습니다.
안녕하세요 승현님! 오랜만에 뵙는 것 같습니다. 이번 미션도 잘 부탁드립니다 🙇
이번 미션에서는
를 구현했습니다
프로젝트 구조
프로젝트 구조는 다음과 같습니다.