블로그
전체 2#카테고리
- 백엔드
#태그
- 인프런워밍업클럽
- 발자국
2025. 03. 16.
0
[인프런 워밍업 클럽 3기 - BE] 2주차 발자국
해당 블로그의 발자국은 정보근님의 입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기 강의 기반으로 작성하였습니다. 2주차 강의 내용 요약 Spring Data JPA Repositories스프링에서 제공하는 Spring Data JPA를 사용하면 인터페이스 상속만으로 기본적인 CRUD 기능 사용 가능 테스트 - 어노테이션@DataJpaTest : JPA 관련 테스트를 위한 설정 제공. 특히 JPA 엔티티, 리포지토리, 데이터베이스 컴포넌트를 테스트하기에 적합. 테스트 실행 시, 내장 데이터베이스를 설정하고 @Entity 및 @Repository 어노테이션이 부여된 클래스들을 검색하여 테스트 환경 설정.@TestInstance : 테스트 인스턴스의 라이프사이클 지정. 기본적으로 JUnit5는 각 @Test 메소드마다 새로운 테스트 인스턴스를 생성하지만 본 프로젝트에서는 @BeforeAll을 사용하여 모든 테스트 메소드에 공통적으로 적용될 작업을 한 번만 수행할 것이기 때문에 라이프사이클을 클래스 단위로 지정.@BeforeAll : 테스트 클래스 내의 모든 @Test 메소드 실행 전에 한 번 실행.@Autowired : 필드 주입 방식으로 의존성을 주입할 때 사용.@DisplayName : 테스트 클래스나 메소드의 이름 정의. 테스트 결과에서 각 메소드를 쉽고 빠르게 인지하기 위해 사용. FetchType엔티티에서 연관관계 매핑 시 설정 가능한 FetchType은 Eager와 Lazy가 있다. Lazy를 적용한다면 사용하지 않을 자식 데이터의 조회를 방지할 수 있다. 연관관계에 따라 기본값이 다르기 때문에 항상 fetch=FetchType.LAZY로 명시해주는 것이 좋다. Fetch JoinJPA에 의존하지 않고 직접 JPQL 쿼리를 보낸다. Join을 활용해 한번에 부모와 자식 데이터를 조회할 수 있다. 하지만 OneToMany, 또는 ManyToMany 관계의 자식 엔티티가 여러 개일 경우, 하나만 조인할 수 있다는 한계가 있다. Batch Fetch SizeIN 절을 사용해 여러 건의 데이터 한번에 조회. 1+N의 쿼리가 1+(N/batch_fetch_size) 정도의 수준으로 줄어든다. 하지만 DBMS에 따라 IN 절의 파라미터 개수 제한이 있기도 하고, 한 번에 많은 데이터를 불러오는 것은 애플리케이션이나 데이터베이스에 부담을 줄 수 있기 때문에 적절한 개수 설정이 필요. 클래스 생성 - 어노테이션@Controller : 컴포넌트 스캔의 대상이 되어 빈으로 등록. 컨트롤러 레이어에 해당함을 명시. SSR(서버 사이드 렌더링) 방식으로 웹 개발할 때 사용. 스프링 내부적으로 return된 문자열과 같은 이름을 갖는 html 파일을 찾아 클라이언트에게 응답. 파일의 경로나 확장자는 별도 옵션으로 지정가능@RestController : 컴포넌트 스캔의 대상이 되어 빈으로 등록. 컨트롤러 레이어에 해당함을 명시. CSR(클라이언트 사이드 렌더링) 방식으로 웹 개발을 하거나, 데이터의 처리만을 담당하는 API를 개발할 때 사용. return 값은 HTTP 응답 메시지의 Body에 들어간다. String 외의 리턴 타입을 정하면 HTTP 헤더 값 등 설정에 따라 JSON 등으로 변환하여 Body에 넣는다.@RequestMapping : HTTP 요청을 정의하는 역할. 클래스와 메소드에 붙일 수 있다. 이 때 클래스에 붙이면, 정의한 경로가 클래스 내부의 모든 메소드에 공통적으로 붙게 된다. PresentationApiController.test()는 “/api/test”의 경로를 가질 것이고, PresentationViewController.test()는 “/test”의 경로를 갖게 된다.@GetMapping : HTTP 프로토콜은 여러 메소드를 갖고 있는데 대표적으로 GET, POST, PUT, DELETE가 있다. @GetMapping 어노테이션은 @RequestMapping(method =[RequestMethod.GET])와 같다. “/api/test” 경로로 GET 요청을 했을 때 PortfolioApiController.test()가 호출. 만약 같은 경로로 POST 요청을 할 경우는 HTTP 상태 코드 중 405 Method Not Allowed 응답을 준다. controller 패키지레이어드 아키텍처에서 컨트롤러란 사용자의 요청이 진입하는 Entry Point 의미. 요청을 받아서 실질적으로 처리하는 역할을 하는 서비스 레이어로 넘겨주고, 서비스 레이어에서 처리한 결과를 응답하는 역할. 클라이언트와의 인터페이스만을 담당하기 때문에, 처리 로직 변경없이 API 명세만을 바꿀 경우 서비스는 그대로 두고 컨트롤러만 새로 만들면 클라이언트와의 인터페이스를 변경할 수 있다. 서비스 개발 - 어노테이션@Transactional : 트랜잭션을 간편하게 열고 닫을 수 있게 해준다.rollbackFor: 어떤 예외가 발생했을 때 롤백할지 정의readOnly: 읽기 전용 트랜잭션으로 설정. JPA를 사용할 경우 더티체킹 등을 수행하지 않게 해준다.isolation: 트랜잭션 고립 수준 정의 서비스 테스트 - 어노테이션@ExtendWith : JUnit 5에서 테스트 확장을 지원하는 어노테이션@InjectMocks : Mockito에서 테스트 대상이 되는 클래스에 인스턴스 주입@Mock : Mockito에서 Mock 객체를 생성할 때 사용 컨트롤러 테스트 - 어노테이션@SpringBootTest : Spring Boot 애플리케이션을 테스트하는 데 사용. 실제 애플리케이션과 유사한 환경을 구성해 테스트 실행 가능@AutoConfigureMockMVC : Spring MVC를 모의로 테스트하는 데 사용. MockMVC 객체가 자동으로 구성되어 컨트롤러를 모의로 테스트할 수 있다. 내부적으로 Spring Boot Test의 일부로 제공되며, MockMVC를 구성하고 테스트 환경을 적절하게 설정.+) Interceptor : 컨트롤러보다 앞단에서 동작하여 여러 컨트롤러의 요청을 공통적으로 처리하는 기능 회고개인적으로 바쁜 한주여서 강의를 듣는 것조차 좀 벅찼던 것 같다. 그렇지만 어쨌든 다 듣기는 했다!! 발자국을 쓰면서 다시 한번 개념을 정리하는 시간을 가질 수 있었다.그리고 역시 테스트 부분이 늘 어렵다. 강의를 들으며 클론코딩만 해보았는데 미션을 통해 스스로 테스트 코드를 짜는 연습을 반복해봐야 할 것 같다. 미션미션 3 ) REST API 설계하기미션2에서 완성한 사용자, 도서, 독서기록, 리뷰 테이블 별로 REST API를 설계해보았는데 솔직히 잘한건지 모르겠다. 간단한 CRUD를 설계하면 되는거라 크게 어렵지는 않았는데 직접 api를 개발해보아야 감이 잡힐 것 같다. 다음 미션도 곧바로 해봐야겠다.
백엔드
・
인프런워밍업클럽
2025. 03. 09.
0
[인프런 워밍업 클럽 3기 - BE] 1주차 발자국
해당 블로그의 발자국은 정보근님의 입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기 강의 기반으로 작성하였습니다.모르거나 헷갈리는 내용만 정리! 1주차 이론웹 프레임워크동적 웹서비스 개발을 편리하게 만들어주는 도구. 웹 개발이 건축이라면, 프레임워크는 건축을 돕는 여러 도구. 프레임워크와 라이브러리를 구분하는 키는 제어의 주도권. 비유해보자면, 프레임워크는 DIY 가구 키트이고 라이브러리는 가구를 만들기 위한 공구들을 모아둔 공구 상자라 할 수 있다. 라이브러리는 사용자가 주도권을 가지고 원하는 것을 만들 수 있다. 프레임워크 안에서도 라이브러리를 사용할 수 있다. 레이어드 아키텍처 (Controller-Service-Repository)가장 대중적인 소프트웨어 아키텍처. 기능별로 아래와 같은 세가지 계층으로 구분된다.Presentation (Controller) : 클라이언트가 요청할 수 있는 인터페이스 정의. 전달받은 데이터를 검증해 Service의 메소드 호출Business (Service) : 목적에 맞게 데이터 처리. 레포지토리의 메소드를 호출해 저장, 수정, 조회, 삭제 수행Data Access (Repository) : 데이터베이스에 접근해 작업 요청 HTTP 상태 코드응답에서 요청의 처리 결과를 표현하는 코드200: OK201: Created202: Accepted300: Multiple Choices400: Bad Request401: Unauthorized403: Forbidden404: Not Found405: Method Not Allowed415: Unsupported Media Type500: Internal Server Error502: Bad Gateway503: Service Unavailable504: Gateway Timeout REST APIHTTP 통신으로 동작하는 어플리케이션 기능을 정의하는 일종의 규칙, 컨벤션. HTTP 규약에는 강제성이 없다.ex) POST /members 클라이언트에서 서버로 데이터를 전달하는 방법Query Parameter, HTTP Request Body (@RequestBody), Path Variable JPA란JPA는 Java Persistence API의 약어로, 자바 ORM 기술의 표준 인터페이스 의미 ORMObject Relational Mapping의 약어로, 객체 관계 매핑 의미. 객체지향 프로그래밍의 인스턴스와 관계형 데이터베이스를 매핑해주는 기술. 구체적인 DBMS에 대한 의존성이 줄어든다는 장점이 있지만 충분한 학습이 필요하고 모든 기능을 구현하기에 한계가 있다는 단점이 있다. 실습repository 패키지스프링에서 리포지토리는 데이터베이스와 직접적으로 상호작용하는 레이어. 데이터베이스에 쿼리를 보내는 리포지토리 레이어를 만들고 서비스 레이어에서 리포지토리 레이어를 참좨 다양한 기능을 개발할 수 있도록 분리Spring Data JPA를 사용하면 기본적인 리포지토리 개발이 매우 쉽다. 스프링이 실행되면 직접 기본적인 CRUD 코드를 만들어주기 때문. 물론 기본적인 기능 외에도 커스텀 메소드를 정의할 수도 있다. 엔티티 개발 - 연관관계 있을 때 어노테이션@OneToMany : 일대다 관계 정의 | 한 엔티티가 여러 다른 엔티티와 관계를 맺는 경우 사용@ManyToOne : 다대일 관계 정의 | 여러 엔티티가 한 엔티티와 관계를 맺는 경우 사용package com.yongback.portfolio.domain.entity import jakarta.persistence.* @Entity class Experience( title: String, description: String, startYear: Int, startMonth: Int, endYear: Int?, endMonth: Int?, isActive: Boolean ) : BaseEntity() { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "experience_id") var id: Long? = null var title: String = title var description: String = description var startYear: Int? = startYear var startMonth: Int? = startMonth var endYear: Int? = endYear var endMonth: Int? = endMonth var isActive: Boolean = isActive @OneToMany(targetEntity = ExperienceDetail::class, fetch = FetchType.LAZY, cascade = [CascadeType.ALL]) @JoinColumn(name = "experience_id") var details: MutableList = mutableListOf() fun getEndYearMonth(): String { if (endYear == null || endMonth == null) { return "Present" } return "${endYear}.${endMonth}" } fun update( title: String, description: String, startYear: Int, startMonth: Int, endYear: Int?, endMonth: Int?, isActive: Boolean ) { this.title = title this.description = description this.startYear = startYear this.startMonth = startMonth this.endYear = endYear this.endMonth = endMonth this.isActive = isActive } fun addDetails(details: MutableList?) { if (details != null) { this.details.addAll(details) } } } Experience는 ExperienceDetail과 1:N의 관계를 가진다. 경력이 하나 있으면 그 경력에 대응하는 상세내용을 여러 개 작성할 수 있는 구조이다. @OneToMany로 Experience 엔티티가 일대다 관계에서 1에 해당한다고 선언하고 N의 대상은 ExperienceDetail임을 targetEntity 파라미터로 지정해준다. fetch는 데이터를 가져오는 전략을 의미하는데, EAGER는 데이터베이스에서 Experience를 가져오는 동시에 ExperienceDetail을 모두 가져오고 LAZY는 실제로 ExperienceDetail을 호출할 때 데이터베이스에서 가져온다. EAGER는 경우에 따라 성능에 치명적인 영향을 가져올 수 있기 때문에 항상 FetchType을 LAZY로 선언해주는 것이 좋다. cascade는 JPA 영속성 컨텍스트에서 엔티티 상태에 변화가 생겼을 때, 매핑된 엔티티를 어떻게 처리할지를 결정한다. 회고이번주에는 학교에서 배웠던 내용이 많이 겹쳐서 복습한다는 느낌으로 강의를 수강하였다. 까먹었던 내용들도 강의를 들으면서 상기시킬 수 있었다. Kotlin을 처음 써봐서 아직 어색한데, 익숙해질 수 있도록 다음주까지 많이 연습을 해야 할 것 같다. 미션이번주 미션은 과제 깃허브 리포지토리를 만들고, 미니 프로젝트를 위한 1:N 테이블을 설계하는 것이다. 내가 기획한 미니 프로젝트는 사용자가 독서 기록을 관리하고, 책을 읽은 후 평점과 리뷰를 남길 수 있는 독서 기록 관리 시스템이다. 배운 개념을 그대로 적용하면 되고, 몇년 전에도 과제로 해봤던 내용이라 크게 어렵지 않았다.
백엔드
・
인프런워밍업클럽
・
발자국