[인프런 워밍업 클럽 BE 2기] 백엔드 프로젝트 - 3주차 발자국
이번 주차에서는 포트폴리오 사이트 화면과 어드민 기능을 개발하며, 사이트의 삽입, 수정, 조회 API 구현과 인터셉터 설정을 완료하였다. 타임리프와 부트스트랩을 활용한 뷰 템플릿 구성, 데이터 처리, 예외 관리 등 다양한 기능을 학습하며 웹 개발의 전체적인 흐름을 이해할 수 있었다.
포트폴리오 사이트 화면 개발하기 1
RestController와 Controller의 차이점
RestController: JSON 데이터를 반환하며, 주로 RESTful API를 구현할 때 사용. HTML 뷰 템플릿을 반환하지 않음.
Controller: HTML 등 뷰 템플릿을 반환하기 위해 사용.
부트스트랩과 타임리프 프래그먼트 사용
부트스트랩 템플릿 적용: 부트스트랩 사이트에서 템플릿을 다운로드하여 프로젝트에 적용.
타임리프 프래그먼트 사용:
head
,footer
,navigation
등의 공통 영역을 분리하여 프래그먼트로 구성.th:fragment
: 프래그먼트의 이름을 지정해 원하는 곳에서 재사용.th:replace
: 프래그먼트를 뷰 파일에 포함할 때 사용.
타임리프 문법 활용
th:each
: 컨트롤러에서 전달된 모델 데이터를 반복하여 출력.th:text
: 서버 데이터를 HTML 요소에 텍스트로 삽입.target="_blank"
: 링크 클릭 시 새 탭에서 열리도록 설정.
포트폴리오 사이트 화면 개발하기 2
동적 콘텐츠와 레이아웃 처리
th:fragment
에 파라미터 전달: 프래그먼트에 파라미터를 전달해 동적 콘텐츠를 처리.th:block
사용 이유: 불필요한 태그 없이 블록을 형성해th:each
와 조건부 렌더링(th:if
)을 함께 사용하기 위함.d-inline
태그: 인라인 요소로 배치하여 레이아웃 조정을 쉽게 함.레이아웃 작업: 공통 레이아웃을
th:fragment
로 정의해, 수정 시 모든 페이지에 자동 반영되도록 구성.
인터셉터의 역할과 설정
인터셉터: 컨트롤러보다 앞단에서 동작하며, 여러 컨트롤러의 요청을 잡아 공통적인 처리를 수행.
addInterceptors
오버라이딩: WebMvcConfigurer에서 인터셉터를 등록할 때 사용.모든 경로를 대상으로 하며, 정적 자원 경로(CSS, JS 등)는 제외.
presentationInterceptor
의 HandlerInterceptor 추가:preHandle
: 컨트롤러에 도달하기 전 실행.postHandle
: 컨트롤러의 응답 후 실행되나, 예외가 발생하면 동작하지 않음.afterCompletion
: 예외 발생 여부와 상관없이 항상 실행.
HttpInterface 사용: 클라이언트 정보를 수집해 기록.
어드민 공통 기능 개발하기
클래스 생성 및 관리
admin
패키지: 컨트롤러, 데이터 처리, 예외 처리, 인터셉터, 보안 관련 클래스를 구성.
예외 처리와 로깅
Throwable: 모든 오류의 최상위 클래스.
Error: 복구할 수 없는 오류.
Exception: 애플리케이션이 복구 가능한 오류.
Checked Exception: 컴파일 시점에 예외가 검출됨.
Unchecked Exception: 런타임 시점에 발생.
ControllerAdvice: 인터셉터와 유사하게 동작하며, 예외 처리를 담당.
@ExceptionHandler
: 특정 예외를 처리하는 메소드.Logger 사용:
LoggerFactory.getLogger
를 활용해 로그를 기록.
DTO 개발
companion object: 클래스 내부에서 정적 메소드와 변수를 정의할 때 사용.
vararg: 가변 인자를 받아 여러 파라미터를 전달할 수 있도록 함.
filterings: 데이터에서 제외할 필드를 관리.
classInfo: 클래스의 메타데이터와 정보를 담음.
인터셉터 추가
postHandle 오버라이딩: 메뉴 데이터를
modelAndView
에 추가해 특정 페이지에 전달.addInterceptors 설정:
admin
경로에 인터셉터를 추가하며, 정적 자원(CSS, JS 등)은 제외.
데이터 조회 기능 개발하기
조회 기능 구현
context 패키지: 화면과 관련된 설정을 서버에서 관리.
orElseThrow
: 데이터가 없을 경우 예외를 발생.@field
: 필드 유효성 검사를 수행.@PathVariable
: 경로 변수를 통해 데이터를 전달.@RequestBody
: 요청 본문 데이터를 객체로 변환.@Validated
: 입력 데이터의 유효성을 검사.
데이터 삽입, 수정, 삭제 기능 개발하기
DTO와 데이터 처리
form
패키지: DTO 역할을 하는 클래스들을 생성해 데이터 전달에 활용.
ifPresent
사용
ifPresent: 값이 존재할 때만 특정 동작을 수행하도록 처리.
[미션4] 조회 REST API 만들기
이번 미션에서는 제품, 브랜드, 카테고리, 재고에 대한 조회 API를 개발하고, 각 경로(/api/products
, /api/brands
, /api/categories
, /api/stocks
)에 대해 테스트 코드를 작성하였다.
이번 미션에서는 조회 API 개발 및 테스트 코드 작성을 통해 RESTful API의 기본 구조를 익히고, Null 처리와 영속성 관리를 경험하였다. 특히 **?.let {}
**과 ?:
엘비스 연산자를 활용해 null-safe한 코드를 작성하는 방법을 익혔으며, 서비스 계층 분리를 통해 더 효율적인 설계를 계획하게 되었다.
ProductDTO에서 발생한 Null 처리 이슈와 해결
조회 API를 구현하는 과정에서 ProductDTO 내의 brand와 category가 null일 경우 NullPointerException이 발생하는 문제가 발견되었다. 이를 해결하기 위해 ?.let {}
구문을 사용해 null-safe하게 처리하였다.
?.let {}의 역할
?.let {}
: 객체가 null이 아닐 때만 코드 블록 내부를 실행한다.예시:
brand = product.brand?.let { BrandDTO(it) }
product.brand
가 null이 아닐 경우에만BrandDTO
를 생성하고, 그렇지 않으면 실행되지 않음.
엘비스 연산자(?:) 사용
조회 로직에서는 **엘비스 연산자 ?:
**를 사용하여, 값이 null일 경우 대체 동작을 수행하도록 하였다.
?:
연산자: 왼쪽의 값이 null이면 오른쪽 값을 반환한다.
product = stock.product ?: throw IllegalArgumentException("제품정보를 찾을 수 없습니다..")
만약
stock.product
가 null이면 IllegalArgumentException을 던지도록 처리.
영속성 설정: Cascade = PERSIST
재고와 관련된 엔티티를 영속성 컨텍스트에 포함시키기 위해, cascade = PERSIST
옵션을 사용하였다. 이 설정은 엔티티가 함께 영속화될 수 있도록 보장하며, 이를 통해 테스트 코드가 문제없이 성공적으로 실행되었다.
재고 수량 설정과 서비스 계층 분리 계획
현재 재고 테이블의 재고 수량(stockCount) 설정은 Stock 엔티티의 메서드를 통해 처리하고 있다.
하지만, 이 로직을 추후 삽입 및 수정 API에서 더 깔끔하게 관리하기 위해 서비스 계층으로 분리할 계획이다.
댓글을 작성해보세요.