과제 요구사항
## 필수 기능 요구사항
- 기존 코드를 모두 JPA를 활용하도록 수정합니다.
- JPA Auditing을 활용한 작성일, 수정일 필드
- Entity와 Spring-Data-Jpa Repository를 도입
- 이메일과 비밀번호를 활용해 로그인 / 회원가입 기능을 추가합니다.
- 회원가입 API
- 비밀번호 암호화(PasswordEncoder 구현)
- 로그인 API 구현
- 필터를 활용한 인증/인가 처리
- Cookie/Session을 활용한 로그인 구현
## 도전 기능 요구사항
- 다양한 예외처리를 적용합니다.
- 이메일 포맷 검증(정규 표현식)
- 할 일 제목은 10글자 이내, 유저명은 4글자 이내
- 댓글 CRUD를 구현합니다.
- 일정 페이징 조회
- Spring Data JPA에서 제공하는 Pageable과 PageRequest를 활용하여 페이징 구현
구현 과정
1. jpa auto-ddl 설정
JdbcTemplate을 사용했을 때에는, 테이블과 초기 데이터를 모두 만들어야하기 때문에, .sql 파일에 create문과 insert문을 작성했다. 하지만 JPA는 구현한 Entity 코드를 바탕으로 테이블을 자동으로 생성해주기 때문에, .sql 문에 create문은 삭제하였다.
spring:
jpa:
defer-datasource-initialization: true
jpa 에서 테이블을 생성한 후, .sql 문을 실행하도록 순서를 고정하기 위해 application.yml 에 위 코드를 작성해야한다.
2. 로그인 HTTP 메서드
로그인의 HTTp 메서드를 GET과 POST 중 고민하였다. 하지만 로그인 API 또한, 사용자의 인증 상태를 바꾸고 토큰 또는 세션을 생성하기 는 변화가 있기 때문에 POST가 적절하다. 또한, GET 메소드는 요청 Body가 없기 때문에 POST가 적절하다.
3. N+1 문제을 고려한 쿼리문 작성
N+1 문제란, 연관 관계에서 부모 객체를 호출할 때 자식 객체가 프록시로 호출(지연 로딩)되면서 생기는 문제이다. 즉, 부모 객체에서 참조하고 있는 자식 리스트를 사용할 때 N개의 select 문이 호출되면서 1(부모) + N(자식) 문제라고 한다.
내 코드에서는 일정 상세 조회 과정에서 연관된 댓글을 조회할 때 발생할 수 있다. 따라서 나는 댓글을 제외한 일정 상세 조회 메소드와, 일정 식별값을 이용해 여러 댓글을 조회하는 단 두 개의 쿼리문을 호출하여 N+1 문제를 방지하였다.
public GetScheduleResponse getScheduleDetail(Long scheduleId) {
GetScheduleResponse response = scheduleRepository.findDetailById(scheduleId)
.orElseThrow(() -> new ScheduleException(SCHEDULE_NOT_FOUND));
List<Comments> comments = commentRepository.findCommentsByScheduleId(scheduleId);
response.setComments(comments);
return response;
}
4. Spring Data JPA 메소드 오버라이딩이 안되는 이유
Spring Data JPA가 제공하는 기본 CRUD문을 내가 오버라이딩할 수 없다. 우리가 작성하는 Repository는 인터페이스일 뿐, Spring Data JPA가 런타임에 Repository의 구현체를 프록시 기반으로 자동 생성하기 때문이다. 즉, 우리가 Repository에 findById()를 작성해도, 실제 동작은 SimpleJpaRepository 프록시에서 처리된다. (우리가 정의한 메소드는 무시)
-> 오버로딩(인자 및 반환값 수정)과 메소드명을 달리 해서 생성하는 방법을 사용하면 된다.
👌 메소드 이름 기반 메소드와의 차이
findById() 같은 기본 메소드는 이미 구현이 존재하기 때문에, @Query로 덮어쓸 수 없고, findByEmail() 같은 메소드 이름 기반 메소드는 구현이 없기 때문에 스프링이 이름을 해석하거나, @Query 를 통해 구현을 위임할 수 있어요.
5. Filter에서 발생한 예외
Filter에서 발생한 예외는 @ExceptionHandler로 잡지 못한다.
Client Request
↓
[🔹 Filter] ← ❗ 예외 발생하면 여긴 Spring MVC 외부
↓
[🔹 Servlet (DispatcherServlet)] ← 여기서부터 Spring MVC 컨트롤러 흐름
↓
[🔹 Controller]
↓
[🔹 ExceptionResolver / @ExceptionHandler]
따라서, Filter에서 직접 예외 응답을 생성하여 내려주어야 한다.
질문
1. N+1 문제를 제가 코드에서 구현한 것처럼 2개의 쿼리로 구분하는 게 좋을까요 Fetch Join 등을 사용해 단 하나의 쿼리로 만드는 게 좋을까요?
2. DTO 명이 고민됩니다. 특히, GetSchedulesResponse 에서 참조하는 List<Comments> 에서, 이미 Comment 라는 엔티티가 존재하여 클래스명이 중복됩니다. 응답을 생성하기 위한 과정에서 쓰이는 DTO 명과, 응답 DTO 명을 튜터님께서는 어떻게 작성하고 계시나요?
3. save 이후 바로 id를 조회할 수 있는 이유가 궁금합니다. 제가 생각하기로는 save를 하면 영속성 컨테이너에 적재되고, 트랜잭션이 종료되기 전에 flush()가 되지 않아 id를 조회할 수 없어야한다고 생각하는데, save는 다른 메소드와 다른가요?
* 질문에 대한 답변은 후에 업데이트할 예정입니다.
'Coding > Server' 카테고리의 다른 글
| [내일배움캠프] 테스트코드 - 실습 (0) | 2025.04.17 |
|---|---|
| [내일배움캠프] 테스트코드 - 이론 (0) | 2025.04.17 |
| [내일배움캠프] Spring 숙련 (2) | 2025.04.02 |
| [내일배움캠프] Spring 입문 (2) | 2025.03.25 |
| [내일배움캠프] Java 문법 종합반 (0) | 2025.03.06 |