💻 뚝딱뚝딱/북북클럽

[개발일지 #038] 공통모듈 구조 만들기, 예외 처리 분리하기

뚜루리 2025. 6. 10. 15:42
728x90
320x100

🎯 오늘 개발 할 기능

  • 공통모듈 만들기
  • 예외 처리 분리하기

🛠️ 개발내용

📌 멀티모듈 구조 만들기

 

 멀티모듈(Multi Module)이란?

  • 하나의 메인 프로젝트 안에서 여러 개의 서브 프로젝트(모듈)로 기능을 분리하는 방식
bookbookclub/
├── bbc-user-service
├── bbc-post-service
├── bbc-common
└── build.gradle (루트)

 

✅ 멀티모듈을 사용하는 이유?

이유 설명
💡 역할 분리 (SRP) 도메인마다 책임을 나누고 관심사를 분리함
🔁 재사용성 공통 코드(common)을 여러 서비스에서 재사용 가능
🧪 테스트 용이성 각 모듈별로 독립적인 단위 테스트/통합 테스트 수행 가능
🚀 빌드 효율성 변경된 모듈만 빌드해서 속도 향상 (Gradle Incremental Build)
⚙️ MSA 전환 준비 나중에 각 모듈을 독립 마이크로서비스로 분리하기 쉬움

 

  멀티모듈(Multi Module) 설정하기

1. 루트 프로젝트 생성 (BookBookClub-MSA)

 

settings.gradle

rootProject.name = 'BookBookClub-MSA'

include 'bbc-common'
include 'bbc-user-service'
include 'bbc-post-service'

그냥 원하는 위치에 폴더를 하나 생성하면 됨.

 

2. 서브모듈 생성 (bbc-user-service, bbc-post-service, bbc-common)

  • 생성한 루프 프로젝트 아래에 이미 만들어진 bbc-user-service, bbc-post-service의 프로젝트를 복사해서 넣어준다. (혹시 망할까봐 복사해줌)
  • bbc-common는 새로 만드는 것이라 폴더 하나 만들어서 루트 프로젝트 아래 넣어줌.
  • bbc-user-service, bbc-post-service, bbc-common 각각 build.gradle 작성.
dependencies {
    implementation project(':bbc-common') // 공통 모듈 참조
    implementation 'org.springframework.boot:spring-boot-starter-web'
    ...
}

 

 
 

3. IntelliJ에서 멀티모듈 구성 인식시키기

  • settings.gradle이 제대로 작성되었으면 IntelliJ가 자동 인식함
  • 안 되면: Gradle → Reload All Projects 클릭

 

📦 예시 구조

bookbookclub/
├── bbc-common/
│   └── ApiResponse.java, ErrorCode.java ...
├── bbc-user-service/
│   └── 회원가입, 로그인, OAuth ...
├── bbc-post-service/
│   └── 피드 CRUD, 좋아요 기능 ...
├── build.gradle
└── settings.gradle

 

 

그리고 나서 공통으로 사용하는 것들을 분리해주고 기존에 있던 것들을 삭제 해주는 작업을 해준다.

 

 


📌 예외 처리 분리하기

기존에 클래스만 분리하고 에러코드랑 핸들러는 공통으로 사용했는데, 도메인에 따라서 핸들러와 에러코드 클래스도 분리하기로 한다.

 

예시) EmailVerificationErrorCode

package com.bookbookclub.bbc_user_service.emailverification.exception;

import com.bookbookclub.common.exception.BaseErrorCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
public enum EmailVerificationErrorCode implements BaseErrorCode {

    EMAIL_NOT_VERIFIED("EMAIL_NOT_VERIFIED", "이메일 인증이 완료되지 않았습니다."),
    EMAIL_VERIFICATION_TOO_MANY_ATTEMPTS("TOO_MANY_ATTEMPTS",
            "이메일 인증 시도 횟수를 초과했습니다. 잠시 후 다시 시도해주세요.");

    private final String code;
    private final String message;

    EmailVerificationErrorCode(String code, String message) {
        this.code = code;
        this.message = message;
    }
}

 

예시) EmailNotVerifiedException

package com.bookbookclub.bbc_user_service.emailverification.exception;


import lombok.Getter;

/**
 * 이메일 인증 미완료 예외
 * - 회원가입 시 인증되지 않은 경우 발생
 */
@Getter
public class EmailNotVerifiedException extends RuntimeException {
    private final EmailVerificationErrorCode errorCode;

    public EmailNotVerifiedException() {
        super(EmailVerificationErrorCode.EMAIL_NOT_VERIFIED.getMessage());
        this.errorCode = EmailVerificationErrorCode.EMAIL_NOT_VERIFIED;
    }
}

 

예시) EmailVerificationExceptionHandler

package com.bookbookclub.bbc_user_service.emailverification.exception;

import com.bookbookclub.common.response.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 이메일 인증 관련 예외 처리 핸들러
 */
@Slf4j
@RestControllerAdvice(basePackages = "com.bookbookclub.bbc_user_service.emailverification")
public class EmailVerificationExceptionHandler {

    /**
     * 이메일 인증 미완료 예외 처리
     */
    @ExceptionHandler(EmailNotVerifiedException.class)
    public ApiResponse<?> handleEmailNotVerified(EmailNotVerifiedException e) {
        log.warn("Email not verified: {}", e.getMessage());
        return ApiResponse.fail(e.getErrorCode());
    }

    /**
     * 이메일 인증 시도 횟수 초과 예외 처리
     */
    @ExceptionHandler(EmailVerificationLimitExceededException.class)
    public ApiResponse<?> handleLimitExceeded(EmailVerificationLimitExceededException e) {
        log.warn("Email verification limit exceeded: {}", e.getMessage());
        return ApiResponse.fail(e.getErrorCode());
    }
}
728x90
320x100