패키지 가이드
dictionary-api(단어사전 관리 도구 백엔드) 프로젝트의 실제 패키지 구조를 예시로 삼는다.
기본 패키지 패턴
Section titled “기본 패키지 패턴”- 패키지 기본 구성
{도메인}.{프로젝트명}
- 샘플
biz.mintchoco.dictionary
- 도메인(
biz.mintchoco)은 도메인 주소를 역방향으로 표기한다. - 프로젝트명(
dictionary)은 소문자 알파벳과 언더바(_)만 사용한다.
- 도메인(
최상위 패키지 구조
Section titled “최상위 패키지 구조”Directorybiz.mintchoco.dictionary/
- DictionaryApiApplication.java
Directoryconfig/ 전역 설정 빈
- …
Directoryentity/ JPA 엔티티 (기능 하위가 아니라 최상위에 모은다)
- …
Directoryinterfaces/ 외부 API 연동 클라이언트 (여러 기능이 함께 참조하는 공용 레이어)
- …
Directoryapi/ 기능별 레이어드 구조
Directoryboard/
Directorycontroller/
- …
Directorydto/
- …
Directoryrepository/
- …
Directoryservice/
- …
Directorydictionary/
- …
Directorydomain/
- …
- …
Directorycommon/ 프로젝트 공통 부품
Directoryapi/ 컨트롤러 공통 헬퍼, 응답 어드바이스
- …
Directorycode/ 결과 코드·포맷 검증용 enum
- …
Directorydto/ 요청/응답 공통 봉투(envelope)
- …
Directoryexception/ 공통 예외, 전역 예외 처리기
- …
Directorymodel/ 세션 등 도메인에 속하지 않는 공용 모델
- …
Directorysecurity/ 인가 관련 애노테이션·인터셉터
- …
Directoryutils/ 정적 유틸리티
- …
entity,interfaces는 특정 기능(api/<feature>) 하위가 아니라 최상위에 둔다 — 여러 기능이 함께 참조하도록 의도적으로 공유하는 레이어이기 때문이다.entity: JPA 엔티티.AbstractEntity(@MappedSuperclass)를 상속해 등록/수정 감사 컬럼 6개를 자동으로 채운다.interfaces: 외부 API 연동 클라이언트(StdictClient,KrdictClient등).interface가 Java 예약어라 복수형으로 이름 지었다.
api/<feature>: 기능별 레이어드 구조. 기능 하나(board,dictionary,domain,auth등)마다controller/dto/repository/service네 서브패키지를 둔다.- DB를 거치지 않는 기능(
search,tools)에는repository가 없다.Entity대신 쓸 중간 표현이 필요하면dto/context서브패키지에Context클래스를 둔다.
- DB를 거치지 않는 기능(
common: 여러 기능이 함께 쓰는 부품만 모은다 — 특정 기능 전용 로직은 여기 두지 않고 해당api/<feature>안에 둔다.
기능(api/<feature>) 서브패키지 가이드
Section titled “기능(api/<feature>) 서브패키지 가이드”- controller
biz.mintchoco.dictionary.api.board.controller
AbstractController를 상속하고@RestController,@RequestMapping("/v1/<feature>"),@RequiredArgsConstructor를 붙인다.
- dto
biz.mintchoco.dictionary.api.board.dto
- API(컨트롤러 메소드)마다 파일을 따로 둔다. 파일명은
<Feature><메소드>형식이다(BoardCreate.java,BoardUpdate.java,BoardFindAll.java,BoardFindByPage.java,BoardDeleteAll.java). - 각 파일은 같은 이름의 네임스페이스 클래스 안에
<Feature><메소드>RequestDto중첩 클래스를 둔다(BoardCreate.BoardCreateRequestDto). - 응답 DTO(
BoardResponseDto)는 파일을 나누지 않고 한 기능이 API 여러 개(find/findAll/findByPage/create/update)에서 공유한다. - 중간 표현이 필요한 기능은
dto/context서브패키지에Context클래스를 별도 파일로 둔다(예:ToolsJasyptEncryptContext).
- API(컨트롤러 메소드)마다 파일을 따로 둔다. 파일명은
- repository
biz.mintchoco.dictionary.api.board.repository
- Spring Data JPA 인터페이스(
BoardRepository extends JpaRepository<BoardEntity, Long>)를 둔다. 동적 조건·페이징은 QueryDsl 커스텀 리포지토리(BoardQueryDslRepository/BoardQueryDslRepositoryImpl)가 맡는다.
- Spring Data JPA 인터페이스(
- service
biz.mintchoco.dictionary.api.board.service
@Service+@RequiredArgsConstructor. 리포지토리 하나만 생성자 주입 필드로 둔다.
DTO 클래스 네이밍(<Feature> 접두어)
Section titled “DTO 클래스 네이밍(<Feature> 접두어)”- 모든 DTO 클래스명 앞에 기능 이름을 붙인다(
BoardCreate,DomainFindAll,ToolsJasyptEncrypt). 중첩 클래스도 줄이지 않고 전체 이름을 쓴다 —ToolsJasyptEncrypt.RequestDto가 아니라ToolsJasyptEncrypt.ToolsJasyptEncryptRequestDto다. - 이유
- 프로젝트 전체에서 DTO 클래스명이 유일해져, 서로 다른 기능의 DTO를 한 파일에서 같이 import해도 이름이 겹치지 않는다.
- springdoc은
/v3/api-docs스키마 이름에 Java 클래스의 simple name을 그대로 쓴다. 접두어가 없으면 여러 기능의RequestDto/ResponseDto가 충돌하거나RequestDto_1처럼 번호가 붙는다. 프런트엔드는 이 스펙으로 TypeScript 타입을 만들기 때문에 타입 이름도 함께 망가진다.