하나씩 차근차근

페이지에 접속하면 보이는 글과 각각의 글에 달린 답변을 누가 작성했는지 글쓴이를 표시해보겠습니다.

글쓴이를 표시하기 위해서는 Question 과 Answer 엔티티에 author 속성을 추가해야 합니다.

 

시작

Question 엔티티에 author 속성을 추가하고

Question 과 글쓴이의 관계는 N:1 관계이기 때문에 @ManyToOne 애노테이션을 붙입니다.

package com.crud.model;
...
@Entity
@Getter
@Setter
public class Question {

	...
	
	@ManyToOne
	private User author;
}

Answer 엔티티에도 author 속성을 추가하고 Answer 또한 N:1 관계이므로 @ManyToOne 을 붙입니다.

package com.crud.model;
...
@Entity
@Getter
@Setter
public class Answer {

	...
	
	@ManyToOne
	private User author;
}

mysql 을 통해 question 과 answer 테이블을 확인해보면

각각 author_id 라는 칼럼이 추가된것을 알 수 있습니다.

MariaDB [springboot_crud]> desc question;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| content     | text         | YES  |     | NULL    |                |
| create_date | datetime     | YES  |     | NULL    |                |
| subject     | varchar(200) | YES  |     | NULL    |                |
| author_id   | bigint(20)   | YES  | MUL | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+
5 rows in set (0.010 sec)

MariaDB [springboot_crud]> desc answer;
+-------------+------------+------+-----+---------+----------------+
| Field       | Type       | Null | Key | Default | Extra          |
+-------------+------------+------+-----+---------+----------------+
| id          | int(11)    | NO   | PRI | NULL    | auto_increment |
| content     | text       | YES  |     | NULL    |                |
| create_date | datetime   | YES  |     | NULL    |                |
| question_id | int(11)    | YES  | MUL | NULL    |                |
| author_id   | bigint(20) | YES  | MUL | NULL    |                |
+-------------+------------+------+-----+---------+----------------+
5 rows in set (0.007 sec)

 

Service 작성

다음으로 QuestionService 와 AnswerService 의 create 메서드에 author 를 넣어서

Question 과 Answer 이 생성될때 author 값도 저장되도록 create 메서드를 수정합니다.

package com.crud.service;
...
@Service
public class QuestionService {

	@Autowired
	private QuestionRepository questionRepository;
	...	
	public void create(String subject, String content, User author) {
		Question q = new Question();
		q.setSubject(subject);
		q.setContent(content);
		q.setAuthor(author);
		q.setCreateDate(LocalDateTime.now());
		questionRepository.save(q);
	}
	...
}
package com.crud.service;
...
@Service
public class AnswerService {

	@Autowired
	private AnswerRepository answerRepository;
	
	public void create(Question question, String content, User author) {
		Answer answer = new Answer();
		answer.setContent(content);
		answer.setCreateDate(LocalDateTime.now());
		answer.setQuestion(question);
		answer.setAuthor(author);
		answerRepository.save(answer);
	}
}

 

UserService 작성

위에서 만든 Service 를 Controller 에서 사용하기 위해서는 Controller 에서 User 를 가져와야합니다.

User 를 가져오는 메서드를 UserService 에 작성해보겠습니다.

package com.crud.service;
...
@Service
public class UserService {

	@Autowired
	private UserRepository userRepository;
	...	
	public User getUser(String username) {
		Optional<User> user = userRepository.findByusername(username);
		// user를 못찾을 경우 예외처리 필요
		return user.get();
	}
}

getUser 메서드에서 user 를 못찾았을 경우 예외처리는 이후에 다시 수정하겠습니다.

 

Controller 수정

다음으로 QuestionController 와 AnswerController 를 수정하겠습니다.

package com.crud.controller;
...
@Controller
public class QuestionController {
	
	@Autowired
	private QuestionService questionService;
	
	@Autowired
	private UserService userService;
	...
	@PreAuthorize("isAuthenticated()")
	@GetMapping("/question/create")
	public String questionCreate(QuestionForm questionForm) {
		return "question_form";
	}
	
	@PreAuthorize("isAuthenticated()")
	@PostMapping("question/create")
	public String questionCreate(@Valid QuestionForm questionForm, BindingResult bindingResult, Principal principal) {
		if(bindingResult.hasErrors()) {
			return "question_form";
		}
		User user = userService.getUser(principal.getName());
		questionService.create(questionForm.getSubject(), questionForm.getContent(), user);
		return "redirect:/question/list";
	}
}

questionCreate 의 인자로 Principal 객체가 추가되었습니다.

Principal 은 스프링 시큐리티에서 제공하는 현재 로그인한 사용자를 알 수 있는 객체로

Principal 의 getName 메서드를 사용하면 현재 로그인한 사용자의 이름을 알 수 있습니다.

Principal 객체의 getName 메서드를 통해 현재 로그인한 사용자명을 알 수 있음.

앞에서 만든 userService 의 getUser 메서드를 통해 현재 로그인한 사용자의 이름(principal.getName()) 을 통해

사용자를 가져와서 저장합니다.

Principal 객체는 로그인한 상태에서만 사용할 수 있기 때문에 만약, 로그인 하지 않은 경우 오류가 발생합니다.

때문에 로그인을 한 상태에서만 메서드를 사용하게 해주는 @PreAuthorize("isAuthenticated()") 애너테이션을 사용합니다.

@PreAuthorize("isAuthenticated()") 는 메서드를 로그인한 상태에서만 사용하게 해줌

AnswerController 또한 동일하게 작성합니다.

package com.crud.controller;
...
@Controller
public class AnswerController {
	
	@Autowired
	private QuestionService questionService;
	
	@Autowired
	private AnswerService answerService;
	
	@Autowired
	private UserService userService;
	
	@PreAuthorize("isAuthenticated()")
	@PostMapping("/answer/create/{id}")
	public String createAnswer(Model model, @PathVariable("id") Integer id, @Valid AnswerForm answerForm, BindingResult bindingResult, Principal principal) {
		
		Question question = questionService.getQuestion(id);
		User user = userService.getUser(principal.getName());
		
		if(bindingResult.hasErrors()) {
			model.addAttribute("question", question);
			return "question_detail";
		}
		
		answerService.create(question, answerForm.getContent(), user);
		
		return String.format("redirect:/question/detail/%s", id);
	}
}

다음으로 Controller 에서 PreAuthorize 애노테이션을 사용할 수 있도록

SecurityConfig 파일에 @EnableMethodSecurity 를 추가합니다.

package com.crud;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
	...
}

 

결과

로그인을 해서 글을 question 을 하나 작성하고 답글을 달아보면

다음과 같이 author_id 에 1이라는 숫자가 저장된것을 알 수 있습니다.

MariaDB [springboot_crud]> select * from question;
+-----+---------------------------------------+---------------------+--------------------+-----------+
| id  | content                               | create_date         | subject            | author_id |
+-----+---------------------------------------+---------------------+--------------------+-----------+
...
| 106 | hello nice to meet you.               | 2023-01-26 16:01:49 | hello              |         1 |
+-----+---------------------------------------+---------------------+--------------------+-----------+
106 rows in set (0.000 sec)
MariaDB [springboot_crud]> select * from answer;
+----+-----------------------+---------------------+-------------+-----------+
| id | content               | create_date         | question_id | author_id |
+----+-----------------------+---------------------+-------------+-----------+
...
|  3 | nice to meet you too~ | 2023-01-26 16:02:01 |         106 |         1 |
+----+-----------------------+---------------------+-------------+-----------+
3 rows in set (0.000 sec)
profile

하나씩 차근차근

@jeehwan_lee

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!