Skip to content

Kimjiman/basic-arch

Repository files navigation

Basic-Arch

순환참조 없는 레이어드 아키텍처 + 공통 인프라를 제공하는 Spring Boot 백엔드 개발 표준화 프레임워크

Java Spring Boot Redis PostgreSQL Docker


1. 왜 만들었나

실무에서 반복적으로 마주쳤던 두 가지 문제를 해결하기 위해 시작했습니다.

문제 1 — 보일러플레이트와 통일성 부재

새 프로젝트가 시작될 때마다 이력 필드, API 응답 포맷, 검색 파라미터, 메서드 시그니처를 매번 다시 작성했습니다. 개발자마다 구현 방식이 달라 코드를 파악하는 데만 시간이 걸렸습니다.

// 팀마다 달랐던 API 응답 포맷
{code:200,data:{...}}
        
{status:"success",data:{...}}

// 팀마다 달랐던 메서드 시그니처
List<T> findAllBy(P p);

List<T> selectListBy(P p);

List<T> getListBy(P p);

문제 2 — 서비스 레이어 순환참조

실무에서 두 가지 규칙을 모두 적용해봤는데, 둘 다 문제가 있었습니다.

규칙 문제
서비스 간 직접 호출 허용 프로젝트 규모가 커질수록 의존 관계가 얽혀 순환참조 에러 발생
서비스 간 직접 호출 금지 필요한 로직을 호출 못하니 같은 코드를 여러 곳에 중복 작성

→ Facade 레이어를 추가해 두 문제를 동시에 해결했습니다.

Service는 서로를 모르고, 로직 조합이 필요하면 Facade에서 여러 Service를 조합해 처리합니다.


2. 아키텍쳐 설계

레이어드 + Facade 패턴

파사드 패턴이란?

Facade 패턴을 선택한 이유

  • 서비스 간 순환 참조 방지

서비스 안에 다른서비스를 호출하는 걸 근본적으로 차단하기때문에, 순환 참조가 구조적으로 절대 발생할 수 없음

  • Bean Validation 대신 ToyAssert를 Facade에서 처리와 검증

입력, 비즈니스 검증이 Facade 레이어 한 곳에 모여 있어, 유지보수가 용이함

flowchart LR
    A[Controller] -->|의존| B[Facade] -->|의존| C[Service] -->|의존| D[Repository] -->|DB 접근| E[(Database)]
Loading
가능한것 안되는것
Controller Facade 의존, 사용자 요청, 응답 반환 검증 로직, 직접 Service 호출
Facade Service 의존, 입력값 검증, 예외 처리, 트랜잭션, 비즈니스 로직 조합 Repository 직접 접근, HTTP 관련 코드
Service Repository의존, 단일 도메인 비즈니스 로직 Repository 직접 접근, 예외, HTTP 관련 코드, 다른 Service 의존
Repository DB 접근, Query 비즈니스 로직

프로젝트 공통 구조 - Base Class

클래스 역할
BaseService 모든 Service가 구현하는 인터페이스. 동일한 메서드 시그니처 강제
BaseEntity PK 체계 단일화, 생성일/수정일 이력 필드 자동화
BaseModel Facade/Controller 계층에서 사용하는 DTO 기반 클래스
BaseSearchParam 검색 조건 공통 파라미터 규격화

Entity / Model 분리

  • Entity (extends BaseEntity<Long>): JPA 영속성 객체, DB와 직접 매핑하는 객체
  • Model (extends BaseModel<Long>): Facade/Controller 계층에서 주고받는 객체
  • Converter (MapStruct): 양방향 변환 (toModel / toEntity)

3. 아키텍쳐

기술 스택

분류 기술
언어 / 플랫폼 Java 21, Spring Boot 3.5, Gradle 8.14
데이터 접근 Spring Data JPA, QueryDSL 5.0, Flyway
매핑 MapStruct 1.5.5, Lombok
인증 / 보안 Spring Security, JWT (jjwt 0.11.5)
캐시 / 세션 Redis 7 (토큰 저장소 + 캐시)
데이터베이스 PostgreSQL 15
API 문서 SpringDoc OpenAPI (Swagger UI)
외부 통신 Spring WebFlux (WebClient)
로컬 인프라 Docker Compose (PostgreSQL + Redis 자동 기동)
모니터링 Prometheus, Grafana, Actuator
클라우드 AWS EC2, RDS(PostgreSQL), ElastiCache(Redis)
웹 서버 Nginx (리버스 프록시)
CI/CD GitHub Actions (main 브랜치 자동 배포)

프로젝트 구조

src/main/java/com/example/basicarch/
├── base/        공통 인프라 (annotation, exception, model, redis, security, utils)
├── config/      Spring 설정 (Security, Redis, Swagger, advice, interceptor, scheduler)
└── module/
    ├── user/    사용자, 역할, 인증
    ├── code/    공통 코드/코드그룹
    ├── menu/    메뉴 관리
    └── file/    파일 업로드/다운로드

각 모듈은 controller / facade / service / repository / converter / entity / model 구조를 따릅니다.

공통 유틸리티

클래스 주요 기능
StringUtils isBlank/isEmpty, masking, lpad/rpad, regex, 포맷
DateUtils LocalDateTime-Date-String 변환, 요일, 날짜 연산
CollectionUtils safeStream, merge, separationList, toMap, extractList
CryptoUtils 랜덤 문자열 생성, SecretKey 생성
SessionUtils SecurityContext에서 현재 사용자 정보 조회
CommonUtils HTTP 응답 직접 쓰기, JWT 디코딩

4. 기능

인증 흐름 — 로그인

flowchart LR
    A[클라이언트] -->|id + password| B[AuthFacade] --> C[UserService] --> D[JwtTokenService\n토큰 생성 + Redis 저장] -->|accessToken + refreshToken| A
Loading

인증 흐름 — AccessToken 재발급

flowchart LR
    A[클라이언트] --> B[JwtTokenService]
    B -->|만료 또는 변조| Z[❌ 인증 실패]
B -->|Redis 토큰과 불일치|Y[❌ 중복 로그인]
B -->|정상|C[새 accessToken 발급] --> A
Loading

RefreshTokenStore는 인터페이스로 추상화되어 있어 Redis 외 다른 저장소로 교체 가능합니다.

인증 흐름 — 요청 인증

flowchart LR
    A[클라이언트] -->|HTTP 요청| B[JwtAuthenticationFilter]
    B -->|허용된 URL| E[요청 처리]
    B -->|보호된 URL\n토큰 없음 또는 만료| Z[❌ 401]
B -->|보호된 URL\n토큰 유효| C[AuthUserDetailsService]
C --> D[SecurityContext 설정] --> E
Loading

동적 URI 권한 체크 (rbac코드 삭제, !추후 구현 예정)

flowchart LR
    A[HTTP 요청] --> B[RoleInterceptor] --> C[MenuService]
    C -->|미등록 URI/역할 포함| E[✅ 허용]
C -->|역할 미포함|F[❌ 차단]
Loading

Redis 캐시 전략

캐시 key:value 갱신 주기 무효화 방식
Code 캐시 code::all 스케줄러 N시간 Redis Pub/Sub
Menu 캐시 menu::all 스케줄러 N시간 Redis Pub/Sub
Refresh Token {jwt:refresh:loginId : refreshToken} JWT 만료 시간(TTL) 로그아웃/재로그인 시 삭제

스케줄러 갱신 주기는 cron.yml에서 관리

API 응답 형식

module 디렉터리 범위 안의 모든 응답은 ResponseAdviceResponse로 자동 래핑합니다.

public static <T> Response<T> fail(int status, String error, String message) {
    return Response.<T>builder()
            .status(status)
            .error(error)
            .message(message)
            .build();
}

public static <T> Response<T> fail(int status, String message) {
    return Response.<T>builder()
            .status(status)
            .message(message)
            .build();
}

주요 모듈

user - 사용자

Spring Security + JWT 기반 인증. Role / UserRole / User 구조로 권한을 관리하고, Redis에 Refresh Token을 저장해 체크합니다.

code - 공통 코드

CodeGroup → Code 1:N 계층으로 코드를 관리한다. code 값 없이 저장하면 자동으로 001, 002..., N 채번되고, 변경 시 리스너를 통한 캐시 무효화를 이용합니다.

menu - 메뉴

idparentId 를 이용한 트리 구조. DB에서 가져온 후 서버에서 트리로 조립합니다. 메뉴에 역할을 설정하면 RoleInterceptor로 접근을 제한합니다.

file - 파일

refPath(테이블명) + refId(PK) 조합으로 어느 도메인에나 파일을 붙일 수 있습니다.


5. 실행 가이드

요구 사항: JDK 21, Docker(Windows는 WSL2 필요)

프로필 포트
local 8085
dev 8080
prod 8080

local 환경

Docker가 설치되어 있으면 프로젝트만 구동하면 됩니다. 프로젝트 구동 시, LocalDockerConfig에서 프로젝트 인프라를 자동으로 세팅해줍니다.

docker

접속 정보

Swagger UI

swagger

Grafana

grafana

배포

main 브랜치 push 시 GitHub Actions가 자동으로 빌드 후 EC2에 배포합니다.

main 브랜치 push 시 Railway에 자동으로 배포됩니다.

A. AWS EC2 + GitHub Actions

flowchart LR
    A[Client] --> B[nginx:80] --> C[Spring Boot:8080]
    C --> D[RDS - PostgreSQL]
    C --> E[ElastiCache - Redis]
Loading

githubcicd


### B. Railway

railway

6. 설계 결정

JWT & Refresh Token

2022년도에 JWT 개념을 처음 접했습니다. 당시 Refresh Token을 인메모리로 관리했는데, 인스턴스가 재시작되면 토큰이 날아가고 사용자가 강제 로그아웃되는 문제가 있었습니다. Basic-Arch에서는 처음부터 Redis에 저장하는 방식을 선택했기 때문에, 토큰관리를 좀더 용이하게 할수 있게 되었습니다.


캐시 무효화 전략

Spring Cache를 사용하며, 두 가지 방식으로 캐시를 관리합니다.

docker exec -it basic-arch-redis redis-cli subscribe cache:invalidate

Redis Pub/Sub — 데이터 변경 시 즉시 무효화

@CacheInvalidate(CacheType.CODE)
  • 스케줄러 갱신 주기는 cron.yml에서 관리

7. 로드맵

순서 기술 학습 내용 상태
1 Prometheus + Grafana Actuator 메트릭 수집, 대시보드 구성 완료
2 AWS + ElastiCache EC2 배포, RDS(PostgreSQL), ElastiCache(Redis) 연동 진행중
3 Kubernetes minikube로 현재 프로젝트 배포, 및 실습 진행중
4 Kafka docker-compose에 Kafka 추가하고 Redis 데이터 유실 문제 처리 대기
5 명칭 변경 ModelDto, ConverterMapper 명칭 변경(인텔리제이에서 지원하는 것 확인) 대기

About

반복적인 보일러플레이트를 제거하고, 개발자가 비즈니스 로직에만 집중할 수 있도록 설계하고 있는 Spring Boot 기반 아키텍처 표준화 프레임워크.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors