Skip to content

패키지 가이드

dictionary-api(단어사전 관리 도구 백엔드) 프로젝트의 실제 패키지 구조를 예시로 삼는다.

  1. 패키지 기본 구성
    {도메인}.{프로젝트명}
  2. 샘플
    biz.mintchoco.dictionary
    • 도메인(biz.mintchoco)은 도메인 주소를 역방향으로 표기한다.
    • 프로젝트명(dictionary)은 소문자 알파벳과 언더바(_)만 사용한다.
  • 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/ 정적 유틸리티
        • …
  1. entity, interfaces는 특정 기능(api/<feature>) 하위가 아니라 최상위에 둔다 — 여러 기능이 함께 참조하도록 의도적으로 공유하는 레이어이기 때문이다.
    • entity: JPA 엔티티. AbstractEntity(@MappedSuperclass)를 상속해 등록/수정 감사 컬럼(create_dt/create_user_id/create_ip/update_dt/update_user_id/update_ip)을 자동으로 채운다.
    • interfaces: 외부 API 연동 클라이언트(StdictClient, KrdictClient 등). interface가 Java 예약어라 복수형으로 이름 지었다.
  2. api/<feature>: 기능별 레이어드 구조. 기능 하나(board, dictionary, domain, auth 등)마다 controller/dto/repository/service 네 서브패키지를 갖는다.
    • DB를 거치지 않는 기능(search, tools)은 repository가 없고, RequestDto/ResponseDto 사이에 Entity를 대신할 중간 표현이 필요하면 dto/context 서브패키지에 Context 클래스를 따로 둔다.
  3. common: 여러 기능이 함께 쓰는 부품만 모은다 — 특정 기능 전용 로직은 여기 두지 않고 해당 api/<feature> 안에 둔다.

기능(api/<feature>) 서브패키지 가이드

Section titled “기능(api/<feature>) 서브패키지 가이드”
  1. controller
    biz.mintchoco.dictionary.api.board.controller
    • AbstractController를 상속하고 @RestController + @RequestMapping("/v1/<feature>") + @RequiredArgsConstructor를 붙인다.
  2. dto
    biz.mintchoco.dictionary.api.board.dto
    • API(컨트롤러 메소드) 단위로 독립 파일을 둔다 — BoardCreate.java, BoardUpdate.java, BoardFindAll.java, BoardFindByPage.java, BoardDeleteAll.java처럼 파일명 자체가 <Feature><메소드>.
    • 각 파일은 같은 이름의 네임스페이스 클래스 안에 <Feature><메소드>RequestDto 중첩 클래스를 둔다(BoardCreate.BoardCreateRequestDto).
    • 응답 DTO(BoardResponseDto)는 파일을 나누지 않고 한 기능이 API 여러 개(find/findAll/findByPage/create/update)에서 공유한다.
    • 중간 표현이 필요한 기능은 dto/context 서브패키지에 Context 클래스를 별도 파일로 둔다(예: ToolsJasyptEncryptContext).
  3. repository
    biz.mintchoco.dictionary.api.board.repository
    • Spring Data JPA 인터페이스(BoardRepository extends JpaRepository<BoardEntity, Long>)와, 동적 조건·페이징을 담당하는 QueryDsl 커스텀 리포지토리(BoardQueryDslRepository/BoardQueryDslRepositoryImpl)를 함께 둔다.
  4. service
    biz.mintchoco.dictionary.api.board.service
    • @Service + @RequiredArgsConstructor. 리포지토리 하나만 생성자 주입 필드로 둔다.

DTO 클래스 네이밍(<Feature> 접두어)

Section titled “DTO 클래스 네이밍(<Feature> 접두어)”
  1. 모든 DTO 클래스명 맨 앞에 기능 이름을 접두어로 붙인다 — BoardCreate, DomainFindAll, ToolsJasyptEncrypt. 중첩 클래스도 줄여 쓰지 않고 전체 이름을 반복한다(ToolsJasyptEncrypt.ToolsJasyptEncryptRequestDto, ToolsJasyptEncrypt.RequestDto처럼 줄이지 않는다).
  2. 이유
    • 프로젝트 전체에서 DTO 클래스명이 유일해져, 서로 다른 기능의 DTO를 한 파일에서 같이 import해도 이름이 겹치지 않는다.
    • springdoc이 생성하는 /v3/api-docs 스키마 이름은 Java 클래스의 simple name을 그대로 쓴다 — 접두어가 없으면 여러 기능이 전부 RequestDto/ResponseDto를 써서 스키마 이름이 충돌하거나 자동 넘버링(RequestDto_1)된다. 프런트엔드가 이 스펙으로 TypeScript 타입을 생성하므로, 접두어가 없으면 생성되는 타입 이름도 함께 망가진다.