백엔드갈아엎기 시리즈

Spring Boot 70 파일 와일드카드 import 제거 + Spotless · 거대 메서드 분해 — POP-SPOT v1.4 백엔드 클린코드

김동현
··5분 읽기

백엔드 7 Wave · 70 파일 · 약 3,700 라인 정리. Spotless 자동 포맷, 와일드카드 import 0, 매직 넘버 상수화, 130 줄 메서드 6 단계로. API/DB 동등.

기능은 그대로 두고 백엔드 코드만 정리한 버전이다. 외부 동작 100% 동일. 70 파일 · 약 3,700 라인. API 응답·DB 스키마·Redis 키 그대로 유지.

이 글에서 다루는 것

  • "기능을 건드리지 않는 리팩터링" 이 왜 포트폴리오 에서 필요했는지
  • Spotless (googleJavaFormat AOSP) 를 도입해 "자동 포맷 검사" 의 한계를 구축한 방법
  • 와일드카드 import (import java.util.*;) 를 전부 구체적으로 풁어 바꾸어야 하는 이유
  • 매직 넘버 (5, 12, 60_000 같은 숫자 리터럴)를 static final 상수로 빼는 이유
  • 130 줄 메서드 를 6 단계로 쪼겜고도 동작이 같은지 확인하는 관점
  • 모르는 단어 한 줄로

    용어한 줄 설명
    SpotlessGradle 플러그인. 코드 포맷을 고정시켜 빌드 시 다른 게 있으면 실패
    googleJavaFormat AOSP구글에서 만든 자바 코드 포맷 규칙. AOSP 변형은 4 칸 인덱트·100 컬럼 제한
    와일드카드 import<code>import java.util.*;</code> 처럼 패키지 전체를 가져오는 구문. 이름 충돌 이 일어나면 추적이 어렵다
    매직 넘버"이게 왜 5 인가" 를 설명해주는 이름 없이 코드에 박힌 숫자
    SLF4J로그 출력의 표준 인터페이스. <code>System.out.println</code> 보다 제어가 쉬움

    무엇이 바뀌었나

    Wave 단위로 끊어서 진행하면 "어디까지 촔겁는가" 에 진행 흐름을 잡을 수 있음.

    Wave범위핵심 작업
    1build.gradleSpotless 플러그인 삽입, googleJavaFormat AOSP 적용
    2음악 서비스 7 파일SpotifySearchService 5 단계 폴백 분해, 매직 넘버 추출
    3자동수집 크롤러 8 파일PopupCrawlOrchestrator 130 줄 → 6 단계 분해
    4Controller 25 파일와일드카드 import 0, 헬퍼 메서드 분리
    5Service 16 파일AuthService/OrderService 7 단계 분해, 메서드 평균 30 줄 미만
    6Entity 핵심 6User · PopupStore · MatePost · Stamp · Orders · MyCourse
    7Config/Exception 9SecurityConfig · JwtFilter · WebSocketConfig · RateLimit
    보강DTO 15 + 잔여 Entity 7"⚡ [13번 수정]" 같은 편집 흔적 84 건 제거

    결과: API 응답 동일, DB 스키마 동일, Redis 키 동일. 외부에서 볼 때는 아무것도 바뀌지 않은 것으로 보이는 리팩터링.


    왜 이렇게 했음

    Spotless 자동 포맷 — 포트폴리오 용 코드 퀄리티 향상이 목표. 사람 눈으로 "탭·스페이스 통일 되어 있나" 확인하는 건 한계가 있고 롬복으로 놈치기도 쉬운다. Spotless 를 빌드 파이프라인에 넣으면 "고치기 전엔 다음 머지 없음" 으로 결정된다.

    와일드카드 import 제거 — IDE 가 자동으로 * 로 접어주는 경우가 많은데, 나중에 이름 충돌 문제 (User 도메인 클래스 vs User 타입 vs lucide-react User 아이콘)이 생기면 추적이 어렵다. 구체적으로 적으면 IDE 의 재명명/찾기 기능이 훨씬 정확해진다.

    매직 넘버 상수화5 가 "로그인 최대 시도 횟수" 인지 "리트라이 지연 초" 인지 읽는 사람은 모른다. MAX_LOGIN_ATTEMPTS_PER_MINUTE = 5 로 적으면 몇 초간 고민할 필요가 없을 정도로 명시적.

    130 줄 메서드 분해runOnce() 안에 검색 · 정규화 · 중복 제거 · Geocoding · 저장 · 로그기록 이 전부 섞여 있었다. 명명된 단계로 나누면 구조가 문단 수준에서 읽힌다.


    코드로는 어떻게 (필요한 부분만)

    build.gradle 에 Spotless 추가.

    파일: popspot-backend/build.gradle (Gradle 빌드 설정)

    groovy
    // v1.4 — popspot-backend/build.gradle 에 Spotless 도입
    plugins {
        id 'com.diffplug.spotless' version '6.25.0'
    //  ^^^^^^^^^^^^^^^^^^^^^^^^^^         ^^^^^^^^
    //  Gradle 플러그인 ID                  버전 고정 — 파이프라인 재현성 확보
    }
    
    spotless {
        java {
    //      ^^^^ java 소스셋에만 규칙 적용. groovy/kotlin 에도 각각 설정 가능
            googleJavaFormat('1.22.0').aosp()
    //      ^^^^^^^^^^^^^^^^ ^^^^^^^^  ^^^^^^
    //      |                |         |
    //      |                |         +-- AOSP 변형: 4칸 인덱트 (기본은 2칸)
    //      |                |             100 컸럼 한계 — 개인 노트북에서 한 눈에 들어오는 폭
    //      |                +-- google-java-format 버전 (포맷 규칙 버전도 고정)
    //      +-- Google 의 공식 자바 포맷. PR 마다 "탭/스페이스 다르다" 싸움 종결
            removeUnusedImports()
    //      ^^^^^^^^^^^^^^^^^^^^^ 실제로 안 쓰는 import 자동 제거
    //                            IDE 의 "unused import warning" 에만 의존하지 않아도 됨
            endWithNewline()
    //      ^^^^^^^^^^^^^^^^ 파일 끝에 개행문자 강제 — POSIX 표준. git diff 노이즈 제거
        }
    }
    
    // v1.4 — ./gradlew check 시 spotlessCheck 가 먼저 돌아감
    tasks.named('check') { dependsOn 'spotlessCheck' }
    //          ^^^^^^^                ^^^^^^^^^^^^^
    //          |                       |
    //          |                       +-- 포맷 위반 있으면 빌드 실패 → PR 머지 차단
    //          +-- gradle 표준 검증 태스크. CI 에서 자동 호출됨
    // 수정 하려면 ./gradlew spotlessApply 실행 → 그림 자동 수정

    매직 넘버 상수화.

    파일: popspot-backend/src/main/java/com/popspot/service/AuthService.java (로그인 시도 회수 체크)

    java
    // v1.3 까지 — 매직 넘버 그대로 노출
    if (attempts >= 5) lockAccount();
    //              ^ 이 5 가 무슨 뜻인지 다른 개발자가 알 방법 없음
    //                  - 로그인 최대 시도 5회?
    //                  - 재시도 지연 5초?
    //                  - 고정 세션 5분?
    //                  고치고 싶을 때 grep 으로 '5' 찾으면 수백 결과

    파일: 같은 AuthService.java (v1.4 교체 후) + 상수 자체는 같은 클래스 상단에 선언

    java
    // v1.4 — 이름 있는 상수로 추출
    private static final int MAX_LOGIN_ATTEMPTS_PER_MINUTE = 5;
    //      ^^^^^^^^^^^^     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //      |                |
    //      |                +-- 대문자 스네이크 — 자바 관습상 상수와 필드 구분
    //      +-- final 은 JVM 이 관련 최적화 적용. static 은 인스턴스당 복제 안 됨
    
    if (attempts >= MAX_LOGIN_ATTEMPTS_PER_MINUTE) lockAccount();
    //              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 읽으면서 의미 해석이 같이 일어남
    //                                            이제 조건을 읽으면 입력 값과 의도가 한꺼번에 드러남

    130 줄 메서드 분해.

    파일: popspot-backend/src/main/java/com/popspot/service/crawler/PopupCrawlOrchestrator.java (v1.3 양식)

    java
    // v1.3 까지 — runOnce() 한 메서드 안에 단계가 전부 섬여 있음 (약 130줄)
    public void runOnce() {
        // 검색 호출 ... 30 줄          ← Naver/Kakao Search API 둘을 순차 호출
        // 정규화 ... 25 줄             ← Groq 프롬프트 컴파일 후 답변 JSON 파싱
        // 중복 제거 ... 15 줄           ← 제목·주소·날짜 같은 건 하나로
        // Geocoding ... 20 줄          ← 주소를 lat/lng 로 변환 (Kakao Local API)
        // DB 저장 ... 25 줄             ← confidence 계산 + 조건 충족 시 INSERT
        // 로그 ... 15 줄                ← 로그 기록·메트릭 업데이트
        // 문제: 한 단계가 실패했을 때 제쳐두고 다음 단계로 가기 어려움 (조기 탈출 return 이 불편)
    }

    파일: 같은 PopupCrawlOrchestrator.java (v1.4 분해 후)

    java
    // v1.4 — 명명된 6개 단계로 분해. 메서드 1개 → 7개
    public void runOnce() {
        var rawResults      = collectRawSnippets();
    //  ^^^   ^^^^^^^^^^      ^^^^^^^^^^^^^^^^^^^
    //  |     |               |
    //  |     |               +-- 1단계: 원시 검색 결과 수집 (List<RawSnippet>)
    //  |     +-- 단계 이름이 변수명으로 이어짐 → 디버깅 시 변수 값을 보면 단계 의미가 바로 드러남
    //  +-- Java 10+ var. 타입 추론 — 점용 타입을 적아 가독성 우선
    
        var normalized      = normalize(rawResults);
    //                                  ^^^^^^^^^^   2단계: 앞 단계 결과 그대로 넘김
    //                                                Groq 정규화. List<NormalizedPopup>
        var deduplicated    = removeDuplicates(normalized);
    //                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^   3단계: 해시(title+address+date) 기준 중복 제거
        var withCoordinates = geocode(deduplicated);
    //                       ^^^^^^^^^^^^^^^^^^^^^   4단계: Kakao Local 로 lat/lng 채움
        var saved           = persistWithConfidence(withCoordinates);
    //                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^   5단계: confidence ≥0.8 이면 게시
        logRunSummary(saved);
    //  ^^^^^^^^^^^^^                                                   6단계: 수집 요약을 admin 로그로 남김
    }
    // 결과: 한 단계만 따로 테스트 가능·단계 당 평균 25줄 안판·각 메서드 IDE outline 에서 본문처럼 읽힘

    핵심 파일: popspot-backend/build.gradle, popspot-backend/src/main/java/.../service/crawler/PopupCrawlOrchestrator.java


    비하인드 · 사고

    Spotless 최초 실행하자 109 파일이 자동 수정되었다. 사소한 게 아니라 JavaDoc 의 긴 줄을 100 컬럼 기준으로 자동 줄바꿈해서 일어난 건데, 마침 한국어 멀티라인 주석이 많이 걸렸다. 이 문제는 v2.7.1, v2.7.2, v2.10.1 에서 다시 돌아온다.

    교훈 한 줄. 자동 포맷은 설정을 한 번 맞추면 운영이 편하지만, 입력 규칙 (멀티라인 주석 최대 컬럼) 을 처음에 단단하게 맞춰둘면 다음 번 충돌이 적다.


    관련 글

  • 이전 — v1.3, 음악 매칭 새 코어
  • 다음 — v1.5, 프론트 any 17 → 0
  • 공유

    댓글