Files
api2/docs/architecture-at-a-glance.md
T
2026-07-20 17:49:24 +09:00

265 lines
9.1 KiB
Markdown

# 구조 한눈에 보기
이 문서는 프로젝트에 처음 들어온 사람이 `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<NoticeListItemVo> 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<AuthLoginVo> 반환
```
이 흐름에서 `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)을 참고한다.