코드 품질 문제
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가 터진다.
사고 과정:
- null 체크 반복 → Null Object 패턴 or Optional 활용
- null 대신 기본값 객체 반환
- 방어적 복사보다는 설계로 해결
해결 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곳을 다 수정해야 한다.
사고 과정:
- 중복 코드 발견 → DRY(Don't Repeat Yourself) 원칙 위반
- 공통 로직 추출
- 재사용 가능한 컴포넌트로 만들기
해결:
// 검증 유틸리티
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개다. 순서를 바꿔서 넣어도 컴파일 에러가 안 난다.
실수하기 딱 좋은 구조다.
사고 과정:
- 관련된 데이터를 객체로 묶는다
- 매개변수 순서 실수를 방지한다
- 나중에 필드 추가가 쉬워진다
해결:
// 사용자 정보 객체
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는 어디서 나온 숫자인가?
}
숫자와 문자열의 의미를 알 수 없다.
나중에 보면 이게 뭔지 기억이 안 난다.
사고 과정:
- 상수값에 의미를 부여한다
- Enum으로 타입 안정성을 확보한다
- 중복된 값을 한 곳에서 관리한다
해결:
// 상수 정의
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 |