Spring MVC 패턴으로 구현한 회원가입/로그인 + 어노테이션 정리 -② Controller

Jee hyun. k·2021년 7월 12일
18
post-thumbnail

이번 포스팅에서는 MVC 패턴으로 구현한 회원가입/ 로그인 기능에서 Controller에 대해 다루고 각각의 메소드에 사용된 어노테이션을 정리해 볼 것이다.

우선 전체적인 구조는 다음과 같다.

  • DTO 는 데이터를 가지고 다닐 패키지 녀석이라서 모든 통신 사이에서 작용한다.
  • Controller : 클라이언트와의 요청과 응답을 담당하는 부분이며 서비스에서 데이터를 주고 받는다.
  • Service : 비즈니스 로직을 담당하는 부분이다.
    이게 무슨말일까? 서버에서 일어나는 모든 처리들을 클라이언트가 알아먹을 수 있게(?) 사용자의 요구사항을 처리하는 일을 한다고 보면 된다.
  • Repository : JPA를 사용했을 때 DB에서 쿼리문(SQL문)의 역할을 한다.
    POST, GET, DELETE 시 어떤 값을 기준으로 해당 명령을 처리할 것인지를 기재하였다.
  • Entity : DB의 테이블 역할을 담당한다.

1 . src → main → java → (프로젝트명의) package
안에 mvc 패턴을 구성할 패키지를 생성해준다.
패키지와 클래스, 인터페이스 구성은 다음과 같다.

📁 controller

  • UserController
  • HomeController

📁 model

  • User
  • UserRole

📁 service

  • UserService

📁 repository

  • [Interface] UserRepository

📁 dto

  • SignupRequestDto

📁 security

  • WebSecurityConfig
    📁kakao
    UserDetailsImpl
    UserDetailsServiceImpl

📁 exception

  • ApiException

Controller 의 역할


  • Controller: 클라이언트와의 요청(request) 과 응답(response) 을 처리하는 곳.
    쉽게 말해 클라이언트, 사용자와 가장 맞닿아 있는 곳이다.

Controller가 해주는 것을 정리해보면 다음과 같다.

  • Service 에 정의된 비즈니스 로직을 호출

  • url에서 id값을 가져와 해당 요청 url에 따라 적절한 view와 mapping을 해주는 작업을 수행함.

  • ResponseBody에 데이터를 담아 반환해준다.
    - JAVA를 JSON으로 변환해주는 작업
    - JSON을 JAVA 객체로 변환해주는 작업


  • UserController

    * 만약 annotation이 활성화 되지 않는다면 Alt+Enter 를 눌러 import를 추가해줍니다.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    // 회원 로그인 페이지
    @GetMapping("/user/login")
    public String login() {
        return "login";
    }

    @GetMapping("/user/login/error")
    public String loginError(Model model) {
        model.addAttribute("loginError", true);
        return "login";
    }

    // 회원 가입 페이지
    @GetMapping("/user/signup")
    public String signup() {
        return "signup";

    }

    // 회원 가입 요청 처리
    @PostMapping("/user/signup")
    public String registerUser(SignupRequestDto requestDto) {
        userService.registerUser(requestDto);
        System.out.println(requestDto);
        return "redirect:/";

    }

    @GetMapping("/user/forbidden")  //인가받지 않은 사용자는 저 페이지를 보여줄꺼임
    public String forbidden() {
        return "forbidden";
    }

    @GetMapping("/user/kakao/callback")
    public String kakaoLogin(String code) {
        // authorizedCode: 카카오 서버로부터 받은 인가 코드
        userService.kakaoLogin(code);

        return "redirect:/";
    }

    @ExceptionHandler({IllegalArgumentException.class})
    public ResponseEntity<Object> handle(IllegalArgumentException ex) {
        ApiException apiException = new ApiException(
                ex.getMessage(),
                HttpStatus.BAD_REQUEST
        );

        return new ResponseEntity<>(
                apiException,
                HttpStatus.BAD_REQUEST
        );
    }

}

1. @controller : 해당 클래스가 컨트롤러 역할임을 스프링에게 알려주는 것.

2. @Autowired : [Dependency Injection] 의존성을 주입 받을 어노테이션.
스프링 IOC 컨테이너에서 필요한 의존 객체의 “타입"에 해당하는 빈(Bean)을 찾아 주입한다.

* 빈(Bean) : 스프링이 생성해주는 객체

service 의존성을 주입받아 service 객체 생성 → 생성된 객체를 @Bean 으로 등록 → 그 객체를 다시 Controller로 보내줌

  • 생성자
  • setter
  • 필드

위의 3가지의 경우에 Autowired를 사용할 수 있다. 그리고 Autowired는 기본값이 true이기 때문에 의존성 주입을 할 대상을 찾지 못한다면 애플리케이션 구동에 실패한다.
해당 메소드에서는 클래스 맨 상단에서 선언해준 userService

private final UserService userService;

객체의 의존성을 주입받을 수 있게 해준다.

3. @GetMapping : 이름에서 알 수 있듯 REST API에서 '조회' 에 해당하는 HTTP GET 요청을
특정 핸들러 메소드에 맵핑하기위한 annotation.


@GetMapping("/user/login") // 회원 로그인 페이지 return
@GetMapping("/user/login/error") //로그인 에러 페이지 return
@GetMapping("/user/signup")//회원가입 페이지 return
@GetMapping("/user/forbidden") //인가받지 않은 사용자는 저 forbidden 페이지를 보여줄꺼임
@GetMapping("/user/kakao/callback") //카카오 로그인을 불러올 페이지 return

ㄴ 주소에 파라미터가 노출 된다. ("url ")

4. @PostMapping : 마찬가지로 REST API에서 HTTP body에 데이터를 넣어 보내기 위한 annotation.


    @PostMapping("/user/signup")
    public String registerUser(SignupRequestDto requestDto) {
        userService.registerUser(requestDto);
        System.out.println(requestDto);
        return "redirect:/";

    }

→ 해당 메소드로 회원가입 요청 (signup request)을 처리한다.

DTO 에 대해서는 다음 게시물에서 다룰 예정이지만, 짧게 설명하고 넘어가자면 요청 관련 data를 가지고 다닐 가방 같은 녀석이다. update시의 DB를 담당하기도 한다.

5. @ExceptionHandler : @Controller, @RestController 가 적용된 Bean 내에서 발생하는 예외를 잡아서 하나의 메서드에서 처리해주는 기능을 한다.

* 빈(Bean) : 스프링이 생성해주는 객체
 @ExceptionHandler({IllegalArgumentException.class})
    public ResponseEntity<Object> handle(IllegalArgumentException ex) {
        ApiException apiException = new ApiException(
                ex.getMessage(),
                HttpStatus.BAD_REQUEST
        );

        return new ResponseEntity<>(
                apiException,
                HttpStatus.BAD_REQUEST
        );
    }

}

ExceptionHandler 어노테이션을 기재해주고 인자로 등록할 예외클래스를 등록하여 return 해준다.
해당 프로젝트에서는 /exception/ApiException 경로에

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@AllArgsConstructor
@Getter
public class ApiException {
    private final String message;
    private final HttpStatus httpStatus;
}

로 예외 처리시 return할 클래스를 생성해주었다.



  • HomeController

    * 만약 annotation이 활성화 되지 않는다면 Alt+Enter 를 눌러 import를 추가해줍니다.

import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {
    @GetMapping("/")
    public String home(Model model, @AuthenticationPrincipal UserDetailsImpl userDetails) {
        model.addAttribute("username", userDetails.getUsername());
        return "index";
    }





    @Secured("ROLE_ADMIN")
    @GetMapping("/admin")
    public String admin(Model model, @AuthenticationPrincipal UserDetailsImpl userDetails) {
        model.addAttribute("username", userDetails.getUsername());
        model.addAttribute("admin", true);
        return "index";
    }
}

* 해당 클래스에서는 스프링 내장 객체인 Model 객체를 import하여 사용하였다.
파라미터에 Model model로 선언해주고

model.addAttribute("username", userDetails.getUsername());
model.addAttribute("admin", true);

이 model.addAttribute( ) 메소드를 이용하여 view 페이지에 전달할 데이터를 (key, value) 형태로 전달한다.

1. @GetMapping : 어노테이션의 역할은 위의 UserController 의 설명 참고.

 @GetMapping("/")

으로 Model에 username의 객체를 갖고 index로 return해주는 메소드이다.
("/") 의 경우 index.html 즉 메인페이지를 의미하고,
/admin 으로 매핑된 페이지는 관리자 권한을 가진 model에 username의 객체를 갖고 index로 return해주는 메소드이다.

2. @Secured : @Secured 어노테이션은 권한을 부여하는 메소드에 선언해주는 annotation 이다. Class나 Method 단위까지 지정을 할 수 있다.

@Secured("ROLE_ADMIN")

로 ROLE_ADMIN만 접근시킬 해당 메소드위에 @Secured 어노테이션을 선언해주었다.

3 . @AuthenticationPrincipal : @AuthenticationPrincipal 을 사용하면 Service - UserDetailsImplement에서 Return한 객체 를 파라미터로 직접 받아 사용할 수 있다.

다음 포스팅에서 이어서 Model, Repository ,DTO, Service, 그리고 Security에 대한 내용을 다뤄보도록 하겠다.

profile
junior developer

10개의 댓글

comment-user-thumbnail
2021년 7월 12일

대단한 사람! 많이 배우고 갑니다

1개의 답글
comment-user-thumbnail
2021년 7월 19일

오오 두번째 라인 진입했네요ㅋㅋㅋ

1개의 답글
comment-user-thumbnail
2021년 7월 19일

너무너무 좋아요~

1개의 답글
comment-user-thumbnail
2021년 7월 19일

역시 우리의 대가리십니다...

1개의 답글