265 lines
9.1 KiB
Markdown
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)을 참고한다.
|