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

9.1 KiB

구조 한눈에 보기

이 문서는 프로젝트에 처음 들어온 사람이 core, admin, front가 왜 나뉘어 있는지와 새 코드를 어느 위치에 만들어야 하는지를 빠르게 이해하기 위한 안내서다.

1. 전체 구조

한 Git 저장소
|
|-- 01-api-core       공유 라이브러리, 단독 실행 안 함
|-- 02-api-admin      관리자 API 서버, 별도 JAR 배포
`-- 03-api-front      사용자 API 서버, 별도 JAR 배포
브라우저 또는 외부 클라이언트
        |
        v
Controller (admin 또는 front)
        |
        v
업무 Service (admin 또는 front)
        |
        +--> core standard Service --> Mapper --> Mapper XML --> DB
        |
        `--> core bespoke Service --> Mapper --> Mapper XML --> DB

02-api-admin03-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 판단법

SQL이 한 테이블만 접근하는가?
  |- 예: standard
  `- 아니오: bespoke

예시:

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를 기능별로 나눈다.

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가 패키지명이고, AdminMemberControllerMenuController가 같은 업무 모듈의 기능별 진입점이다.

Core bespoke 이름

core bespoke의 Service/Mapper는 기준 테이블명과 이를 호출하는 admin/front 업무 Service명을 결합한다.

AdminMemberService
-> TestUserAdminMemberService
-> TestUserAdminMemberMapper

AuthService
-> TestUserAuthService
-> TestUserAuthMapper

backOffice는 실행 모듈의 메뉴 패키지 경계이므로 core bespoke 클래스명에 넣지 않는다. 이 기준으로 backOffice.AdminMemberService가 호출하는 TEST_USER JOIN 조회는 TestUserBackOfficeService가 아니라 TestUserAdminMemberService에 둔다.

core standard

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

com.alist.api.core.modules.bespoke.testUserToken
├─ dto
│  └─ TestUserTokenRefreshViewDto
├─ vo
│  └─ TestUserTokenRefreshViewVo
├─ mapper
│  └─ TestUserTokenAuthMapper
└─ service
   └─ TestUserTokenAuthService

resources/mapper/bespoke/testUserToken
└─ TestUserTokenAuthMapper.xml

admin/front 업무 모듈

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를 쓴다.

testUser
testUserToken
testCorsAllowedList

클래스명

standard
{Table}Service, {Table}Mapper
예: TestUserService, NoticeMapper

bespoke
{BaseTable}{BusinessModule}Service, {BaseTable}{BusinessModule}Mapper
예: TestUserAuthService, NoticeSupportService

admin, front, Query, Custom은 core bespoke Service 이름에 붙이지 않는다. core는 서버가 아니라 데이터와 업무 목적을 기준으로 재사용하기 때문이다.

메서드명

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에 둔다.

변수명

변수는 타입과 역할이 드러나게 쓴다.

AuthLoginVo authLoginVo;
TestUserTokenModifyDto testUserTokenModifyDto;
List<NoticeListItemVo> noticeList;

result, data, dto, vo처럼 의미가 너무 넓은 이름은 피한다. boolean은 결과가 드러나게 added, updated, deleted, loggedIn, refreshed처럼 작성한다.

6. 인증 흐름 예시

관리자 로그인은 JOIN 조회와 단일 테이블 수정을 함께 사용한다.

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 모듈 설계, 코드 컨벤션, 보안 및 응답 규칙을 참고한다.