ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] MVC 기본 어노테이션과 Lombok 정리하기
    Server/Spring MVC 2021. 10. 7. 21:59
    728x90
    반응형

    Spring 기본적인 어노테이션 정리하기

    이번 글에서는 Spring Boot에서 MVC를 사용할 때 자주 사용하는 어노테이션과 Lombok 어노테이션에 대해서 간단하게 알아보겠습니다.

    121212

    위의 사진은 먼저 Spring Boot로 프로젝트를 만들었을 때 프로젝트 구조에 대한 정리입니다. 보통 view 와 관련된 파일들을 resources 디렉토리 아래에 작업을 하는데요. 대표적으로 jsp가 존재합니다. Spring Boot에서 jsp를 사용하는 법 은 여기를 확인하시면 됩니다. 대략 프로젝트 구조에 대해서 정리가 되었다 가정하고 바로 어노테이션들의 특징에 대해서 알아보겠습니다.



    @RequestMapping

    RequestMapping은 URI를 Controller의 메소드와 맵핑을 할 때 사용하는 스프링 프레임워크 어노테이션입니다.

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping(value = "spring")
    public class TestController {
    
        @RequestMapping(value = "study", method = RequestMethod.GET)
        public String test() {
            return "test";
        }
    }
    • 위의 코드 예시라면 http://localhost:8080/spring/study 로 접속하면 위의 메소드가 실행됩니다.
    • 클래스 위의 RequestMapping은 위와 같이 GET, POST를 지정해서도 사용하지만, 공통경로를 지정하기 위해서 많이 사용합니다.

     

     

    @GetMapping, @PostMapping, @DeleteMapping, @PutMapping, @PatchMapping

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @Slf4j
    public class TestController {
    
        @GetMapping("test")
        public String test() {
            return "test";
        }
    }

    위에서 RequestMapping이 공통 경로를 지정하는데 많이 사용된다고 했는데요. 그 이유는 번거롭게 따로 GET, POST 설정을 안해도 되도록 GetMapping와 같은 어노테이션이 등장했기 때문입니다. 즉, GET, POST, PUT, PATCH, DELETE 메소드 역할에 맞게 사용하여 URL 매핑하면 됩니다.

     

    여기까지는 아주 무난하고 쉽쥬? Spring Boot와 Spring MVC의 힘으로 아주 편하게 사용할 수 있습니다.



    @RequestParam

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @Slf4j
    public class TestController {
    
        @GetMapping("test")
        public String test(@RequestParam("name1") String name1,
                           @RequestParam("name2") String name2) {
            log.info(name1);  // lee
            log.info(name2);  // choi
            return "test";
        }
    }

     

    Request 형식

    http://localhost:8080/test?name1=lee&name2=choi

    다음 알아볼 어노테이션은 RequestParam인데요. 여러가지 용도로 사용될 수 있겠지만 대표적으로 위와 같이 GET 메소드의 query string을 꺼내올 때 @RequestParam을 사용합니다.



     

    RequestPart

    RequestPartmultipart/form-data 형태로 요청이 올 때 사용하는 어노테이션입니다. 즉, 대표적으로 사진 업로드 하는 API에서 많이 사용되는데요.

    import com.example.ggyunispring.service.S3Service
    import org.springframework.web.bind.annotation.*
    import org.springframework.web.multipart.MultipartFile
    
    @RequestMapping("/test")
    @RestController
    class TestController(
        private val s3Service: S3Service
    ) {
    
        @PostMapping("/upload")
        fun uploadTest(@RequestPart("image") multipartFile: MultipartFile) {
            s3Service.upload(multipartFile)
        }
    
    } 

    이번에는 Kotlin으로 MultipartFile 을 받아오는 것을 간단하게 작성해보았습니다.

     

    post

     

    참고로 포스트맨에서 MultipartFile로 요청하는 방법은 위와 같습니다.



    RequestParm과 RequestPart 차이는 무엇일까?

    RequestParamRequstPart의 차이가 무엇인지 얘기가 종종 나오곤 하는데요. 왜냐면 위의 사진 업로드 예제 코드를 보았을 때 RequestPart가 아닌 RequestParam을 사용해도 코드는 정상적으로 작동을 하기 때문인데요. 그래서 둘의 차이에 대해서 좀 더 알아보았습니다.

     

    스크린샷 2021-10-07 오후 9 10 53

    Note that @RequestParam annotation can also be used to associate the part of a "multipart/form-data" request with a method argument supporting the same method argument types. 
    The main difference is that when the method argument is not a String or raw MultipartFile / Part, @RequestParam relies on type conversion via a registered Converter or PropertyEditor while RequestPart relies on HttpMessageConverters taking into consideration the 'Content-Type' header of the request part. 
    RequestParam is likely to be used with name-value form fields while RequestPart is likely to be used with parts containing more complex content e.g. JSON, XML).

    Spring 공식 문서 를 보면 두 어노테이션의 차이를 위와 같이 설명하고 있습니다. 요약하자면 RequestParammultipart/form-data을 받는데 사용할 수 있고 등록된 Converter 또는 PropertyEditor를 통한 형식 변환에 의존한다고 합니다. 반면에 RequestPart는 요청 부분의 'Content-Type' 헤더를 고려하는 HttpMessage Converters에 의존한다는 것입니다.

     

    이거만 보면 무슨말이지? 싶기도 한데요. 좀 더 보면 RequestParam은 name-value form 형태에 좀 더 적합한 거 같고, RequestPart는 JSON, XML 처럼 복잡한 내용을 포함할 때 사용하는게 더 좋다고 합니다. (제가 사용한 예제 코드는 RequestParam이 더 적절한 거 같습니다!)



     

    @RequestBody

    import com.example.demo.dto.User;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    
    @Slf4j
    @RestController
    public class TestController {
    
        @PostMapping("test")
        public String test(@RequestBody UserRequestDTO userRequestDTO) {
            log.info(userRequestDTO.getName());  // 이름
            log.info(userRequestDTO.getPart());  // 파트
            return "test";
        }
    }

     

    Request Body

    {
        "name" : "이름",
        "part" : "파트"
    }

    다음으로 알아볼 어노테이션은 RequestBody 입니다. 아주 많이 사용하고 중요한 어노테이션인데요. UserRequestDTO 클래스에 name, part 필드가 존재합니다. @RequestBody는 HTTP Header에 application-json 으로 요청이 온다면 내부적으로 MessageConverter 중의 json을 객체에 매핑해주는 MappingJacksonHttpMessageConverter가 실행됩니다. (MessageConverter는 여러가지가 있으니 일단은 json에 대해서만 알아두어도 된다 생각하지만 더 궁금하다면 더 찾아보면 될 거 같습니다!)



    RequestHeader

    @RestController
    @Slf4j
    public class TestController {
    
        @GetMapping("test")
        public void test(@RequestHeader("token") final String token) {
            log.info(token);
        }
    }
    

    RequestHeader 어노테이션은 HTTP Header 정보를 꺼내는 어노테이션입니다. 예를들어 JWT를 사용한다면 API 인가 요청시 Request Header에 Token을 담아서 요청하는데 이 때 토큰을 HTTP Header에서 꺼내야 할 때 사용할 수 있습니다.



    PathVariable

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @Slf4j
    public class TestController {
    
        @GetMapping("test/{id}")
        public String test(@PathVariable(value = "id") Long id) {
            log.info(String.valueOf(id));
            return "test";
        }
    }

     

    Request 형식

    http://localhost:8080/test/1

    PathVariable도 정말 많이 사용되는 어노테이션인데요. 이 어노테이션은 params 값을 가져오는데 사용하는 어노테이션입니다. {id}의 이름과 파라미터 이름이 같다면 value 는 사용하지 않아도 된다. (ex: @PathVariable Long id)



    Lombok 정리하기

    Java의 코드를 보면 일반적으로 아래와 같이 변수는 private를 선언하고 getter/setter를 사용하게 됩니다.

    public class Login {
    
        private String id;
        private String password;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }

    위와 코드를 보면 Getter/Setter를 직접 작성하고 있습니다. 그런데 만약에 필드가 20개 30개라면 클래스의 길이가 엄청 길어질 것입니다.
    또한 필드가 수정되면 메소드를 다시 수정해야 한다는 단점이 존재합니다.

     

    이러한 단점을 해결하기 위해서 나온 것이 Lombok 입니다. Lombok은 자바 라이브러리이고, @Data, @Getter, @Setter, @Builder 등등의 다양한 어노테이션을 제공합니다.

     

    Gradle (build.gradle)

    compileOnly 'org.projectlombok:lombok:1.18.10'
    annotationProcessor 'org.projectlombok:lombok:1.18.10'

    Maven (pom.xml)

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    자신이 사용하는 프로젝트에 맞게 의존성을 추가하겠습니다.



    Intellij plugin 설치하기

    그리고 intellij에서 lombok을 사용하기 위해 plugin을 설치해주어야 합니다.

    • Windows : file > Setting > Plugins > 'lombok' 입력 > INSTALL
    • MacOS : Intellij IDEA > preferences > Plugins > 'lombok' 입력 > INSTALL

    test

    lombok 설치가 완료되었으면 Restart Intellij IDEA 버튼이 나오고, intellij를 restart 하겠습니다.

    • Settings > Build > Compiler > Annotation Processors 로 이동 후, Enable annotation processing을 체크하겠습니다.

     

    test2

    이제 lombok에서 제공하는 몇가지 어노테이션에 대해 정리해보겠습니다.



    1. @Data

    import lombok.Data;
    
    @Data
    public class User {
        private int userIdx;
        private String name;
        private String part;
    }

    위와 같이 User 클래스에 @Data라는 어노테이션을 추가해주면 아래와 같이 메소드들이 생기는 것을 알 수 있습니다. 따라서 어노테이션 하나만으로 아래의 메소드를 사용할 수 있습니다. 하지만 일반적으로 @Data 어노테이션은 현재 사용하지 않는 많은 메소드를 만들기 때문에 사용을 지양하는 것이 좋습니다.

     

    lom



    2. @NoArgsConstructor

    @NoArgsConstructor
    public class User {
        private int userIdx;
        private String name;
        private String part;
    
        public User(int userIdx, String name, String part) {
            this.userIdx = userIdx;
            this.name = name;
            this.part = part;
        }
    }

    자바에서는 매개변수가 있는 생성자가 존재하면 기본 생성자는 자동으로 만들어주지 않게 되는데요. 이럴 때 직접 기본 생성자를 만드는 것이 아니라 @NoArgsConstructor 어노테이션을 사용하여 기본 생성자를 만들 수 있습니다.



    3. @AllArgsConstructor

    @Data
    @AllArgsConstructor
    public class User {
        private int userIdx;
        private String name;
        private String part;
    }

    위의 코드처럼 직접 모든 필드가 들어간 생성자를 만들어 줄 필요 없이 @AllArgsConstructor를 사용하면 모든 필드가 들어간 생성자를 만들어줍니다.



    4. @RequiredArgsConstructor

    import lombok.RequiredArgsConstructor;
    import lombok.NonNull;
    
    @RequiredArgsConstructor
    public class User {
        private final int userIdx;
        @NotNull
        private String name;
        private String part;
    }

    @RequiredArgsConstructor는 아주 많이 사용되는 어노테이션인데요. final이 붙은 필드와 @NotNull가 존재하는 필드로 생성자를 만들어주는 역할을 합니다. 위의 예시라면 userIdx, name을 매게변수로 하는 생성자를 만들어 줄 것입니다.



    5. @Slf4j

    로그를 출력하기 위해서 알고리즘 풀 때는 System.out.println()을 사용했겠지만 실제 개발 환경에서는 사용하면 안되는 메소드인데요. Spring에서는 @Slf4j나 다른 logger를 사용하여 로그를 출력할 수 있습니다.

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @Slf4j
    public class TestController {
    
        @GetMapping("/test")
        public String test() {
            log.info("로그입니다");
            return "test";
        }
    }

    위와 같이 log.info()를 사용하여 로그를 출력한다. (log.error(), log.debug() 다양하게 사용할 수 있습니다.)



    6. @Builder란?

    Builder 패턴에 대해서 모른다면 여기 를 참고해도 좋을 거 같습니다. 빌더 패턴은 Effective Java 아이템 2에도 나오는 내용입니다. 자바에서 유용하게 많이 사용될 수 있는 패턴인데요. 이거를 직접 구현한다면 꽤나 복잡한 코드를 작성해야 하지만 어노테이션 하나로 편리하게 사용할 수 있습니다.

    스크린샷 2021-10-07 오후 9 44 06

    사용법의 예시는 위와 같이 사용할 수 있는데요. 클래스 전체에 사용할 수도 있고, 생성자를 만들어서 그 생성자에서만 사용할 수도 있습니다.

     

    그 외적으로 Getter, Setter 등등 있지만 이름에서도 쉽게 어떤 것인지 알 수 있기에 단순한 것들을 생략하겠습니다!

    반응형

    댓글

Designed by Tistory.