elevne's Study Note
Spring Boot 복습 (6) 본문
게시판의 질문, 답변에는 누가 글을 작성하였는지 알려주는 속성이 필요하다. Question, Answer 클래스에 아래 코드를 추가해준다.
@ManyToOne
private SiteUser author;
Question, Answer 에 위 속성을 추가해준 뒤에는 질문과 답변 저장 시 author 속성도 저장할 수 있는 것이다. Answer Controller 부터 아래와 같이 수정해주었다.
package com.springboot.study.controller;
import com.springboot.study.entity.Question;
import com.springboot.study.entity.SiteUser;
import com.springboot.study.form.AnswerForm;
import com.springboot.study.service.AnswerService;
import com.springboot.study.service.QuestionService;
import com.springboot.study.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.Valid;
import java.security.Principal;
@RequestMapping("/answer")
@RequiredArgsConstructor
@Controller
public class AnswerController {
private final QuestionService questionService;
private final AnswerService answerService;
private final UserService userService;
@PostMapping("/create/{id}")
public String createAnswer(Model model, @PathVariable("id") Integer id,
@Valid AnswerForm answerForm, BindingResult bindingResult
, Principal principal) {
Question question = this.questionService.getQuestion(id);
SiteUser siteUser = this.userService.getUser(principal.getName());
if (bindingResult.hasErrors()) {
model.addAttribute("question", question);
return "question_detail";
}
this.answerService.create(question, answerForm.getContent(), siteUser);
return String.format("redirect:/question/detail/%s", id);
}
}
현재 로그인한 사용자에 대한 정보를 알기 위해서는 Spring Security 가 제공하는 Principal 객체를 사용해야 한다고 한다. principal.getName() 을 통해 현재 로그인한 사용자의 사용자 ID 를 얻을 수 있다. 이 ID 를 통해 SiteUser 을 조회하는 메서드를 아래와 같이 추가해준다.
package com.springboot.study.service;
import com.springboot.study.entity.SiteUser;
import com.springboot.study.exceptions.DataNotFoundException;
import com.springboot.study.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Optional;
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public SiteUser create(String username, String email, String password) {
SiteUser user = new SiteUser();
user.setUsername(username);
user.setEmail(email);
user.setPassword(passwordEncoder.encode(password));
this.userRepository.save(user);
return user;
}
public SiteUser getUser(String username) {
Optional<SiteUser> siteUser = this.userRepository.findByUsername(username);
if (siteUser.isPresent()) {
return siteUser.get();
} else {
throw new DataNotFoundException("SITE_USER_NOT_FOUND");
}
}
}
또, 답변 저장 시 작성자를 저장하도록 AnswerService 의 create 메서드도 수정해준다.
package com.springboot.study.service;
import com.springboot.study.entity.Answer;
import com.springboot.study.entity.Question;
import com.springboot.study.entity.SiteUser;
import com.springboot.study.repository.AnswerRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@RequiredArgsConstructor
@Service
public class AnswerService {
private final AnswerRepository answerRepository;
public Answer create(Question question, String content, SiteUser user) {
Answer answer = new Answer();
answer.setContent(content);
answer.setCreateDate(LocalDateTime.now());
answer.setQuestion(question);
answer.setAuthor(user);
this.answerRepository.save(answer);
return answer;
}
}
위와 같은 방식으로 Question 을 수정해주었다.
전부 작성해준 이후, 로그인하지 않은 상태에서 질문 또는 답변을 등록하면 500 에러 (서버오류) 가 발생하게된다. 이 오류는 Principal 객체가 null 값이라서 발생하게 되는 것이다. Principal 객체는 로그인을 해야만 생성되는 객체이기 때문이다. 이 문제를 해결하기 위해서는 principal 객체를 사용하는 메서드에 @PreAuthorize("isAuthenticated()") 어노테이션을 붙여주어야 한다. 이 어노테이션이 붙은 메서드는 로그인이 필요한 메서드를 의미하고, 만약 로그인이 되지 않은 상태에서 호출될 경우 로그인 페이지로 이동시킨다. 그 다음으로 Config 에도 @EnableMethodSecurity(prePostEnabled = ture) 를 붙여주어 @PreAuthorize 어노테이션을 동작하게끔 한다.
이후, 로그인을 하지 않고 접근할 때 위와 같이 로그인 페이지로 넘어가게 되는 것을 확인할 수 있다.
Reference:
'Backend > Spring' 카테고리의 다른 글
Spring Security : JWT 적용해보기 (1) (0) | 2023.06.10 |
---|---|
Spring Boot Project (1) (0) | 2023.06.06 |
Spring Boot 복습 (5) (0) | 2023.04.09 |
Spring Boot 복습 (4) (0) | 2023.04.08 |
Spring Boot 복습 (3) (0) | 2023.04.07 |