🤔 고민했던 부분
프로젝트 하면서 고민했던 부분을 담아보았다.
1️⃣ 생성자 전달 방식 vs 정적 팩토리 메서드 방식
🔍 문제 상황
" 실제 데이터 Entity 를 dto 로 담을려고 하고 있다. 그런데 두 가지 방식 중 어떤 방식으로 선택해야하는지 고민하게 되었다. "
1. dto 를 지금 생성자로 this.~ 만든 후, 그걸 다시 new ~ 하여 불러주는 방법.
2. dto 를 toDto() 라는 함수를 통해서 return 을 dto 로 바꾸어주는 메서드를 활용하는 방법
🧠 문제 분석
" 생성자 vs 정적 팩토리 메서드 비교 "
1️. 생성자 (new
)
✅ 장점
- 직관적이며 익숙한 방식이다.
- 컴파일 타임에 오류를 쉽게 발견할 수 있다.
- 불변 객체에서
final
필드 초기화가 용이하다.
❌ 단점
- 메서드명을 가질 수 없어 어떤 변환을 수행하는지 알기 어렵다.
- 불필요한
new
호출이 필요해 코드가 길어진다. - 생성자 오버로딩이 많아질 경우 가독성이 저하된다.
- 반환 타입을 유연하게 변경할 수 없다.
- 객체를 재사용할 수 없어 매번 새로운 인스턴스를 생성해야 한다.
2. 정적 팩토리 메서드 (from()
, of()
, valueOf()
등)
✅ 장점
- 의미 있는 메서드명을 사용할 수 있어 가독성이 좋다.
- 객체 생성을 캡슐화할 수 있다.
- 다양한 입력 형식을 지원할 수 있다.
- 반환 타입을 유연하게 지정할 수 있다.
- 객체 재사용 및 싱글턴 패턴 적용이 가능하다.
❌ 단점
- 상속이 불가능해 서브클래스에서 재사용하기 어렵다.
- 프로젝트 내 네이밍 컨벤션이 일관되지 않으면 혼란을 초래할 수 있다.
🔑 문제 결론
" 일반적인 DTO 변환에는 from(User user)
을 추천 "
비교 항목 | 생성자 (new ) |
정적 팩토리 메서드 (from ) |
---|---|---|
가독성 | 의미 불명확 | 메서드명을 통해 직관적으로 전달 가능 |
유연성 | new 키워드 필요 |
필요 시 캐싱된 객체 반환 가능 |
확장성 | 생성자 오버로딩이 많아질수록 복잡해짐 | 다양한 입력을 받는 여러 메서드 정의 가능 |
반환 타입 | 항상 해당 클래스의 인스턴스 반환 | 서브클래스나 인터페이스 구현체 반환 가능 |
객체 재사용 | 매번 새로운 객체 생성 | 필요 시 캐싱된 객체 반환 가능 |
싱글턴 패턴 지원 | 불가능 | 가능 |
- 단순한 객체 생성 → 생성자 (new
)
- 객체 변환 & 유연한 생성 필요 → 정적 팩토리 메서드 (from()
)
2️⃣ Client에서 Server로 Data를 전달하는 방법 고려
🔍 문제 상황
일정의 댓글을 조회하는 과정에서 일정의 ID를 어떤 방식으로 받아올지 고민했다.
1. api 관점에서 "/comments/postID" 이런식으로 PathVariable 를 받아 오는게 나을지.
2. PostId 를 Dto 에 담아서 보낼지
2가지 방식을 고민하고 있었다.
🧠 문제 분석
1. API URL 설계
- API를 설계할 때 자원(Resource) 단위로 URL을 정리하는 것이 좋다고 알려져 있다.
- 따라서 댓글(Comment)을 조회하거나 추가할 때, 상위 객체인 Plan을 통해 접근하는 방식이 더 적절하다고 판단된다.
2. Post ID를 DTO에 포함할지?
- Post ID를 DTO에 포함하는 방식은 확장성이 좋지만, Controller에서 의미 파악이 어려울 수 있다는 고민이 있었다.
- DTO에 포함하면 확장성이 좋아진다.
- 하지만 Controller만 보고 의미를 직관적으로 이해하기 어려울 수도 있음.
=> 최종 결정
- DTO에 포함하는 방식을 사용하지 않고, URL에서
Plan ID
를 통해 접근하는 방식으로 통일하자
🔑 문제 결론
" /plans/{id}/comments
방식으로 API를 설계하기로 하기로 했다!
이렇게 하면 API의 계층 구조가 명확해지고, 특정 Plan에 속한 댓글을 조회한다는 점에서 요청 api 가 직관적이게 된다. "
- 고려한 URL 방식은 아래와 같다.
- ❌
/comments/{postId}
→ 개별 자원인 Comment에 직접 접근하는 방식 - ✅
/plans/{id}/comments
→ Plan을 통해서 접근하는 방식
! 추가적인 정리
- API를 설계할 때 자원(Resource) 중심의 URL을 따르는 것이 좋다는 원칙을 적용했다.
- 따라서 댓글(Comment)은 Plan을 통해 접근하는 방식(/plans/{id}/comments
)을 선택했다.
- 또한, Post ID를 DTO에 포함하는 방식은 확장성은 있지만 Controller에서의 가독성을 고려하여 사용하지 않기로 결정했다.
- 이러한 고민 과정을 거쳐 최종적으로 API를 /plans/{id}/comments
방식으로 설계 했다.
3️⃣ HttpServletrequest 의 DTO 변환 로직의 위치 고민
🔍 문제 상황
" 컨트롤러에서 HttpServletrequest
를 LoginDto
로 변환하는 것이 맞을까? 아니면 서비스 계층에서 처리하는 것이 더 좋을까? "
🧠 문제 분석
1. 컨트롤러에서 처리하는 경우
- 요청(Request)을 받는 관점에서 적절하다.
- 요청 데이터를 검증하고, DTO로 변환하여 서비스로 전달하는 것이 역할에 맞다.
2. 서비스에서 처리하는 경우
request
가 서비스에 종속될 수 있다.- 서비스 계층의 재사용성이 낮아질 가능성이 있다.
- 따라서 이 방식은 적절하지 않다고 판단.
🔑 문제 결론
" 컨트롤러에서 LoginDto
로 변환하는 방식이 더 적절하다고 판단하여 선택! "
4️⃣ 패키지를 Entity 별로 분리한 이유
🔍 문제 상황
Dto 의 파일이 많아져서 분류를 할 필요성이 생겼음
🧠 문제 분석
- Entity를 기준으로 기능을 만들기 때문에 Entity 별로 패키지를 만드는 게 편리할 거 같음
- 오류 발생 시 Entity, Request, Response를 함께 확인해야 함
- 관련된 파일들을 한 패키지에 묶으면 유지보수가 용이
🔑 문제 결론
" Entity를 기준으로 패키지를 나누면 연관된 클래스들을 한 곳에서 관리할 수 있어 유지보수가 쉽다! "
5️⃣ Repository 에서 Optional 로 데이터를 넘기기
🔍 문제상황
" Repository 의 Optional 데이터를 Optional 그대로 넘길지 아니면 OrElseThrow 형식으로 null 값을 처리한 상태로 넘길지 고민했다. "
🧠 문제 분석
- Repository에서 Optional을 반환하면 Service 계층에서 이를 해석하고 예외를 처리해야 하므로, 서비스 로직이 복잡해짐.
- Optional로 감싸진 Entity는 바로 사용하기 어렵고, 불필요한 래핑 해제 로직이 생김.
- Service에서 직접 Optional을 다루게 되면 도메인 객체를 이용한 비즈니스 로직 구성 시 불편함 발생.
- 계층 분리 원칙에 따라 예외는 Service에서 처리하는 것이 일반적이지만, 도메인과 서비스 간의 효율적인 흐름이 깨질 수 있음.
- Repository는 단순히 데이터를 제공하는 역할인데, Optional 자체도 데이터를 바로 주는 방식에는 부적절할 수 있음.
🔑 문제 결론
- Optional은 Repository 내부에서 적절히 처리하고, 유효한 Entity를 직접 반환하는 방식이 더 나음.
- 유효성 검사 및 예외 처리는 Repository에서 하고, Service에서는 필요한 객체만 바로 활용할 수 있게 한다.
- Repository에 default 메서드를 두어 예외를 던지도록 구성하면 좋음.
- 이렇게 하면 서비스는 비즈니스 로직에만 집중할 수 있음.
6️⃣ 로그인 기능은 PostMapping
🔍 고민 상황
로그인 기능을 만들 때 @GetMapping을 사용할지, @PostMapping을 사용할지에 대한 고민이 생김.
로그인이 단순히 데이터 조회(GET)처럼 보일 수도 있지만, 실제로는 서버의 상태를 변경하므로 어떤 방식이 REST 원칙에 더 부합하는지 판단이 필요함.
🧠 문제 분석
- 로그인은 서버 상태를 변경하는 작업이다.
- 로그인 시 서버는 세션 생성 또는 토큰 발급 등을 통해 상태를 변화시킨다.
- 단순 조회가 아닌, 인증 및 보안 관련 처리가 포함됨.
- POST는 상태 변경을 위한 HTTP 메서드이다.
- POST는 데이터를 전송하여 서버의 상태를 바꾸거나 새로운 리소스를 생성하는 데 사용된다.
- 로그인은 새로운 리소스를 생성하진 않지만, 인증 상태를 생성한다는 점에서 POST가 적절하다.
- GET은 안전하고 멱등적이어야 한다.
- GET 요청은 서버의 상태를 변경하지 않아야 하며, 같은 요청을 여러 번 보내도 결과가 같아야 한다.
- 로그인은 그 자체로 서버에 변화를 일으키므로 GET에 부적절하다.
🔑 문제 결론
- 로그인은 반드시 POST 방식으로 처리해야 한다.
- 서버 상태(세션 생성, 토큰 발급 등)를 변경하는 행위이기 때문에 GET이 아닌 POST가 REST 아키텍처 원칙에 부합한다.
- 보안 측면에서도 GET은 URL에 정보가 노출될 수 있어, 로그인과 같은 민감한 작업에 적절하지 않다.
트러블 슈팅
실제 에러가 났던 부분을 정리한 부분입니다. 😮
1️⃣ 원인 분석 불가 오류
🔍 문제 상황
- 406 에러가 떴다.해당 에러는 클라이언트가 요청한 콘텐츠 형식을 서버가 지원하지 않을 때 발생한다.
- 일단 DB 에 처리는 되어진 상황인데 응답 Json 만 반환되지 않는 것이다.
🧠 문제 분석
- Response Dto 에 문제가 있는 거 같다.
- 실제 DB 에는 반영이 잘 되었는데, 반환만 못한 것이니 Dto 오류 형식에 문제가 있는 것이다.
🔑 문제 결론
- 이번 오류가 신기한게 나는 뭐 특별히 만진게 없다.
- 근데 해결이 됐다. 왜일까.. ? dto 의 코드 속에서 주석이 하나 빠졌었나 싶다.
2️⃣ 페이징 빈배열 반환
🔍 문제 상황
- 페이징 구현 후 테스트 하는 상황에서 자꾸 빈배열만을 반환했다.
🧠 문제 분석
- 코드에 디버그를 찍고, 확인해보았는데 특별한 오류는 없었다. log 를 찍어보았는데도 오류는 보이지 않고 그저 빈배열만 찍혔다.
- 왜일까 왜일까 30분간 해맸는데, 내가 입력값으로 PageNumber 를 큰 값을 주고 있었다.
🔑 문제 결론
- PageNumber 값을 적절하게 낮추어주니 문제가 해결됐다.
- 해당 부분을 생각 못해서 엄한 곳만 수정했다. 그나마 긍정적으로 생각하는게.. 해당 부분을 거치면서 Page Info 도 map 형태로 정리하여 반환해주는 코드를 만들었다.
'백엔드 부트캠프 > TIL' 카테고리의 다른 글
[내일배움캠프Spring-33일차] CH 3 일정 관리 앱 Develop 完 (0) | 2025.04.03 |
---|---|
[내일배움캠프Spring-32일차] CH 3 일정 관리 앱 Develop Lv7~Lv8, Refactoring (1) | 2025.04.02 |
[내일배움캠프Spring-30일차] CH 3 일정 관리 앱 Develop Lv2~Lv6 (0) | 2025.03.31 |
[내일배움캠프Spring-29일차] CH 3 일정 관리 앱 Develop Lv0~Lv1 (0) | 2025.03.28 |
[내일배움캠프Spring-28일차] Spring DI/IoC (1) | 2025.03.27 |