1. 코드 구현 전 원활하고 구체적인 의사소통 필요
1) 개요
프로젝트 진행이 아직 미숙하여 초기에 팀원 간의 충분한 의사소통이 이루어지지 않아 프로젝트 진행 중에 기본 구조 및 코딩 규칙 등이 통일 되지 않았다. 이로 인해 코드 merge 후 여러 문제점 발생
2) 문제 상황
- 기본 구조와 의존성 정의 X
- 문제
- 팀원이 각각 다른 방식으로 프로젝트의 기본 구조를 설계
- BaseEntity를 포함한 예외 처리 방식, 의존성 추가 등에 대한 사전 합의가 부족
- 결과
- 중복된 의존성이 추가됨 (ex. spring-boot-starter-validation 중복 추가)
- 예외 처리가 전역 예외처리와 Service 계층에서 각각 처리되어 일관성 부족 및 혼란
- 문제
- 용어 정의 및 타입 불일치
- 문제
- 가게 주인과 관련된 용어를 각각 다르게 정의(Owner vs User vs Master)
- ID 타입이 통일되지 않고 일부는 int, 일부는 Long으로 정의
- 결과
- 데이터 타입 변환 및 통합 과정에서 불필요한 에러와 코드 수정 발생
- 문제
3) 문제 해결
* 기본 구조 정의
-모든 엔티티의 공통 필드를 관리하기 위해 BaseEntity 설계 및 통일
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public abstract class BaseEntity {
@CreatedDate
@Column(name = "created_at",updatable = false)
private LocalDateTime createdAt;
// 수정일
@LastModifiedDate
@Column(name = "modified_at")
private LocalDateTime modifiedAt;
@Column(name = "deleted_at")
// 삭제 시간
// soft delete 시 값을 추가
LocalDateTime deletedAt;
}
-예외 처리 방식은 @RestControllerAdvice를 통한 전역 예외 처리 방식으로 최대한 통일
-통일하기 힘든 예외는 Service 계층에 구현
* 데이터 타입 및 용어 통일
-코드 refactoring 을 통해 데이터 타입을 일치 시키고 용어 또한 Owner로 통일
4) 결론
구현 전에 기본 구조와 코딩 규칙을 명확히 정의해서 code convention, git commit conventin 등을 따로 정의하는 것이 중요하다는 것을 깨달았다. 저번 프로젝트를 경험하고 git convention은 통일을 했는데 code convention에 대한 내용은 소통하지 못했다. 이후 프로젝트에서는 시작 단계에서 조금 더 확실한 문서화를 통해 문제를 방지해야겠다.
2. 최소 주문 검증 로직
1) 개요
주문 시 최소 주문 금액을 검증하는 로직에서 BigDecimal 사용을 간과하여 계산하는 과정에서 문제가 발생
2) 문제 상황
금액 검증 로직에서 BigDecimal 대신 일반 숫자 연산을 사용, 일부 주문 금액에서 부동소수점 관련 계산 오류가 발생
if (price * quantity < shop.getMinimumPrice()) {
throw new IllegalArgumentException("최소 주문 금액을 만족하지 못했습니다.");
}
부정확한 검증으로 인해 실제로는 최소 주문 금액을 만족했지만 주문이 되지 않는 경우 발생 가능성이 있어보임
3) 문제 해결
BigDecimal 기반으로 로직을 수정
//주문 금액 계산
BigDecimal totalPrice = menu.getPrice().multiply(BigDecimal.valueOf(requestDto.getQuantity()));
//최소 주문 금액 검증
//BigDecimal로 처리
if(totalPrice.compareTo(BigDecimal.valueOf(shop.getMinimumPrice())) < 0){
throw new IllegalArgumentException("최소 주문 금액을 만족하지 못했습니다.");
}
BigDecimal 에서 사용할 수 있는 메서드인 multiply, compareTo 를 사용하여 계산 로직 구현
계산의 정확성을 보장
4) 결론
금액과 같은 데이터를 다룰때에는 어떠한 타입을 쓰는지 잘 확인해야하고, 정확성을 위해 BigDecimal을 사용하는 것이 일반적이라는 것을 알게 되었다. 그리고 BigDecimal은 일반 계산과 달리 메서드를 사용하여 계산 로직을 구현해야 한다는 것을 알게 되었다.
<BigDecimal 메서드>
*기본 연산
- add(BigDecimal augend) : 두 값의 합
- subtract(BigDecimal subtrahend) : 두 값의 차
- multiply(BigDecimal multiplicand) : 두 값의 곱
- divide(BigDecimal divisor, RoundingMode roundingMode) : 두 수의 나눗셈, 소수점을 반올림하거나 버림
- RoundingMode를 지정해야 무한 소수 오류 방지
BigDecimal a = new BigDecimal("10.50");
BigDecimal b = new BigDecimal("2.30");
BigDecimal result = a.add(b); // 12.80
BigDecimal a = new BigDecimal("10.50");
BigDecimal b = new BigDecimal("2.30");
BigDecimal result = a.subtract(b); // 8.20
BigDecimal a = new BigDecimal("10.50");
BigDecimal b = new BigDecimal("2");
BigDecimal result = a.multiply(b); // 21.00
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP); // 3.33 (소수점 둘째 자리 반올림)
*비교 연산
- compareTo(BigDecimal val) : 두 값을 비교
- -1 : 호출한 객체가 작음
- 0 : 두 값이 같음
- 1 : 호출한 객체가 큼
BigDecimal a = new BigDecimal("10.50");
BigDecimal b = new BigDecimal("10.00");
System.out.println(a.compareTo(b)); // 1
System.out.println(b.compareTo(a)); // -1
System.out.println(b.compareTo(b)); // 0
*생성 및 초기화 관련 메서드
- BigDecimal.valueOf(double val)
- double 값을 BigDecimal로 변환
- stripTrailingZeros()
- 불필요한 소수점 0을 제거
BigDecimal a = BigDecimal.valueOf(10.50); // 올바른 방식
BigDecimal a = new BigDecimal("10.5000");
BigDecimal result = a.stripTrailingZeros(); // 10.5
3. 상수 사용 문제
1) 개요
Const 클래스에 상수를 정의해두었으나 의미를 제대로 파악하지 못하여, 이를 활용하지 않고 문자열을 직접 사용하는 방식으로 구현함
2) 문제 상황
Const 클래스에 정의된 상수의 의미를 이해하지 못해서 문자열을 직접 사용하는 방식으로 구현
package com.example.whattheeat.constant;
public abstract class Const {
public static final String LOGIN_USER = "loginUser";
public static final String AUTHENTICATION = "authentication";
}
코드의 유지보수성이 떨어지고 오타가 발생할 가능성이 큼 -> 비효율적
3) 문제 해결
문자열을 직접 사용한 모든 코드를 Const 상수로 대체
//문자열 사용 코드
public ResponseEntity<OrderResponseDto> createOrder(
@SessionAttribute("authenticatedUserId") Long customerId,
@SessionAttribute("authenticatedUserRole") String role,
@RequestBody @Valid OrderRequestDto requestDto)
//상수 사용 코드
public ResponseEntity<OrderResponseDto> createOrder(
@SessionAttribute(name = Const.LOGIN_USER) Long customerId,
@SessionAttribute(name = Const.AUTHENTICATION) String role,
@RequestBody @Valid OrderRequestDto requestDto)
4) 결론
상수를 활용함으로써 코드의 일관성과 유지보수성을 높이고 실수를 방지할 수 있다는 것을 배웠다.
<Const 클래스의 역할>
1. 상수의 중앙 관리
- 반복적으로 사용되는 값을 한 곳에서 관리하여, 중복과 실수를 방지
- 세션 키, 권한 값, URL 경로 등
public abstract class Const {
public static final String LOGIN_USER = "authenticatedUserId";
public static final String AUTHENTICATION = "authenticatedUserRole";
}
2. 가독성 향상
- 하드코딩된 문자열이나 숫자를 사용하지 않고 의미 있는 변수명을 사용하므로 코드 가독성을 높임
3. 유지보수성 개선
- 상수가 변경되더라도 해당 값을 한 곳에서 수정하면 되므로 유지 보수가 쉬워짐
- 세션 키 변경 시 Const.LOGIN_USER만 수정하면 전체 코드에 반영됨
'내배캠 > 프로젝트, 개인과제 트러블슈팅' 카테고리의 다른 글
[내일배움캠프/백엔드] JPA 심화 개인과제 트러블 슈팅 (1) | 2024.12.19 |
---|---|
[내일배움캠프/백엔드] Spring 심화 개인과제 트러블 슈팅 (1) | 2024.11.29 |
[내일배움캠프/백엔드] Spring 숙련. 뉴스 피드 프로젝트 트러블 슈팅 (1) | 2024.11.22 |
[내일배움캠프/백엔드] Spring 숙련 개인과제 일정 관리 앱 develop 트러블 슈팅 (0) | 2024.11.15 |
[내일배움캠프/백엔드] Spring 입문 개인과제 일정 관리 앱 만들기 트러블슈팅 (0) | 2024.11.08 |