elevne's Study Note

Spring Boot 복습 (6) 본문

Backend/Spring

Spring Boot 복습 (6)

elevne 2023. 4. 10. 12:05

게시판의 질문, 답변에는 누가 글을 작성하였는지 알려주는 속성이 필요하다. 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 어노테이션을 동작하게끔 한다.

 

 

 

result

 

 

 

이후, 로그인을 하지 않고 접근할 때 위와 같이 로그인 페이지로 넘어가게 되는 것을 확인할 수 있다.

 

 

 

 

 

 

Reference:

https://wikidocs.net/162330

'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