삽질저장소

코프링 일주일 공부하고 정리하는 글

2022-01-06spring

필드 주입 방법

  • 다음의 코드에서 필드 주입은 동작하지 않는다. 필드 타입들이 null 이 될 수 없기 때문에 컴파일 타임에서 에러가 발생한다.

    • @Controller
      class ArticleController {
          @Autowired
          private var articleService : ArticelService
      
          @Autowired
          private var tagService : TagService
      }
  • 생성자 주입으로 변경. 변수는 val 로 해야 한다. val 는 변경 불가능한(immutable) 참조를 지정하는 변수다.

    • @Controller
      class ArticleController(
          private val articleService : ArticelService
          private val tagService : TagService) {
      }
  • 필드 주입이 필요하면 지연 초기화를 사용 한다. lateinit 변경자를 붙이면 프로퍼티를 나중에 초기화 할 수 있다. 이때 이 프로퍼티는 항상 var 여야 한다.

    • @Controller
      class ArticleController {
          @Autowired
          private lateinit var articleService : ArticelService
      
          @Autowired
          private lateinit var tagService : TagService
      }

@RequestParam

  • @RequestParam 의 필수 파라미터를 나타낼 때 required 속성을 사용하지 않음

    • @RequestMapping("/")
      @Controller
      class SearchController {
          @GetMapping
          fun search(@RequestParam(required = true) keyword : String?) {
              return "/result"
          }
      }
  • 위의 keyword 라는 값을 필수 값으로 만들기 위해서는 null 이 불가능한 타입으로 바꿔줘야 한다.

    • @RequestMapping("/")
      @Controller
      class SearchController {
          @GetMapping
          fun search(@RequestParam keyword : String) {
              return "/result"
          }
      }

java 의 static 키워드 대체하기

  • companion object 를 사용하여 정적 필드와 메소드를 정의

    • public class Article {
          public static final String DEFAULT_THUMBNAIL = "..";
      
          public static String thumbnail() {
              ...
          }
      }
    • class Article {
          companion object {
              val DEFAULT_THUMBNAIL = ".."
      
              fun thumbnail(): String {
                  ...
              }
          }
      }
  • kotlin 코드를 자바에서 참조해야 한다면 다음과 같이 const, @JvmStatic 키워드를 추가해야 한다.

    • class Article {
          companion object {
              const val DEFAULT_THUMBNAIL = ".."
      
              @JvmStatic
              fun thumbnail(): String {
                  ...
              }
          }

테스트

  • 생성자 주입

    • @TestConstructor(autowireMode = AutowireMode.ALL)
      internal class ForumControllerTests(val mockMvc: MockMvc) {
          ...
      }
  • mocMvc 테스트

    • 기존 자바에서 작성할 때
    • mockMvc.perform(
          get("/forum/topics").accept(MediaType.TEXT_HTML)
      ).andExpect(
          status().isOK
      ).andExpect(
          view().name("forum/topics")
      ).andExpect(
          model().attributeExists("topics")
      )
    • 코틀린에서 작성할 때(com.ninja-squad:springmockk 를 사용하면 조금 더 간결하게 mock 을 사용할 수 있다.)
    • mockMvc.get("/forum/topics") {
          accept = MediaType.TEXT_HTML
      }.andExpect {
          status { isOK() }
          view { name("forum/topics") }
          model { attributeExists("topics") }
      }

spring data JPA

  • findByIdOrNull()(spring data 2.1.4(springBoot 2.1.2)에 추가). kotlin extension function 으로 구현되어 있다.

    • val optionalUser: Optional<User> = userRepository.findById(1)
      optionalUser.map { it.username }.orElse("")
      optionalUser.orElse(null)?.username ?: ""
    • 위 코드를 이렇게 간단히 쓸 수 있다.
    • val user: User? = userRepository.findByIdOrNull(1)
      user?.username ?: ""
  • JPA Entity

    • java 코드
    • @Entity
      public class Person(
          @Id
          @GeneratedValue
          private Long id;
      
          @Column(nullable = false)
          private String name;
      
          // optional
          private String phoneNumber;
      
          // getters, setters
      }
    • kotlin 코드

      • jpa entity 는 기본 생성자가 필요하기 때문에 kotlin(“plugin.jpa”) 가 필요하다.
      • 해당 플러그인은 @Entity, @Embeddable, @MappedSuperClass 을 사용하면 자동으로 기본 생성자를 만들어준다.
    • @Entity
      class Person(
          @Id
          @GeneratedValue
          var id: Long?,
      
          @Column(nullable = false)
          var name: String,
      
          var phoneNumber: String?
      )

kotlin plugin

  • plugins {
        val kotlinVersion = "1.6.10"
        kotlin("plugin.spring") version kotlinVersion
        kotlin("plugin.jpa") version kotlinVersion
    }
  • plugins.spring

    • 해당 플러그인을 사용하면 @Component, @Async, @Transactional, @Cacheable, @SpringBootTest, @Configuration, @Controller, @RestController, @Service, @Repository 어노테이션에 app-open을 자동으로 추가한다.(kotlin-allopen, plugin.spring 은 동일한 프로젝트다.)
    • 코틀린에서 기본적으로 클래스는 final 이며, 해당 플러그인으로 open 키워드가 추가되게 된다.
    • Spring Boot 2.x 버전부터는 CGLIB Proxy 방식으로 Bean 을 관리하는데, CGLIB Proxy 는 Target Class 를 상속받아 생성하기 때문에 open 으로 상속이 가능한 상태이어야 한다. 그래서 all-open 플러그인이 필요하다.
  • plugins.jpa

    • 위에서 언급한 것처럼 @Entity, @Embeddable, @MappedSuperClass 어노테이션을 사용하면 no-arg생성자(기본생성자)가 자동으로 생성된다.
    • Hibernate 는 Reflection 으로 객체를 생성하기 때문에 protected 이상의 생성자가 필요하다.
  • all-open JPA

    • all-open 은 아래처럼 명시적으로 선언해줘야 한다.
    • allOpen {
          annotation("javax.persistence.Entity")
          annotation("javax.persistence.MappedSuperclass")
          annotation("javax.persistence.Embeddable")
      }
    • jpa 에서 all-open 이 필요한 이유

      • Lazy Loading 을 하기 위해서는 Proxy 객체이어야 하는데, kotlin 은 기본적으로 final 이기 때문에 Proxy 객체를 생성하지 못한다.
      • 그래서 all open 을 명시적으로 선언해줘서 상속이 가능하도록 해야 Proxy 객체가 만들어지고 Lazy Loading 을 사용할 수 있다.

똑같이 생겼지만 의미가 다른 코틀린과 자바의 코드

  • kotlin java
    T non-nullable nullable
    class final non-final
    List immutable mutable

참조 사이트

  • Thank You for Visiting My Blog, Have a Good Day 😆
    © 2021 Developer shPark, Powered By Gatsby.