반응형

 

이번 2주차 발자국 내용은 spring 테스트 코드에 대한 내용이 대부분이다.

아무래도 Spring에서 가장 중요한 요소는 테스트 코드라고 생각되어 강의를 들으면서 썼던 TEST 코드에 대해 하나하나씩 파헤쳐 볼 예정이다 !


package com.hyup.portfolio.domain.repository
	
	import com.hyup.portfolio.domain.entity.Project
	import org.springframework.data.jpa.repository.JpaRepository
	import java.util.Optional
	
	interface ProjectRepository : JpaRepository<Project, Long> {
	    fun findAllByIsActive(isActive: Boolean): List<Project>
	
	    override fun findById(id: Long): Optional<Project>
}

 

위 코드에서 궁금한 내용은 ?

[ QUESTION ] findById를 왜 override를 해서 사용하는 이유

 

-> JpaRepository가 이미 제공하는 메서드를 다시 선언하기 때문에 오버라이드를 해야함
-> override를 활용해서 재정의 하면 기능적으로 안좋다.

[ WHY ?] override를 활용해서 재정의 하면 기능적으로 안좋은 이유


1. 기본 동작의 일관성 문제

  • JpaRepository에서 제공하는 findById는 기본적인 CRUD 동작을 안정적이고 효율적으로 처리하는데,
    이를 재정의하면 예상치 못한 문제를 발생시킬 수 있음.
  • 다른 개발자들이 코드를 이해할 때, 어려움을 안겨줄 수 있음


2. 유지보수성 저하

  • JPA의 업데이트나 변경 사항이 발생했을 때 호환성 문제가 발생할 가능성이 높음
  1. 기능적 안정성 감소
    • Spring Data Jpa는 트랜잭션 관리, 데이터베이스 연결 관리, 성능 최적화 등을 내부적으로 처리. 이를 재정의 하면 내부적으로 처리되던 여러 최적화나 기능이 무시될 수 있음
  2.  

 

[ SOLVE ] 그럼 override를 활용하지 않고 어떤 방법을 사용할 수 있나?

  1. CUSTOM Repository 사용
  2. interface ProjectRepository : JpaRepository<Project, Long> { fun findAllByIsActive(isActive: Boolean): List<Project> override fun findById(id: Long): Optional<Project> }
interface CustomProjectRepository {
    fun findByIdCustom(id: Long): Optional<Project>
}

 

  1. 쿼리 메서드 활용
  2. interface ProjectRepository : JpaRepository<Project, Long> { fun findAllByIsActive(isActive: Boolean): List<Project> fun findByIdAndIsActive(id: Long, isActive: Boolean): Optional<Project> }

@DataJpaTest 메서드

@DataJpaTest 메서드를 타고 들어가면 @Transactional 이라는 어노테이션 존재

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(DataJpaTestContextBootstrapper.class)
@ExtendWith(SpringExtension.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(DataJpaTypeExcludeFilter.class)
@Transactional
@AutoConfigureCache
@AutoConfigureDataJpa
@AutoConfigureTestDatabase
@AutoConfigureTestEntityManager
@ImportAutoConfiguration
public @interface 
# DataJpaTest 어노테이션이 포함하고 있는 어노테이션 

이중 @Transactional 이라는 어노테이션은 테스트 메소드 하나를 하나의 트랜잭션으로 보고 메소드가 종료될 때 그 트랜잭션에서 발생한 작업들을 모두 롤백해줌

 

[ QUESTION ] 트랜잭션에서 발생한 작업들을 모두롤백해야 하는 이유

[ ANSWER ]

  1. 테스트 간 독립성 보장

테스트는 독립적으로 수행되어야 하고, 하나의 테스트가 다른 테스트에 영향을 주지 않아야 함. 트랜잭션을 롤백하면, 테스트 내에서 발생한 모든 데이터 변경이 원래 상태로 돌아가기 때문에 데이터베이스 상태가 항상 초기 상태로 유지

  1. 데이터 일관성 유지

테스트가 끝난 후에도 트랜잭션을 롤백하지 않으면, 테스트 중에 삽입되거나 수정된 데이터가 데이터베이스에 남아 있게 된다. 이렇게 되면 이후의 테스트나 실제 어플리케이션 실행에 영향을 미쳐, 데이터의 일관성이 깨질 수 있음

  1. 테스트 성능 향상

테스트 중 데이터베이스에 많은 데이터를 삽입하거나 수정하는 경우, 이를 실제로 영구 저장하는 것보다 트랜잭션을 롤백하는 것이 성능 면에서 유리.


인스턴스 생명주기

  1. PER_METHOD (기본값)
  • 각 테스트 메서드마다 새로운 인스턴스가 생성
  • 각 테스트 메서드는 독립적인 상태 유지
  • 테스트마다 새로운 인스턴스가 생성되므로, 테스트 클래스의 상태를 공유할 수 없고 테스트 간의 의존성도 없어야 함.
@TestInstance(TestInstance.Lifecycle.PER_METHOD) // 생략 가능 (기본 값)
class ExampleTest {
    
    @BeforeEach
    void setUp() {
        // 테스트 메서드 실행 전 호출됨
    }
    
    @Test
    void testA() {
        // testA 실행 시 새로운 인스턴스가 생성됨
    }
    
    @Test
    void testB() {
        // testB 실행 시 또 다른 새로운 인스턴스가 생성됨
    }
}

PER_METHOD는 각 테스트마다 독립적으로 실행되어서 서로의 상태에 영향을 주지 않지만 상태를 공유하는 것이 불가능하다.

 

2. PER_CLASS

  • 하나의 인스턴스만 생성되고, 모든 테스트 메서드에서 이 인스턴스가 재사용된다.
  • @BeforeAll을 사용하면 클래스 전체에서 한 번만 실행되므로 성능을 향상시킬 수 있음
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ExampleTest {

    @BeforeAll
    void initAll() {
        // 모든 테스트 메서드 실행 전 한 번만 호출됨
    }

    @Test
    void testA() {
        // testA 실행 시 동일한 인스턴스가 사용됨
    }

    @Test
    void testB() {
        // testB 실행 시에도 동일한 인스턴스가 사용됨
    }
}

PER_CLASS는 상태를 공유할 수 있어 성능이 향상되지만 테스트 간 인스턴스 상태가 공유되므로, 테스트 간 의존성이 발생함.


@BeforeAll, @AfterAll

  • @BeforeAll : PER_CLASS에서만 사용되며, 모든 테스트 전에 한 번만 실행
  • @AfterAll : PER_METHOD와 PER_CLASS 모두에 사용되고 모든 테스트 전에 한 번만 실행 됨

[QUESTION] @BeforeAll 이 PER_CLASS에서만 사용되는 이유, @AfterAll은 PER_METHOD, PER_CLASS 모두 사용가능한 이유

[ANSWER]

@BeforeAll은 클래스 수준에서 한 번만 실행되는 메서드. 즉 테스트 클래스의 모든 테스트 메서드가 실행되기 전에 딱 한 번 실행됨

PER_CLASS에서는 테스트 클래스에 대해 하나의 인스턴스만 생성되므로, @BeforeAll 메서드가 클래스 인스턴스에서 한 번 실행되고, 이후의 모든 테스트가 동일한 인스턴스를 사용

@AfterAll 은 모든 테스트 메서드가 실행된 후에 한 번만 실행되는 메서드이다. 여기서 PER_CLASS에서는 클래스 인스턴스가 하나만 생성되므로, 모든 테스트가 끝나고 한번만 호출되면 된다.

PER_METHOD에서는 각 테스트마다 새로운 인스턴스가 생성되지만, @AfterAll이 테스트 메서드가 모두 끝난 후에 한 번 호출되도록 관리할 수 있다.


미션 - REST API 설계

 

이번 미션에서는 REST API 설계하는 미션이 주어졌다.

 

HTTP 주요 메서드 정리 (GET / POST / PUT / PATCH / DELETE)

  • GET : 리소스 조회
  • POST : 요청 데이터 처리, 주로 등록에 사용
  • PUT : 리소스를 대체, 즉 덮어쓰기 수행
  • PATCH : 리소스 부분 변경
  • DELETE : 리소스 삭제


이번 미션과 발자국을 하면서 백엔드 개발자가 HTTP 주요 메서드에 대해 집중해야 된다는 것을 알 수 있었다. api를 설계할려면 어쩔수 없이 각 HTTP 메서드에 대해 알아야 URL을 설정할 때 어떻게 만들어야 되는지를 알 수 있기 때문이다. 이번주 는 개인 일정이 너무 많아서 아쉽게도 진도를 다 따라가지 못했다. 발자국은 만들어야 되기 때문에 여기서 내가 가장 공부해볼만한 것이 TEST 코드에 대해서 조사를 하는 것이기 때문에 열심히 하였다.

GET, POST, PUT, PATCH, DELETE 메서드에 대해서 조금이라도 공부할 수 있어서 좋았고, 따로 공부해서 블로그에 올려야 겠다고 생각하였다!

반응형
반응형

 

 

발자국 내용은 딱히 형식이 정해져 있지 않아서 발자국 쓰기 전날까지 들었던 강의들 중, 모르는 내용들을 적어서 내가 다시 봤을 때, "아 맞다!" 라는 말이 나올 정도로만 적을 예정이다!


 

목차

  1. 모르는 내용 정리
  2. 미션 해결 과정
  3. 회고

 


1. 모르는 내용 정리

 

<Gradle>

Gradle : Gradle은 Groovy를 기반한 빌드 관리 도구.

  • 빌드 관리 도구 : 프로젝트 내에서 다양하게 외부 라이브러리와 로컬 레포지토리에서도 라이브러리 별로 버전 관리를 해야할 때 용이
  • Gradle이 왜 필요할까?
    • Gradle
      • groovy 언어를 사용한 Domain-specific-language를 사용 ( 코드가 간결)
      • 어느 task가 업데이트 되었는지 체크
      • 이미 반영된 빌드 부분은 더이상 재 실행되지 않는다. -> 빌드 시간 단축
    • Maven
      • java8용 프로젝트 관리 도구 apache의 ant 대안
      • 외부 저장소에서 필요한 라이브러리, 플러그인들을 다운로드 한 후 , 로컬 시스템의 캐시에 모두 저장
    • 그래서 왜 Gradle인데!!!
      • Gradle은 작업 의존성을 그래프, Maven은 고정적이고 선형적 모델을 기반
      • Gralde은 어떤 task가 업데이트되었고 안되었는지를 체크
      • Gradle은 이미 업데이트 된 task에 대해서는 작업이 실행되지 않으므로 빌드 시간 단축
      • 빌드 설정 규모가 작으면 큰 차이를 느끼지 못하지만 규모가 크면 훨씬 Gradle의 빌드 시간이 단축된다는걸 알 수 있다.
  • Gradle과 Maven 차이를 알면 Gradle이 왜 필요한지 알 수 있다.

<Dependencies>

  • Spring Web : 웹 어플리케이션을 개발하기 위한 핵심 기능 제공
  • Thymeleaf : View Template, 동적으로 화면을 구성할 수 있게 해줌
  • Spring Data JPA : JPA를 이요한 구현체를 더 추상화시켜 더 쉽고 간편하게 JPA를 이용 가능
  • mysql driver : mysql를 연동할 때 필요한 dependency
  • h2 database : RDBMS, 테스트 단계 또는 작은 규모의 프로젝트에서 사용
  • validation : 데이터에 대한 유효성 검증 처리를 수행
  • Spring Security : 홈페이지에 인증 및 권한 기능을 빠르게 부여해 인증 및 권한 보호 기능을 손쉽게 추가할 수 있음.

 

<프로그램이 시작되는 시작되는 코드>

src/main/kotlin/PortfolioApplicatioin.kt

fun main(args: Array<String>) {
    runApplication<PortfolioApplication>(*args)
}

Whitelabel Error Page 오류가 뜨는 이유 : 어플리케이션이 뭘 해야 할지 모르기 때문에 Whitelabel Error Page를 보내줌.(정상)

 

<Git 용어>

Git : 분산 버전 관리 시스템 ⇒ 협업을 쉽게 해주는 툴 : GitHub

Commit : git에 저장하는 단계

Rollback : 이력 되돌리기

Branch : branch 생성 및 제거, 확인등의 기능을 하는 명령어

Merge : 브랜치 병합

Conflict : 충돌

Repository : 원격 저장소 , 인터넷이나 네트워크 어딘가에 있는 저장소

Push : 로컬 브랜치를 원격 저장소로 푸시할 때 사용하는 기본 명령어

 

<Git 명령어>

  • git init : git 공간으로 초기화
  • git status 명령어 입력시 Untracked files 라는 게 있는데 이건 git에서 따로 설정을 안한다는 소리
  • https://www.toptal.com/developers/gitignore/ : gitignore 파일을 자동으로 만들어줌

 

<github에 PUSH 방법>

  1. 터미널에서 git init
  2. git remote “git 저장소”
  3. → 인텔리제이(GUI)로 할 시에는 git → Manage Remote
  4. commit
  5. push

 

<application-default.yml , application-docker.yml>

profile이 default 이면, application-default.yml 파일에서 환경변수를 가져오고

profile이 docker 이면, application-docker.yml 파일에서 환경변수를 가져온다.

 

application-default.yml 파일에서 spring:jpa:hibernate:ddl-auto:create 인데

왜 application-docker.yml 파일에서는 spring:jpa:hibernamte:ddl-auto:none 일까?

=> application-default.yml 에서는 개발 목적으로 데이터베이스 스키마를 매번 재생성할 필요가 있지만, application-docker.yml 에서는 베포 및 운영 환경에 적합하게 데이터베이스 구조가 변경되지 않도록 하기 위한 설정

 

<어노테이션 정리>

@Id : 해당 테이블의 PK 필드를 의미

@Entity : JPA를 사용해 테이블과 매핑할 클래스에 붙여주는 어노테이션

@GeneratedValue : JPA에서 Entity의 PK를 생성하여 주는 기능

-> @GeneratedValue(name="member_id") : PK로 사용될 Entity의 프로퍼티에 @Id 어노테이션 선언

-> @GeneratedValue(strategy=GenerationType.IDENTITY) : 기본 키 생성을 데이터베이스에 위임한다.

-> @GeneratedValue(strategy=GenerationType.AUTO) : 기본값으로 DB 방언에 맞춰 자동으로 설정하여 준다.

@Column : 객체 필드와 DB 테이블 칼럼을 맵핑한다.

@Component : 클래스를 자동으로 빈으로 등록하기 위해 클래스 레벨에서 사용

@Profile : 빈이나, 컴퓨넌트에게 프로필을 정해줄 수 있음

@PostConstruct : 객체의 생성이 일어난 직 후에 초기화를 수행하는 메서드에 부착한다.

@ManyToOne : 단방향 관계이고 FK관리에 있어서 가장 자연스럽다. @JoinColumn 어노테이션과 같이 쓰인다.

@JoinColumn : 엔티티 테이블에 FK 칼럼을 정의해준다.

 

자료형뒤에 ? 는 무슨 의미일까 ?

ex ) Long?

자료형 뒤에 ? 가 오는 것은 Kotlin에서 사용되는 문법이고 ?를 자료형 뒤에 붙이는 방식은 nullable 여부를 나타낸다. 예를 들면 var name: String? = null 이 코드는 해당 변수 name에 null 값이 올 수도 있다는 뜻이다.

 

 

<코드 분석>

  • var type: SkillType = SkillType.valueOf(type) : SkillType.valueOf(type) 은 type이라는 문자열을 SkillType 열거형의 값으로 변환. 만약 type이 열거형에 없는 값이면 예외 발생
  • var cookies: String? = httpServletRequest.cookies ?.map {"${it.name}:${it.value}"} ?.toString() : httpServletRequest에서 쿠키 정보를 가져오고, 쿠키 이름과 값을 "이름:값" 형식으로 변환하는 코드
  • var referer: String? = httpServletRequest.getHeader("referer") : HTTP 요청에서 "Referer" 헤더 값을 가져온다. 이 코드는 사용자가 어떤 페이지에서 현재 페이지로 이동했는지 나타냄
  • @OneToManyvar details: MutableList<ExperienceDetail> = mutableListOf() : JPA 관계를 나타내고, 엔티티 간의 일대다(One-to-Many) 관계를 나타냄


2. 미션

미션 1과 2를 제출하는건데, 미션1은 테이블 설계하기와 깃허브 리포지토리에 프로젝트 올리기이다.

 

미션 1 : https://github.com/HyupTech/LMS/commit/fa47b404d36b3ce418f16213e3bb30ca96b812ed

미션 2 : https://github.com/HyupTech/LMS/commit/0993897036a0e17e7a366031b950235edd5d506e

  


 

3. 회고

발자국을 작성하면서 나는 이제까지 강의를 보면서 공부를 했지만 다시 한번 이렇게 정리를 해가면서 강의를 보지 않았다. 왜냐하면 시간이 너무 아까웠고, 차라리 정리하는 시간에 강의를 하나 더 보자는 마인드였다. 하지만 발자국을 써보면서 왜 이렇게 좋은걸 내가 안했을까라는 후회가 들고, 이렇게 정리를 해가면서 했으면 아마 실력이 조금이라도 더 올랐지 않았나 라는 생각이 들었다. 앞으로 발자국도 쓰고, 내가 따로 공부하고 있는것도 정리해가면서 공부를 해야겠다.

 

3-1. 미션 회고 

이번 미션에서 처음으로 ERD를 구성하고 어디에 PK를 주고 관계 설정을 어떻게 할지에 대한 고민이 많았던 것 같다. 현재 대학교 2-2에 재학중인데 데이터베이스 과목을 수강중인데 꽤 도움이 되었던 것 같고, 백엔드 개발자가 될려면 데이터베이스 공부도 놓지 말아야겠다고 생각이 들었다. 앞으로 더 많은 미션들이 기다리고 있는데 열심히 공부를 해야겠다! 

반응형

+ Recent posts