# 구조 한눈에 보기 이 문서는 프로젝트에 처음 들어온 사람이 `core`, `admin`, `front`가 왜 나뉘어 있는지와 새 코드를 어느 위치에 만들어야 하는지를 빠르게 이해하기 위한 안내서다. ## 1. 전체 구조 ```text 한 Git 저장소 | |-- 01-api-core 공유 라이브러리, 단독 실행 안 함 |-- 02-api-admin 관리자 API 서버, 별도 JAR 배포 `-- 03-api-front 사용자 API 서버, 별도 JAR 배포 ``` ```text 브라우저 또는 외부 클라이언트 | v Controller (admin 또는 front) | v 업무 Service (admin 또는 front) | +--> core standard Service --> Mapper --> Mapper XML --> DB | `--> core bespoke Service --> Mapper --> Mapper XML --> DB ``` `02-api-admin`과 `03-api-front`는 각각 독립 실행된다. 따라서 admin JAR에는 front Controller가, front JAR에는 admin Controller가 들어가지 않는다. 두 서버가 공통으로 사용하는 코드는 `01-api-core`에 둔다. ## 2. 어디에 무엇을 두는가 | 위치 | 책임 | 두면 안 되는 것 | |---|---|---| | `core/common` | JWT, 예외, 공통 응답, 페이징, 유틸 | 화면별 정책, Controller | | `core/config` | DataSource, MyBatis, 공통 인프라 설정 | admin/front 화면 전용 설정 | | `core/modules/standard` | 한 테이블의 조회, 등록, 수정, 삭제 | 여러 테이블 JOIN SQL | | `core/modules/bespoke` | JOIN, 집계, `INSERT ... SELECT`, 예외적인 복합 SQL | 단일 테이블 CRUD 중복 | | `admin/modules` | 관리자 Controller, 업무 흐름, Form, 응답 VO | front Controller | | `front/modules` | 사용자 Controller, 업무 흐름, Form, 응답 VO | admin Controller | admin/front의 `modules` 바로 아래 1 depth는 테이블명이 아니라 서비스의 업무 메뉴 기준이다. 예를 들어 백오피스 메뉴 아래에는 관리자 계정, 권한, 메뉴 관리 기능이 함께 있으므로 모두 `backOffice` 모듈에 둔다. 일반 회원 기능이 필요해져도 백오피스의 관리자 계정 관리와 섞지 않는다. ### standard와 bespoke 판단법 ```text SQL이 한 테이블만 접근하는가? |- 예: standard `- 아니오: bespoke ``` 예시: ```text test_user의 LAST_LOGIN_AT 수정 -> standard.testUser.TestUserService test_user + test_user_token을 JOIN한 관리자 로그인 조회 -> bespoke.testUser.TestUserAuthService ``` `standard`는 테이블마다 Service를 하나만 둔다. `test_user`에 조회가 더 필요하면 `TestUserService`에 메서드와 DTO/VO를 추가한다. `TestUserLoginService`처럼 단일 테이블용 Service를 새로 만들지 않는다. ## 3. 패키지 모양 실행 모듈은 화면 메뉴의 1 depth를 패키지로 두고, 그 안에서 Controller와 업무 Service를 기능별로 나눈다. ```text com.alist.api.admin.modules.backOffice ├─ AdminMemberController.java ├─ AccountController.java ├─ MenuController.java ├─ service │ ├─ AdminMemberService.java │ ├─ AccountService.java │ └─ MenuService.java ├─ form │ ├─ AdminMemberAddForm.java │ ├─ AccountAddForm.java │ └─ MenuAddForm.java └─ vo ├─ AdminMemberListVo.java ├─ AccountListVo.java └─ MenuListVo.java ``` 패키지는 프로젝트 규칙에 따라 lower camel case로 작성한다. 따라서 `backOffice`가 패키지명이고, `AdminMemberController`와 `MenuController`가 같은 업무 모듈의 기능별 진입점이다. ### Core bespoke 이름 core `bespoke`의 Service/Mapper는 기준 테이블명과 이를 호출하는 admin/front 업무 Service명을 결합한다. ```text AdminMemberService -> TestUserAdminMemberService -> TestUserAdminMemberMapper AuthService -> TestUserAuthService -> TestUserAuthMapper ``` `backOffice`는 실행 모듈의 메뉴 패키지 경계이므로 core bespoke 클래스명에 넣지 않는다. 이 기준으로 `backOffice.AdminMemberService`가 호출하는 `TEST_USER` JOIN 조회는 `TestUserBackOfficeService`가 아니라 `TestUserAdminMemberService`에 둔다. ### core standard ```text com.alist.api.core.modules.standard.testUser ├─ dto │ ├─ TestUserAddDto │ ├─ TestUserListDto │ ├─ TestUserModifyDto │ └─ TestUserViewDto ├─ vo │ ├─ TestUserAddVo │ ├─ TestUserListItemVo │ ├─ TestUserListVo │ ├─ TestUserModifyVo │ └─ TestUserViewVo ├─ mapper │ └─ TestUserMapper └─ service └─ TestUserService resources/mapper/standard/testUser └─ TestUserMapper.xml ``` ### core bespoke ```text com.alist.api.core.modules.bespoke.testUserToken ├─ dto │ └─ TestUserTokenRefreshViewDto ├─ vo │ └─ TestUserTokenRefreshViewVo ├─ mapper │ └─ TestUserTokenAuthMapper └─ service └─ TestUserTokenAuthService resources/mapper/bespoke/testUserToken └─ TestUserTokenAuthMapper.xml ``` ### admin/front 업무 모듈 ```text com.alist.api.admin.modules.auth ├─ AuthController ├─ form │ └─ AuthLoginForm ├─ service │ └─ AuthService └─ vo └─ AuthLoginVo ``` front도 같은 모양을 사용한다. 단, Controller와 Form/VO는 서로 공유하지 않는다. ## 4. 클래스별 역할 | 클래스 | 위치 | 하는 일 | |---|---|---| | `*Controller` | admin/front | HTTP 요청 수신, Form 검증, 인증 주체 확인, `ApiResponse` 반환 | | admin/front `*Service` | admin/front | 업무 정책, 여러 core 호출 조합, 트랜잭션, core VO를 외부 VO로 변환 | | `*Service` | core standard | 한 테이블의 데이터 접근 계약 제공 | | `*Service` | core bespoke | JOIN·집계·복합 SQL 데이터 접근 계약 제공 | | `*Mapper` | core | Java 메서드와 Mapper XML 연결 | | `*Mapper.xml` | core resources | 실제 SQL | | `*Form` | admin/front | HTTP 요청 바인딩과 입력 검증 | | `*Dto` | core | Service에서 Mapper로 전달하는 조건 또는 저장 값 | | core `*Vo` | core | Mapper 조회 결과와 core 내부 처리 값 | | admin/front `*Vo` | admin/front | 외부 API 응답 값 | Controller는 core Service, Mapper, core DTO/VO를 직접 사용하지 않는다. admin/front 업무 Service가 경계를 지킨다. ## 5. 이름 읽는 법 ### 패키지명 패키지는 lower camel case를 쓴다. ```text testUser testUserToken testCorsAllowedList ``` ### 클래스명 ```text standard {Table}Service, {Table}Mapper 예: TestUserService, NoticeMapper bespoke {BaseTable}{BusinessModule}Service, {BaseTable}{BusinessModule}Mapper 예: TestUserAuthService, NoticeSupportService ``` `admin`, `front`, `Query`, `Custom`은 core bespoke Service 이름에 붙이지 않는다. core는 서버가 아니라 데이터와 업무 목적을 기준으로 재사용하기 때문이다. ### 메서드명 ```text admin/front 업무 Service {select|insert|update|delete}{기능명} 예: selectLogin, selectRefresh, updateLogout core standard {select|insert|update|delete}{Table}{Action} 예: selectTestUserList, updateTestUserModify core bespoke {baseTable}{function}{ResultShape} 예: testUserLoginView, testUserTokenRefreshView, noticeSupportList ``` bespoke 조회 메서드는 결과 모양을 마지막에 붙인다. `List`, `View`, `Count`, `Summary`, `Tree` 등이 예다. 조회 조건은 `ByUserIdx`처럼 이름에 붙이지 않고 DTO에 둔다. ### 변수명 변수는 타입과 역할이 드러나게 쓴다. ```java AuthLoginVo authLoginVo; TestUserTokenModifyDto testUserTokenModifyDto; List noticeList; ``` `result`, `data`, `dto`, `vo`처럼 의미가 너무 넓은 이름은 피한다. boolean은 결과가 드러나게 `added`, `updated`, `deleted`, `loggedIn`, `refreshed`처럼 작성한다. ## 6. 인증 흐름 예시 관리자 로그인은 JOIN 조회와 단일 테이블 수정을 함께 사용한다. ```text AuthController.login -> AuthService.selectLogin -> TestUserAuthService.testUserLoginView -> test_user + test_user_token JOIN -> PasswordEncoder로 비밀번호 검증 -> JwtTokenProvider로 Access/Refresh Token 발급 -> TestUserTokenService.updateTestUserTokenModify -> test_user_token refresh token, 만료 시각 저장 -> TestUserService.updateTestUserModify -> test_user LAST_LOGIN_AT 저장 -> AuthLoginVo 조립 -> ApiResponse 반환 ``` 이 흐름에서 `password`, `refreshToken`은 인증 처리용 core VO에만 두고 외부 응답 VO에는 기본적으로 넣지 않는다. ## 7. 새 기능을 만들 때 순서 1. API가 admin인지 front인지 정하고 해당 `modules/{기능}` 패키지를 만든다. 2. 사용할 SQL이 단일 테이블인지, JOIN·복합 SQL인지 판단한다. 3. core standard 또는 bespoke에 DTO, VO, Mapper, Service, Mapper XML을 만든다. 4. admin/front에 Form, 업무 Service, 외부 응답 VO, Controller를 만든다. 5. Controller가 core 타입을 직접 import하지 않는지 확인한다. 6. Swagger `@Tag`, `@Operation`, `@Schema`과 컴파일 검증을 한다. 상세 규칙은 [Core 모듈 설계](core-module-design.md), [코드 컨벤션](code-conventions.md), [보안 및 응답 규칙](security-and-response.md)을 참고한다.