2026-01-30 11:28:18 +09:00
2026-01-30 11:28:18 +09:00
2026-01-30 11:28:18 +09:00
2026-01-30 11:28:18 +09:00
2026-01-30 11:28:18 +09:00
2026-01-30 11:28:18 +09:00
2026-01-30 11:28:18 +09:00
2026-01-30 11:28:18 +09:00
2026-01-30 11:28:18 +09:00
2026-01-30 11:28:18 +09:00

Backend 프로젝트 가이드

Spring Boot 3.x 기반 백엔드 프로젝트 개발 가이드입니다.


목차

  1. 개발 규칙
  2. 개발 환경 및 옵션 리스팅
  3. 설치 및 동작 방법
  4. 아키텍처 & 책임 분리 규칙
  5. 네이밍 & 패키지 규칙
  6. 예외 처리 & 에러 응답 규칙
  7. 로그 & 감사(Audit) 규칙
  8. 설정 관리 규칙
  9. 보안 관련 추가 규칙
  10. 테스트 & 검증 규칙
  11. 금지 패턴 / 안티 패턴 목록
  12. 성능 최적화 가이드
  13. 의존성 관리
  14. 트러블슈팅 가이드

1. 개발 규칙

1.1 시큐어 코딩 규칙 준수

  • OWASP Top 10 기반 보안 규칙 준수 필수
  • 모든 입력값은 검증 후 사용 (DTO + @Valid 활용)
  • SQL Injection 방지: MyBatis #{}만 사용, ${} 절대 금지
  • Path Traversal 방지: FileUtils.safeResolve() 사용 필수
  • XSS 방지: 출력 시 HTML 이스케이프 처리
  • 세션 보안: HttpOnly, Secure, SameSite 설정 준수
  • 인가 검증: 모든 민감 기능에 소유권/권한 확인 필수

상세 규칙은 base_arcitectures_md/SECURE_RULE.md 참조

1.2 공통 소스 기능 및 활용 방법

공통 유틸리티 (common/util/)

  • Utils.java: Cookie, Crypto, DateTime, Json, Masking

    • Utils.Json.toJson() / fromJson(): JSON 직렬화/역직렬화
    • Utils.Crypto.sha256(): SHA-256 해시
    • Utils.Crypto.randomToken(): 보안 토큰 생성
    • Utils.DateTime.nowKst(): KST 기준 현재 시간
    • Utils.Masking.maskHeaders(): 헤더 민감정보 마스킹
    • Utils.Masking.sanitizeBodyForLog(): 로그용 본문 마스킹
  • FileUtils.java: 파일/경로 보안 처리

    • FileUtils.safeResolve(): Path Traversal 방지 경로 해석
    • FileUtils.sanitizeFilename(): 파일명 정제
    • FileUtils.isAllowedExtension(): 확장자 화이트리스트 검증
  • ServletUtils.java: HttpServletRequest 보조

    • ServletUtils.getClientIp(): 클라이언트 IP 추출
    • ServletUtils.getBearerToken(): Bearer 토큰 추출
    • ServletUtils.getCookieValue(): 쿠키 값 추출
    • ServletUtils.isAjax() / isJson(): 요청 타입 판단

공통 응답 (common/response/)

  • ApiResponse<T>: 표준 API 응답 포맷

    ApiResponse.success(data);  // 성공 응답
    ApiResponse.error(apiError); // 에러 응답
    
  • PageQuery: 페이징 쿼리 파라미터

    • pageIndex, pageSize, offset, limit 자동 계산
    • applyTotalCount() 호출 후 totalPages, hasNext, hasPrevious 사용
  • PageResult<T>: 페이징 결과 래퍼

    • items: 실제 데이터 리스트
    • page: PageQuery 메타데이터

공통 예외 (common/exception/)

  • ErrorCode: 표준 에러 코드 enum
  • BizException: 비즈니스 예외 (ErrorCode 기반)
  • GlobalExceptionHandler: 전역 예외 처리 (@RestControllerAdvice)

1.3 공통소스 활용해서 서비스 구현하는 규칙

  1. Controller: 요청/응답 변환만 담당

    • DTO 검증 (@Valid)
    • Service 호출
    • ApiResponse로 래핑
  2. Service: 비즈니스 로직 전담

    • 공통 유틸 활용 (Utils.*, FileUtils.*, ServletUtils.*)
    • BizException으로 비즈니스 예외 처리
    • Mapper 호출
  3. Mapper: 데이터 접근 전담

    • MyBatis XML 또는 @Mapper 인터페이스
    • #{}만 사용, ${} 금지
  4. DTO: 요청/응답 데이터 전달 객체

    • @NotBlank, @NotNull, @Size 등 검증 어노테이션 활용

1.4 공통 소스 수정 규칙

  • 공통 소스는 불가피한 경우가 아니면 절대 수정 금지
  • 추가(Extension)는 허용: 새로운 유틸 메서드 추가 가능
  • 수정이 필요한 경우 반드시 팀 리뷰 및 승인 필요
  • 공통 소스 수정 시 모든 도메인에 미치는 영향 검토 필수

2. 개발 환경 및 옵션 리스팅

2.1 필수 환경

  • Java: 17 이상
  • Gradle: 8.x 이상 (Wrapper 포함)
  • Spring Boot: 3.5.10
  • Database: MariaDB 10.x 이상
  • IDE: IntelliJ IDEA 권장 (또는 Eclipse, VS Code)

2.2 주요 의존성

  • spring-boot-starter-web: 웹 애플리케이션
  • spring-boot-starter-security: 인증/인가
  • spring-boot-starter-validation: 입력 검증
  • mybatis-spring-boot-starter:3.0.4: MyBatis 통합
  • mariadb-java-client: MariaDB 드라이버
  • commons-lang3:3.20.0: 공통 유틸리티

2.3 옵션 기능

현재 프로젝트는 기본 구조만 제공하며, 다음 기능들은 옵션으로 추가 가능:

  • Redis: 세션 저장소 (운영 환경 권장)
  • OAuth2: 소셜 로그인
  • 스케줄러: @Scheduled 기반 작업
  • 파일 업로드: FileUtils 기반 보안 업로드

옵션 활성화는 application.ymlbuild.gradle에 의존성 추가 후 설정 파일에서 제어.


3. 설치 및 동작 방법

3.1 사전 준비

  1. Java 17 설치 확인

    java -version
    
  2. MariaDB 설치 및 데이터베이스 생성

    CREATE DATABASE tscc CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    CREATE USER 'tscc'@'localhost' IDENTIFIED BY 'tscc1234';
    GRANT ALL PRIVILEGES ON tscc.* TO 'tscc'@'localhost';
    FLUSH PRIVILEGES;
    

3.2 프로젝트 설정

  1. 의존성 설치

    cd backend
    ./gradlew build
    

    또는 IDE에서 Gradle Sync 실행

  2. 환경별 설정 파일 확인

    • src/main/resources/application.yaml: 공통 설정
    • src/main/resources/application-dev.yml: 개발 환경
    • src/main/resources/application-prod.yml: 운영 환경
  3. 데이터베이스 연결 정보 확인

    • application-dev.yml에서 spring.datasource.* 확인

3.3 실행

  1. IDE에서 실행

    • BootstrapApplication.java를 Run/Debug
  2. Gradle로 실행

    ./gradlew bootRun
    
  3. 빌드 후 JAR 실행

    ./gradlew bootJar
    java -jar build/libs/base-0.0.1-SNAPSHOT.jar
    

3.4 확인

  • 서버 시작 후 http://localhost:8080/api/auth/csrf 접속하여 CSRF 토큰 확인
  • 로그에서 "Started BootstrapApplication" 메시지 확인

4. 아키텍처 & 책임 분리 규칙

4.1 계층별 책임 명확화

Controller (api/<domain>/controller/)

  • 책임: HTTP 요청/응답 처리
  • 역할:
    • 요청 파라미터 → DTO 변환
    • DTO 검증 (@Valid)
    • Service 메서드 호출
    • ApiResponse로 응답 래핑
  • 금지:
    • 비즈니스 로직 포함
    • DB 직접 접근
    • Mapper 직접 호출

Service (api/<domain>/service/)

  • 책임: 비즈니스 로직 처리
  • 역할:
    • 도메인 로직 구현
    • 트랜잭션 관리 (@Transactional)
    • Mapper 호출
    • 공통 유틸 활용
    • BizException 발생
  • 필수: 모든 비즈니스 로직은 Service에 위치

Mapper (api/<domain>/mapper/)

  • 책임: 데이터 접근
  • 역할:
    • SQL 쿼리 실행
    • 결과 → 도메인 객체 변환
  • 금지:
    • 비즈니스 로직 포함
    • ${} 사용

DTO (api/<domain>/dto/)

  • 책임: 계층 간 데이터 전달
  • 구분:
    • Request DTO: Controller 입력
    • Response DTO: Controller 출력
  • 필수: 검증 어노테이션 (@NotBlank, @NotNull, @Size 등)

Common (common/)

  • 책임: 공통 기능 제공
  • 구성:
    • util/: 유틸리티 (Utils, FileUtils, ServletUtils)
    • response/: 공통 응답 포맷
    • exception/: 공통 예외 처리
    • config/: 공통 설정
    • validation/: 공통 검증 로직

4.2 비즈니스 로직 위치 규칙

위치 비즈니스 로직 허용 여부
Controller 금지
Service 필수
Mapper 금지
Util 금지
Common 금지 (공통 기능만)

예시:

// ❌ 잘못된 예: Controller에 비즈니스 로직
@PostMapping("/users")
public ApiResponse<User> createUser(@RequestBody UserRequest req) {
    if (req.getEmail().contains("@admin.com")) {
        throw new RuntimeException("Admin email not allowed");
    }
    // ...
}

// ⭕ 올바른 예: Service에 비즈니스 로직
@Service
public class UserService {
    public User createUser(UserRequest req) {
        if (req.getEmail().contains("@admin.com")) {
            throw new BizException(ErrorCode.INVALID_REQUEST, "Admin email not allowed");
        }
        // ...
    }
}

4.3 공통 모듈과 도메인 모듈의 경계 규칙

  • 공통 모듈 (common/): 모든 도메인에서 공통으로 사용하는 기능

    • 도메인 특화 로직 포함 금지
    • 도메인별 분기 처리 최소화
  • 도메인 모듈 (api/<domain>/): 특정 도메인 전용 기능

    • 다른 도메인에서 직접 참조 금지
    • 도메인 간 통신은 Service → Service 호출

4.4 "이 로직은 어디에 두어야 하는가" 판단 기준

  1. 입력 검증: Controller (DTO + @Valid)
  2. 비즈니스 규칙: Service
  3. 데이터 조회/저장: Mapper
  4. 공통 변환/포맷팅: Common Util
  5. 보안 처리: Common Util (FileUtils, ServletUtils)
  6. 예외 변환: GlobalExceptionHandler

5. 네이밍 & 패키지 규칙

5.1 패키지 네이밍 규칙

  • 기본 패키지: kr.tscc.base

  • 도메인 패키지: kr.tscc.base.api.<domain>

    • 예: kr.tscc.base.api.auth
    • 예: kr.tscc.base.api.user
    • 예: kr.tscc.base.api.document
  • 계층별 서브패키지:

    • controller/: Controller 클래스
    • service/: Service 클래스
    • dto/: DTO 클래스
    • mapper/: Mapper 인터페이스
  • 공통 패키지: kr.tscc.base.common

    • config/: 설정 클래스
    • exception/: 예외 클래스
    • response/: 응답 클래스
    • util/: 유틸리티 클래스
    • validation/: 검증 클래스

5.2 클래스 / 메소드 / 변수 네이밍 기준

클래스

  • Controller: {Domain}Controller (예: AuthController)
  • Service: {Domain}Service (예: AuthService)
  • DTO: {Purpose}{Domain} (예: LoginRequest, MeResponse)
  • Mapper: {Domain}Mapper (예: UserMapper)
  • Util: {Purpose}Utils (예: FileUtils, ServletUtils)

메소드

  • Controller: HTTP 메서드 기반 (예: login, logout, me)
  • Service: 비즈니스 동사 (예: createUser, updateUser, deleteUser)
  • Mapper: CRUD 동사 (예: findById, insert, update, delete)

변수

  • camelCase 사용
  • boolean: is, has, can 접두사 (예: isActive, hasPermission)
  • Collection: 복수형 (예: users, items)

5.3 약어 사용 허용 / 금지 리스트

허용 약어

  • id, url, api, dto, vo, dao, util, config, auth, admin

금지 약어

  • usr (→ user), svc (→ service), ctrl (→ controller)
  • mgr (→ manager), info (→ information), num (→ number)

5.4 DB 컬럼 ↔ DTO ↔ VO ↔ API 필드 매핑 규칙

DB 컬럼 → DTO/VO

  • 스네이크 케이스 → 카멜 케이스 (MyBatis map-underscore-to-camel-case: true 활용)
    • DB: user_id, created_at
    • DTO: userId, createdAt

DTO → API 응답

  • 카멜 케이스 유지 (JSON 기본)
    • DTO: userId, email
    • API: {"userId": 1, "email": "user@example.com"}

예외: API 명세서 요구사항

  • API 명세서에서 스네이크 케이스를 요구하는 경우, DTO에 @JsonProperty 사용
    @JsonProperty("user_id")
    private Long userId;
    

6. 예외 처리 & 에러 응답 규칙

6.1 공통 Exception 구조

ErrorCode (enum)

public enum ErrorCode {
    INVALID_REQUEST("C001", "Invalid request"),
    UNAUTHORIZED("C002", "Unauthorized"),
    FORBIDDEN("C003", "Forbidden"),
    NOT_FOUND("C004", "Resource not found"),
    INTERNAL_ERROR("C999", "Internal server error");
}

BizException

throw new BizException(ErrorCode.INVALID_REQUEST);
throw new BizException(ErrorCode.NOT_FOUND, "User not found: " + userId);

GlobalExceptionHandler

  • @RestControllerAdvice로 전역 처리
  • SecurityException → 403 Forbidden
  • BizException → 400 Bad Request (ErrorCode 기반)
  • Exception → 500 Internal Server Error

6.2 비즈니스 예외 vs 시스템 예외 구분

예외 타입 발생 위치 처리 방법 사용자 메시지
비즈니스 예외 Service BizException 구체적 메시지
시스템 예외 모든 계층 GlobalExceptionHandler 일반화된 메시지

예시:

// 비즈니스 예외: 사용자에게 구체적 메시지
if (user == null) {
    throw new BizException(ErrorCode.NOT_FOUND, "User not found: " + userId);
}

// 시스템 예외: 일반화된 메시지 (상세는 로그에만)
catch (SQLException e) {
    log.error("Database error", e);
    throw new BizException(ErrorCode.INTERNAL_ERROR);
}

6.3 API 응답 포맷 통일 규칙

모든 API 응답은 ApiResponse<T> 형식:

// 성공 응답
{
  "success": true,
  "data": { ... },
  "error": null
}

// 에러 응답
{
  "success": false,
  "data": null,
  "error": {
    "code": "C001",
    "message": "Invalid request"
  }
}

Controller 예시:

@PostMapping("/login")
public ApiResponse<MeResponse> login(@Valid @RequestBody LoginRequest request) {
    authService.login(request);
    SessionUser user = (SessionUser) authService.me();
    return ApiResponse.success(new MeResponse(user.getUserId(), user.getEmail(), user.getDisplayName()));
}

6.4 로그 기록 기준과 사용자 노출 메시지 분리 규칙

  • 로그: 상세 정보 (스택 트레이스, 파라미터, 내부 상태)
  • 사용자 메시지: 일반화된 메시지 (민감 정보 제외)

예시:

// ❌ 잘못된 예: 사용자에게 상세 정보 노출
catch (SQLException e) {
    return ApiResponse.error(new ApiError("DB_ERROR", e.getMessage()));
}

// ⭕ 올바른 예: 로그에는 상세, 사용자에게는 일반화
catch (SQLException e) {
    log.error("Database error: userId={}, operation={}", userId, operation, e);
    return ApiResponse.error(new ApiError(ErrorCode.INTERNAL_ERROR.code(), ErrorCode.INTERNAL_ERROR.message()));
}

7. 로그 & 감사(Audit) 규칙

7.1 로그 레벨 사용 기준

레벨 사용 시기 예시
DEBUG 개발 중 상세 디버깅 파라미터 값, 중간 상태
INFO 정상 흐름의 중요 이벤트 로그인 성공, 주요 비즈니스 작업 완료
WARN 예상 가능한 문제 잘못된 입력, 재시도 필요
ERROR 예상치 못한 오류 예외 발생, 시스템 오류

예시:

log.debug("Processing user request: userId={}, params={}", userId, params);
log.info("User logged in: userId={}", userId);
log.warn("Invalid input: field={}, value={}", field, value);
log.error("Failed to process request: userId={}", userId, exception);

7.2 개인정보 및 민감정보 마스킹 규칙

마스킹 대상:

  • 비밀번호, 토큰, 세션 ID
  • 주민번호, 카드번호, 계좌번호
  • 이메일 (일부 마스킹 가능)
  • 전화번호 (일부 마스킹 가능)

마스킹 방법:

  • Utils.Masking.maskHeaders(): HTTP 헤더 마스킹
  • Utils.Masking.sanitizeBodyForLog(): 요청/응답 본문 마스킹

예시:

// ❌ 잘못된 예: 민감정보 로그 출력
log.info("User login: email={}, password={}", email, password);

// ⭕ 올바른 예: 마스킹 후 로그 출력
log.info("User login: email={}", Utils.Masking.maskEmail(email));

7.3 공통 로깅 유틸 사용 규칙

  • RequestResponseLoggingFilter: 모든 HTTP 요청/응답 자동 로깅

    • 민감 정보 자동 마스킹
    • /health, /actuator 제외
  • 수동 로깅: logback-spring.xml 설정 확인

    • 패키지별 로그 레벨 설정
    • 파일/콘솔 출력 설정

7.4 요청 추적용 식별자(Request ID 등) 사용 여부

현재는 기본 구조만 제공. 필요 시 다음 추가 가능:

  • MDC (Mapped Diagnostic Context) 활용
  • RequestResponseLoggingFilter에서 Request ID 생성/추가
  • 로그에 Request ID 포함

8. 설정 관리 규칙

8.1 application.yml 분리 전략

  • application.yaml: 공통 설정 (MyBatis, 로깅 등)
  • application-dev.yml: 개발 환경 (데이터베이스, 로그 레벨 등)
  • application-prod.yml: 운영 환경 (데이터베이스, 보안 설정 등)

활성 프로파일 설정:

# application.yaml
spring:
  profiles:
    active: dev  # 또는 prod

8.2 환경별(dev / stage / prod) 설정 원칙

  • 공통 설정: application.yaml
  • 환경별 설정: application-{profile}.yml
  • 민감 정보: 환경 변수 또는 Secret Manager 사용
  • 데이터베이스: 환경별 별도 인스턴스

8.3 옵션 처리 기준 (enable / disable 방식)

옵션 기능은 다음 방식으로 제어:

  1. 의존성 추가/제거: build.gradle

  2. 설정 파일에서 활성화/비활성화: application-{profile}.yml

    feature:
      redis:
        enabled: true
      oauth2:
        enabled: false
    
  3. 조건부 Bean 생성: @ConditionalOnProperty 활용

    @ConditionalOnProperty(name = "feature.redis.enabled", havingValue = "true")
    @Bean
    public RedisTemplate<?, ?> redisTemplate() {
        // ...
    }
    

8.4 Redis, 외부 시스템 사용 여부를 옵션으로 제어하는 규칙

  • Redis: 세션 저장소 옵션

    • 활성화: spring.session.store-type=redis
    • 비활성화: 기본 인메모리 세션
  • 외부 API: 설정 파일에서 URL/키 관리

    • 개발: Mock 서버 또는 테스트 환경
    • 운영: 실제 외부 API

9. 보안 관련 추가 규칙 (시큐어 코딩 보강)

9.1 인증 / 인가 흐름 준수 규칙

인증 (Authentication)

  • 세션 기반 인증 사용
  • 로그인 성공 시 SessionUser 세션 저장
  • LoginUserPrincipal로 Spring Security 통합

인가 (Authorization)

  • Deny-by-default: 명시적 허용만 접근 가능
  • 소유권 검증: 리소스 접근 시 사용자 ID 확인
  • RBAC: 역할 기반 접근 제어 (UserRoles enum 활용)

예시:

// ❌ 잘못된 예: 소유권 검증 없음
@GetMapping("/documents/{id}")
public ApiResponse<Document> getDocument(@PathVariable Long id) {
    return ApiResponse.success(documentMapper.findById(id));
}

// ⭕ 올바른 예: 소유권 검증 포함
@GetMapping("/documents/{id}")
public ApiResponse<Document> getDocument(@PathVariable Long id) {
    Document doc = documentMapper.findById(id);
    SessionUser user = getCurrentUser();
    if (!doc.getUserId().equals(user.getUserId())) {
        throw new BizException(ErrorCode.FORBIDDEN);
    }
    return ApiResponse.success(doc);
}

9.2 세션 / 토큰 사용 시 주의 사항

  • 세션 쿠키 보안 옵션:

    • HttpOnly: true (XSS 방지)
    • Secure: true (HTTPS 전용, 운영 환경)
    • SameSite: Strict 또는 Lax (CSRF 방지)
  • 세션 고정 공격 방지: 로그인 시 세션 재발급 (sessionFixation().migrateSession())

  • 세션 타임아웃: 적절한 시간 설정 (기본 30분)

9.3 파일 업로드 / 다운로드 처리 규칙

업로드

  1. 확장자 화이트리스트: FileUtils.isAllowedExtension() 사용
  2. MIME 타입 검증: Content-Type 확인
  3. 파일 크기 제한: 설정 파일에서 제한
  4. 파일명 정제: FileUtils.sanitizeFilename() 사용
  5. 저장 경로 검증: FileUtils.safeResolve() 사용
  6. 웹루트 밖 저장: 실행 파일 접근 방지
  7. UUID 파일명: 원본 파일명 노출 방지

다운로드

  1. Path Traversal 방지: FileUtils.safeResolve() 사용
  2. 소유권 검증: 파일 접근 권한 확인
  3. Content-Disposition: 안전한 파일명 설정

9.4 외부 API 연동 시 보안 체크리스트

  • API 키/토큰 환경 변수 관리 (하드코딩 금지)
  • HTTPS만 사용 (HTTP 금지)
  • 타임아웃 설정 (무한 대기 방지)
  • 입력값 검증 (외부 API로 전송 전)
  • 응답 검증 (예상 형식 확인)
  • 재시도 정책 (Rate Limiting 고려)
  • 로깅 (민감 정보 제외)

10. 테스트 & 검증 규칙

10.1 단위 테스트 작성 기준 (필수 / 선택 구분)

필수 테스트

  • Service 비즈니스 로직: 핵심 비즈니스 규칙 검증
  • Util 보안 기능: FileUtils, ServletUtils 등

선택 테스트

  • Controller: 통합 테스트로 대체 가능
  • Mapper: 실제 DB 연동 테스트 (로컬 환경)

예시:

@SpringBootTest
class AuthServiceTest {
    @Autowired
    private AuthService authService;

    @Test
    void testLoginSuccess() {
        // Given
        LoginRequest request = new LoginRequest();
        request.setEmail("user@example.com");
        request.setPassword("password123");

        // When & Then
        assertDoesNotThrow(() -> authService.login(request));
    }
}

10.2 테스트용 데이터 작성 규칙

  • 테스트 전용 데이터: @Sql 또는 @TestPropertySource 활용
  • 격리: 각 테스트는 독립적으로 실행 가능해야 함
  • 정리: @AfterEach 또는 @Sql(scripts = "cleanup.sql", executionPhase = AFTER_TEST_METHOD)

10.3 로컬 테스트 → 통합 테스트 흐름

  1. 로컬 단위 테스트: Service, Util 등
  2. 로컬 통합 테스트: Controller + Service + Mapper (로컬 DB)
  3. 통합 테스트 환경: 실제 테스트 서버 (선택)

10.4 테스트 미수행 시 병합 제한 여부

  • 현재는 권고 사항 (필수 아님)
  • 향후 CI/CD 파이프라인에서 테스트 실패 시 병합 차단 가능

11. 금지 패턴 / 안티 패턴 목록

11.1 공통 Util에 비즈니스 로직 포함

// ❌ 잘못된 예
public class Utils {
    public static boolean isAdminUser(String email) {
        return email.contains("@admin.com");
    }
}

// ⭕ 올바른 예: Service에 비즈니스 로직
@Service
public class UserService {
    public boolean isAdminUser(String email) {
        return email.contains("@admin.com");
    }
}

11.2 Controller에서 DB 직접 접근

// ❌ 잘못된 예
@RestController
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        return userMapper.findById(id);
    }
}

// ⭕ 올바른 예: Service를 통한 접근
@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/users/{id}")
    public ApiResponse<User> getUser(@PathVariable Long id) {
        return ApiResponse.success(userService.findById(id));
    }
}

11.3 옵션 무시 후 하드코딩

// ❌ 잘못된 예
@Value("${feature.redis.enabled:false}")
private boolean redisEnabled;

public void someMethod() {
    // 옵션 무시하고 하드코딩
    RedisTemplate<?, ?> redis = new RedisTemplate<>();
    // ...
}

// ⭕ 올바른 예: 옵션 확인
@ConditionalOnProperty(name = "feature.redis.enabled", havingValue = "true")
@Bean
public RedisTemplate<?, ?> redisTemplate() {
    // ...
}

11.4 공통 코드 복사 후 개별 서비스에 포함

// ❌ 잘못된 예: 공통 코드를 각 Service에 복사
@Service
public class UserService {
    private String maskEmail(String email) {
        // 마스킹 로직 복사
    }
}

@Service
public class DocumentService {
    private String maskEmail(String email) {
        // 동일한 마스킹 로직 복사
    }
}

// ⭕ 올바른 예: 공통 Util 사용
public class UserService {
    public void someMethod() {
        String masked = Utils.Masking.maskEmail(email);
    }
}

11.5 MyBatis ${} 사용

<!-- ❌ 잘못된 예: SQL Injection 위험 -->
<select id="findUsers">
    SELECT * FROM users WHERE name = '${name}'
</select>

<!-- ⭕ 올바른 예: #{} 사용 -->
<select id="findUsers">
    SELECT * FROM users WHERE name = #{name}
</select>

11.6 민감 정보 로그 출력

// ❌ 잘못된 예
log.info("User login: email={}, password={}", email, password);

// ⭕ 올바른 예: 마스킹 또는 제외
log.info("User login: email={}", Utils.Masking.maskEmail(email));

11.7 예외 삼키기 (Empty Catch)

// ❌ 잘못된 예
try {
    someMethod();
} catch (Exception e) {
    // 아무 처리 없음
}

// ⭕ 올바른 예: 로깅 또는 재throw
try {
    someMethod();
} catch (Exception e) {
    log.error("Error occurred", e);
    throw new BizException(ErrorCode.INTERNAL_ERROR);
}

12. 성능 최적화 가이드

12.1 데이터베이스 최적화

쿼리 최적화

  • 인덱스 활용: 자주 조회되는 컬럼에 인덱스 생성
  • N+1 문제 방지: JOIN 또는 @BatchSize 활용
  • 페이징 필수: 대량 데이터 조회 시 PageQuery 사용

예시:

// ❌ 잘못된 예: N+1 문제
List<User> users = userMapper.findAll();
for (User user : users) {
    List<Document> docs = documentMapper.findByUserId(user.getId()); // N번 쿼리
}

// ⭕ 올바른 예: JOIN 또는 배치 조회
List<User> users = userMapper.findAllWithDocuments(); // 1번 쿼리

12.2 애플리케이션 최적화

트랜잭션 최소화

  • 필요한 범위만 트랜잭션: @Transactional 범위 최소화
  • 읽기 전용 트랜잭션: 조회만 하는 경우 readOnly = true

로깅 최적화

  • 로그 레벨 조정: 운영 환경에서는 INFO 이상만
  • 과도한 로깅 방지: 반복적인 로그는 제한

13. 의존성 관리

13.1 버전 관리 원칙

  • 명시적 버전 지정: build.gradle에 버전 명시
  • 보안 패치 우선: 취약점 발견 시 즉시 업데이트
  • 마이너 버전 업데이트: 정기적으로 검토 및 업데이트

13.2 보안 취약점 점검

정기 점검:

./gradlew dependencyCheckAnalyze

수동 점검:

13.3 업데이트 프로세스

  1. 의존성 업데이트: build.gradle 수정
  2. 로컬 테스트: 업데이트 후 빌드 및 테스트
  3. 통합 테스트: 개발 환경에서 검증
  4. 운영 배포: 검증 완료 후 배포

14. 트러블슈팅 가이드

14.1 자주 발생하는 문제

문제 1: MyBatis 매퍼 파일을 찾을 수 없음

증상: Could not find resource mapper/**/*.xml

해결:

  1. application.yaml에서 mybatis.mapper-locations 확인
  2. src/main/resources/mapper/ 디렉터리 구조 확인
  3. 빌드 후 build/resources/main/mapper/에 파일 존재 확인

문제 2: 세션이 유지되지 않음

증상: 로그인 후 요청 시 401 Unauthorized

해결:

  1. withCredentials: true 설정 확인 (프론트엔드)
  2. CORS 설정에서 allowCredentials: true 확인 (백엔드)
  3. 쿠키 도메인/경로 설정 확인

문제 3: CSRF 토큰 오류

증상: 403 Forbidden (CSRF 토큰 불일치)

해결:

  1. CookieCsrfTokenRepository.withHttpOnlyFalse() 설정 확인
  2. 프론트엔드에서 XSRF-TOKEN 쿠키 읽기 확인
  3. 요청 헤더에 X-XSRF-TOKEN 포함 확인

14.2 디버깅 팁

로그 레벨 조정

# application-dev.yml
logging:
  level:
    kr.tscc.base: DEBUG
    org.springframework.web: DEBUG
    org.mybatis: DEBUG

SQL 로깅

# application-dev.yml
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

참고 문서

  • 아키텍처 문서: base_arcitectures_md/BACK_ARCHITECTURE_V1.md
  • 보안 규칙: base_arcitectures_md/BACKEND_SECURE_RULE.md
  • 공통 보안 규칙: ../SECURE_RULE.md

마지막 업데이트: 2024년

Description
No description provided
Readme 122 KiB
Languages
Java 100%