[spring] 언제! 리팩토링해야 할까? 실전 가이드- 코드 품질 문제 (2/3)

2025. 10. 4. 13:48·공부일기../Spring

코드 품질 문제

5. null 체크가 너무 많은 경우

문제: null 체크 지옥
해결: Null Object 패턴, Optional
효과: NPE 방지, 가독성 향상

 

문제 상황:

public void displayUserInfo(User user) {
    if (user != null) {
        if (user.getProfile() != null) {
            if (user.getProfile().getAddress() != null) {
                System.out.println(user.getProfile().getAddress().getCity());
            } else {
                System.out.println("주소 없음");
            }
        } else {
            System.out.println("프로필 없음");
        }
    } else {
        System.out.println("사용자 없음");
    }
}

null 체크 지옥... 가독성도 떨어지고 실수로 체크 안 하면 NPE가 터진다.

 

사고 과정:

  1. null 체크 반복 → Null Object 패턴 or Optional 활용
  2. null 대신 기본값 객체 반환
  3. 방어적 복사보다는 설계로 해결

해결 1: Null Object 패턴

class NullUser extends User {
    @Override
    public Profile getProfile() {
        return new NullProfile();
    }
}

class NullProfile extends Profile {
    @Override
    public Address getAddress() {
        return new NullAddress();
    }
}

class NullAddress extends Address {
    @Override
    public String getCity() {
        return "정보 없음";
    }
}

// 사용
public void displayUserInfo(User user) {
    System.out.println(user.getProfile().getAddress().getCity());
}

해결 2: Optional

public void displayUserInfo(User user) {
    String city = Optional.ofNullable(user)
        .map(User::getProfile)
        .map(Profile::getAddress)
        .map(Address::getCity)
        .orElse("정보 없음");
    
    System.out.println(city);
}

 

 

 


 

6. 같은 코드가 여러 곳에 복사된 경우

문제: 중복 코드
해결: 공통 로직 추출
효과: 변경 시 한 곳만 수정하면 됨

 

문제 상황:

// 사용자 컨트롤러
public class UserController {
    public Response getUser(Long id) {
        if (id == null || id <= 0) {
            return Response.error("잘못된 ID입니다");
        }
        // ... 로직
    }
}

// 상품 컨트롤러
public class ProductController {
    public Response getProduct(Long id) {
        if (id == null || id <= 0) {
            return Response.error("잘못된 ID입니다");
        }
        // ... 로직
    }
}

// 주문 컨트롤러
public class OrderController {
    public Response getOrder(Long id) {
        if (id == null || id <= 0) {
            return Response.error("잘못된 ID입니다");
        }
        // ... 로직
    }
}

ID 검증 로직이 3번 복사됐다. 나중에 검증 규칙이 바뀌면 3곳을 다 수정해야 한다.

 

사고 과정:

  1. 중복 코드 발견 → DRY(Don't Repeat Yourself) 원칙 위반
  2. 공통 로직 추출
  3. 재사용 가능한 컴포넌트로 만들기

해결:

// 검증 유틸리티
public class ValidationUtils {
    public static void validateId(Long id) {
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("잘못된 ID입니다");
        }
    }
}

// 또는 애너테이션 활용 (Spring)
public class UserController {
    public Response getUser(@Positive Long id) {
        // 검증은 프레임워크가 자동 처리
    }
}

// 또는 공통 베이스 클래스
public abstract class BaseController {
    protected void validateId(Long id) {
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("잘못된 ID입니다");
        }
    }
}

 

 


 

 

7. 매개변수가 너무 많은 경우

문제: 매개변수 4개 이상
해결: Parameter Object 패턴
효과: 가독성 향상, 파라미터 그룹 관리 용이

 

문제 상황:

public void createUser(String name, String email, String phone, 
                       String address, String city, String zipCode,
                       int age, String gender) {
    // 어떤 파라미터가 뭔지 헷갈린다
}

// 호출할 때도 지옥
createUser("홍길동", "hong@example.com", "010-1234-5678",
          "서울시 강남구", "서울", "12345", 30, "남성");

매개변수가 8개다. 순서를 바꿔서 넣어도 컴파일 에러가 안 난다.

실수하기 딱 좋은 구조다.

 

사고 과정:

  1. 관련된 데이터를 객체로 묶는다
  2. 매개변수 순서 실수를 방지한다
  3. 나중에 필드 추가가 쉬워진다

해결:

// 사용자 정보 객체
public class UserInfo {
    private String name;
    private String email;
    private String phone;
    private Address address;
    private int age;
    private String gender;
    
    // 빌더 패턴 활용
    public static class Builder {
        private String name;
        private String email;
        // ... 나머지 필드
        
        public Builder name(String name) {
            this.name = name;
            return this;
        }
        
        public UserInfo build() {
            return new UserInfo(this);
        }
    }
}

// 깔끔한 메서드
public void createUser(UserInfo userInfo) {
    // ...
}

// 호출도 명확하다
UserInfo info = new UserInfo.Builder()
    .name("홍길동")
    .email("hong@example.com")
    .phone("010-1234-5678")
    .build();
    
createUser(info);

 

 


 

8. 매직 넘버와 매직 스트링이 많은 경우

문제: 의미 없는 상수값
해결: 상수 또는 Enum 사용
효과: 코드 의도 명확화, 오타 방지

 

문제 상황:

public void processUser(User user) {
    if (user.getStatus() == 1) {
        // 1이 뭐지? 활성? 비활성?
    }
    
    if (user.getAge() >= 19) {
        // 왜 19인가?
    }
    
    if (user.getType().equals("VIP")) {
        // "VIP" 오타 나면 찾기 어렵다
    }
    
    double discount = price * 0.15;
    // 0.15는 어디서 나온 숫자인가?
}

숫자와 문자열의 의미를 알 수 없다.

나중에 보면 이게 뭔지 기억이 안 난다.

 

사고 과정:

  1. 상수값에 의미를 부여한다
  2. Enum으로 타입 안정성을 확보한다
  3. 중복된 값을 한 곳에서 관리한다

해결:

// 상수 정의
public class UserConstants {
    public static final int STATUS_ACTIVE = 1;
    public static final int STATUS_INACTIVE = 0;
    public static final int ADULT_AGE = 19;
    public static final double VIP_DISCOUNT_RATE = 0.15;
}

// 또는 Enum 활용
public enum UserStatus {
    ACTIVE(1),
    INACTIVE(0);
    
    private final int code;
    
    UserStatus(int code) {
        this.code = code;
    }
    
    public int getCode() {
        return code;
    }
}

public enum UserType {
    VIP, GOLD, SILVER, NORMAL
}

// 명확한 코드
public void processUser(User user) {
    if (user.getStatus() == UserStatus.ACTIVE) {
        // 명확하다
    }
    
    if (user.getAge() >= UserConstants.ADULT_AGE) {
        // 19가 성인 기준임을 알 수 있다
    }
    
    if (user.getType() == UserType.VIP) {
        // 오타 방지, IDE 자동완성 지원
    }
    
    double discount = price * UserConstants.VIP_DISCOUNT_RATE;
    // 15% 할인임을 명확히 알 수 있다
}

 

 

 


 

리팩토링 체크리스트 (코드 품질)

  • [ ] null 체크가 3단계 이상 중첩되었나?
  • [ ] 비슷한 코드가 여러 곳에 있나? (DRY)
  • [ ] 매개변수가 4개 이상인가?
  • [ ] 매직 넘버나 매직 스트링이 있나?
  • [ ] if-else가 3단계 이상 중첩되었나?

하나라도 해당되면 리팩토링을 고려해볼 시점이다.

 

 

이전 편: 설계 원칙 위반

[spring] 언제! 리팩토링해야 할까? 실전 가이드- 설계원칙 위반 (1/3)


다음 편: 유지보수성 문제 (분기 증가, 테스트 어려움) [spring] 언제! 리팩토링해야 할까? 실전 가이드-유지보수성 문제 (파트 3/3)

'공부일기.. > Spring' 카테고리의 다른 글

[spring] 스프링 컨테이너와 스프링 빈  (1) 2025.10.15
[spring] 언제! 리팩토링해야 할까? 실전 가이드-유지보수성 문제 (파트 3/3)  (0) 2025.10.05
[spring] 언제! 리팩토링해야 할까? 실전 가이드- 설계원칙 위반 (1/3)  (0) 2025.10.03
[Spring] Spring Boot MySQL 설정 (커넥션 풀, OSIV, SQL 로깅)  (0) 2025.09.18
[Spring Boot] application.yml 설정파일 마스타~~  (0) 2025.09.14
'공부일기../Spring' 카테고리의 다른 글
  • [spring] 스프링 컨테이너와 스프링 빈
  • [spring] 언제! 리팩토링해야 할까? 실전 가이드-유지보수성 문제 (파트 3/3)
  • [spring] 언제! 리팩토링해야 할까? 실전 가이드- 설계원칙 위반 (1/3)
  • [Spring] Spring Boot MySQL 설정 (커넥션 풀, OSIV, SQL 로깅)
s0-0mzzang
s0-0mzzang
공부한것을 기록합니다...
  • s0-0mzzang
    승민이의..개발일기..🐰
    s0-0mzzang
  • 전체
    오늘
    어제
    • 전체~ (108)
      • 마음가짐..! (10)
      • 공부일기.. (76)
        • weekly-log (6)
        • Spring (19)
        • Java (18)
        • DataBase (10)
        • git (2)
        • JPA (6)
        • kafka (1)
        • Backend Architecture (3)
        • Troubleshooting (삽질..ㅋ) (2)
        • Cloud (1)
        • Docker (2)
        • 알고리즘 (1)
        • 리액트 (2)
        • Infra (3)
      • 하루일기.. (22)
        • 그림일기 (8)
        • 생각일기 (14)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

  • 인기 글

  • 태그

    StringTokenizer
    TDD
    ERD
    다짐
    JPA
    spring boot
    React
    스프링부트
    ADC 환경
    인프라 기초
    Paging
    BufferedReader
    SpringBoot
    swagger
    자바
    spring
    항해플러스
    항해99
    MySQL
    리팩토링
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
s0-0mzzang
[spring] 언제! 리팩토링해야 할까? 실전 가이드- 코드 품질 문제 (2/3)
상단으로

티스토리툴바