🎁2024 역대급 사랑주간 시작🎁

인프런 워밍업 클럽 2기 - 백엔드 프로젝트(Kotlin, Spring) / 3주차 발자국

인프런 워밍업 클럽 2기 - 백엔드 프로젝트(Kotlin, Spring) / 3주차 발자국

image

1주 동안 배운 내용을 정리하고 회고하는 시간을 가져보자.

 

11 ~ 12 일차 - Controller, Test, View

Test

이전과 이어서 Controller를 개발하고 이에 대한 Test를 작성하게 되었다.

아래는 작성한 테스트 코드의 일부분이다.

@DisplayName("[API 컨트롤러 테스트]")
@SpringBootTest // Spring boot를 실제로 띄운 다음에 테스트가 진행 됨
@AutoConfigureMockMvc // MockMvc 관련 설정 세팅
class PresentationApiControllerTest(
        @Autowired private val mockMvc: MockMvc
) {
		// 생략
	
    private fun performGet(uri: String): MvcResult {
		    return mockMvc
		            .perform(MockMvcRequestBuilders.get(uri))
		            .andDo(MockMvcResultHandlers.print())
		            .andReturn()
    }
}

@SpringBootTest

  • Spring Boot 애플리케이션을 테스트 할 때 사용되는 어노테이션이다.

  • 실제 애플리케이션 환경과 유사한 환경을 구성하여 테스트를 진행할 수 있다.

@AutoConfigureMockMvc

  • SpringMVC를 모의로 테스트 할 때 사용되는 어노테이션이다.

  • MockMvc 객체가 자동으로 구성하고 테스트 환경을 유사하게 구성할 수 있다.

MockMvc는 실제 서버를 실행하지 않고도 컨트롤러의 요청 및 응답을 테스트 할 수 있도록 한다.

여기서 SpringBootTestAutoConfigureMockMvc를 같이 사용하는 것에 모순을 느끼게 되었고, 좀 더 내용을 찾아보게 되었다.

@WebMvcTest

  • MVC 레이어를 테스트하는데 사용되는 어노테이션이다.

  • 특정 컨트롤러와 그에 관련된 컴포넌트들만 주입받아 특정 컨트롤러의 동작을 집중적으로 테스트 할 수 있다.

  • MockMvc를 자동으로 설정하고 주입하여 요청 및 응답을 테스트 할 수 있도록 한다.

  • 기본적으로 Service, Repository를 제외하기에 테스트가 가볍고 빠르게 진행될 수 있다. ⇒ 필요하다면 Mock 객체를 통해 주입할 수 있다.

결론적으로 말하자면 아래와 같다.

💡 Contorller를 집중적으로 테스트하기 위해서는 @SpringBootTest, @AutoConfigureMockMvc 를 사용하는 것 보다 @WebMvcTest를 사용하는 것이 Controller 단위 테스트 패턴에 적합하다.

 

그렇다고 @SpringBootTest를 사용하면 안된다는 것은 아니다.

실제로 통합 테스트를 작성할 때 (=Controller, Service, Repository 등 여러 컴포넌트가 함께 작동할 때)는 SpringBootTest를 사용하여 실제로 적용될 설정이나 복잡한 시나리오가 정상적으로 작동되는지 확인할 때 사용할 수 있다.

 

View

Thymeleaf를 활용해서 포트폴리오 페이지를 만들어 보게되었다.

강사님 처럼 고양이 사진을 넣어보고 싶었지만, 어울리는 사진을 넣기가 힘들기에 가지고 있던 다른 사진으로 대체해보았다.

image

기존 코드에서 중복되는 부분을 fragment와 layout으로 분리하였고, 이를 이용하여 더 깔끔한 코드를 만들 수 있었다.

 

13 ~ 15 일차 - Admin Controller, Service

Kotlin

13 ~ 15일차에 접어들면서 대부분의 Kotlin 문법에 대해서는 꽤 익숙해진 것 같다.

3 ~ 5일차에는 변수 선언과 함수 선언 그리고 문자열 작성에 대한 차이점을 다뤄봤었다.

이번에는 강의에서 만나봤던 차이점 중 간편하다고 느꼈던 차이점에 대해서 정리해보고자 한다.

 

먼저, for 문이 간편하게 바뀌었다는 생각을 많이 하게 되었다.

변수 선언같은 부분도 사라지고, .. 을 이용해 1에서 10까지의 범위를 지정할 수 있었다.

물론, until과 같이 이전 값 까지 증가와 같은 기능과 step()을 이용하여 증가 값을 설정할 수도 있다.

for (i in 1..10) // Kotlin
for (int i = 1; i <= 10; i++) // Java

 

두번째로 @RequiredArgsConstructor를 사용하지 않아도 되는 부분이 매우 편했다.

Kotlin은 class 생성 시 기본 생성자를 매우 쉽게 작성할 수 있고, 이를 프로퍼티로 생성 해준다.

 

여기서의 프로퍼티는 자바에서의 필드 뿐만 아니라 getter, setter를 포함한다고 생각하면 된다.

이에 대한 자세한 내용은 아래 블로그를 참고하면 도움이 될 것이라고 생각한다.

[Java/Kotlin] 필드(Field)와 프로퍼티(Property)는 무슨 차이가 있을까?

 

이때, val로 매개변수를 선언하게되면 Java 로 생각하자면 불변 필드와 해당 필드에 대한 생성자까지 만들어지게 된다.

@Service
class AdminAchievementService(
    private val achievementRepository: AchievementRepository 
)
@Service
public class AdminAchievementService {
    private final AchievementRepository achievementRepository;

    public AdminAchievementService(AchievementRepository achievementRepository) {
        this.achievementRepository = achievementRepository;
    }
   
    // 생략
}

그렇기에 Java에서 생성자 주입을 통해 의존성을 주입 할 수 있게 되며 별도의 @RequiredArgsConstructor 어노테이션을 사용하지 않아도 된다.

 

마지막으로 그외에도 다양한 편의 기능들이다.

val pageAttributes = mutableMapOf<String, Any>(
        Pair("menuName", "Resume"),
        Pair("pageName", table.name),
        Pair("editable", true),
        Pair("deletable", false),
        Pair("hasDetails", false)
) // Kotlin에서 Map에 데이터를 넣어줄 때 Pair를 사용할 수 있음

val id = line.slice(0..endIndex).toLong() // Long 타입으로 변환

Pair와 같이 Map에 데이터를 더 편하게 넣을 수 있도록 Key-Value 형식으로 값을 설정할 수 있었고,

String의 경우도 단순히 toLong()과 같은 기능으로 변환을 쉽게 할 수 있었다.

 

이런 것들이 별 것 아닌 거 같아보여도 실제로 쌓이다 보면 꽤나 피로한 요소들이어서 정말 좋다고 느꼈던 것 같다.

아직 모르는 부분도 분명히 많을 것이지만 Kotlin을 통해서 만족감을 느낄 수 있었다.

 

퍼사드 패턴이 과연 유리한가?

다음으로 정리할 내용은 퍼사드 패턴에 대해서다.

퍼사드 패턴(Facade Pattern)은 구조 패턴의 한 종류로 복잡한 서브 클래스들의 공통적인 기능을 정의하는 상위 수준의 인터페이스를 제공하는 패턴이다.

 

이는 사실 8 ~ 10일차에서 Service, Repository 부분에서 소개했던 한 번에 의존관계를 주입받을 수 있는 Repository 를 생성했다는 내용에서 사용 된 패턴이다.

사용할 때는 사실 모르고 사용했지만, Admin 부분에 대한 강의에서는 퍼사드 패턴을 사용하지 않고 필요한 컴포넌트들만 주입받아 사용하며 강사님께서 “퍼사드 패턴과 이런 방식 중에 뭐가 더 유리한지 생각해보면 좋을 것 같다”고 말씀해주셨다.

 

기존에 코드를 작성할 때는 디자인 패턴에 대해서 모르기도 했었지만 항상 필요한 것만 불러와서 써도 괜찮다고 생각하고 있었다.

하지만, 실제로 퍼사드 패턴을 사용하면서 느낀 점은 “컴포넌트를 묶어 사용할 수 있다면 코드가 많이 줄겠다” 였다.

실제로 Presentation 부분에서는 컴포넌트를 묶음으로써 생성자 부분에 작성하게 된 코드를 줄일 수 있었다.

 

하지만, 반대로 생각해보면 묶을 필요가 없다면 굳이 사용할 필요가 없는 패턴이기도 하다.

실제로 Admin 부분을 개발하면서는 각 도메인 부분으로 폴더를 나눠 서비스 로직을 작성했으며, 각 서비스 단에서 필요한 리포지토리만 불러와 사용하였다.

 

결론적으로 상황에 따라 유리할 수도 있고, 아닐 수도 있다.

학습하다 보면 항상 결론은 위와 같이 나오는 것 같은데 그만큼 상황에 맞는 구조와 코드를 잘 적용시키는게 중요하다는 생각을 하게되었다.

 

서브 미션

이번 3주차에는 미션 4를 수행하게 되었다.

미션 4는 조회 API를 만들고 이에 대한 테스트 코드를 작성하는 것이 내용이었고 열심히 작성해서 제출하였다.

이후 미션 내용을 README에도 정리해보았다.

https://github.com/ppusda/MML

서브 미션을 진행하면서도 약간의 배운 점이 생겨서 이 또한 정리해보고자 한다.

 

TargetEntity와 MappedBy

mappedBy의 경우는 많이 사용해봤지만, TargetEntity의 경우는 사용한 적이 없어 어떤 차이가 있는지 궁금하게되었다.

targetEntity

  • 관계의 대상 엔티티를 명시적으로 지정할 때 사용된다. (관계 대상을 명시적으로 지정)

  • 주로 @OneToMany@ManyToMany와 같은 어노테이션에서 사용하며, 관계의 상대 엔티티 클래스의 이름을 지정한다.

mappedBy

  • 관계의 주체와 종속성을 정의합니다. (관계의 주체를 설정함)

  • 주로 양방향 관계에서 사용되며, 관계의 주체가 되는 쪽에서 어떤 필드가 관계를 관리하는지를 지정한다.

 

총 회고

3주차에는 드디어 화면을 직접 개발해보면서 좀 더 포트폴리오에 다가가고 있다는 생각이 들었다.

미션을 따라서 진행하면서 좀 더 나만의 포트폴리오 처럼 꾸밀 수 있는 방법은 뭐가 있을까에 대해서도 생각해보려고 한다.

 

개발적으로도 성장하고 있는 것이 느껴지기도 하지만 그와 동시에 많이 멀었다는 생각도 계속하게 된다.

특히 강사님의 코드를 보며 학습하다보니까 구조나 코드가 정말 정갈하다는 생각을 하게되었다.

위에서 정리했던 Kotlin의 특성이나 디자인 패턴을 정말 잘 적용한다는 생각이 들었고, 앞으로 나도 그렇게 할 수 있는 개발자가 되고 싶다는 생각을 하게 되었다.

 

Kotlin에 대해서도 많이 익숙해진 것 같다.

Kotlin이 Java의 상위호환이라는 말을 많이 들었는데, 이전 경험이 있어서 크게 기대는 하지 않고 학습을 진행했다.

하지만 정말 편한 점이 많이 있었고, 더 공부해서 코루틴 같은 개념도 적용해보고 싶다고 생각했다.

 

앞으로도 계속 개발자로 공부할 수 있도록 노력해야겠다.

모두 화이팅! (o゚v゚)ノ

댓글을 작성해보세요.

채널톡 아이콘