이번 포스팅에서는 MVC 패턴으로 구현한 회원가입/ 로그인 기능에서 Controller에 대해 다루고 각각의 메소드에 사용된 어노테이션을 정리해 볼 것이다.
우선 전체적인 구조는 다음과 같다.
1 . src → main → java → (프로젝트명의) package
안에 mvc 패턴을 구성할 패키지를 생성해준다.
패키지와 클래스, 인터페이스 구성은 다음과 같다.
📁 model
📁 service
📁 repository
📁 dto
📁 security
📁 exception
Controller 의 역할
Controller가 해주는 것을 정리해보면 다음과 같다.
Service 에 정의된 비즈니스 로직을 호출
url에서 id값을 가져와 해당 요청 url에 따라 적절한 view와 mapping을 해주는 작업을 수행함.
ResponseBody에 데이터를 담아 반환해준다.
- JAVA를 JSON으로 변환해주는 작업
- JSON을 JAVA 객체로 변환해주는 작업
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)을 찾아 주입한다.
service 의존성을 주입받아 service 객체 생성 → 생성된 객체를 @Bean 으로 등록 → 그 객체를 다시 Controller로 보내줌
위의 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 내에서 발생하는 예외를 잡아서 하나의 메서드에서 처리해주는 기능을 한다.
@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할 클래스를 생성해주었다.
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에 대한 내용을 다뤄보도록 하겠다.
대단한 사람! 많이 배우고 갑니다