Spring/게시판 만들기 프로젝트

[Spring Boot] #4_1 게시판 만들기

sujin7837 2020. 12. 22. 12:57
반응형

학습 목표

-객체 간의 관계 설정(@OneToMany, @ManyToOne 등)

 

학습 목차

4-1. 회원과 질문 간의 관계 매핑 및 리팩토링

4-2. 질문 상세보기 기능 구현

4-3. 질문 수정/삭제 기능 구현

4-4. 수정/삭제 기능에 대한 보안 처리 및 LocalDateTime 설정

4-5. 답변 추가 및 답변 목록 기능 구현

4-6. QuestionController 중복 제거 리팩토링

4-7. 원격 서버에 소스 코드 배포

 

 

//Question.java

@Entity
public class Question {	
	@Id
    @GeneratedValue
    private Long id;
    
    @ManyToOne	//2.
    @JoinColumn(foreignKey=@ForeignKey(name="fk_question_writer"))	//3.
    private User writer;	//1.
    private String writer;
    private String title;
    private String contents;
    private LocalDateTime createDate;	//4.
    
    public Question() {}	
    
    public Question(String writer, String title, String contents) {
    	super();
        this.writer=writer;
        this.title=title;
        this.contents=contents;
        this.createDate=LocalDateTime.now();	//4.
    }
    
    public String getFormattedCreateDate() {	//5.
    	if(createDate==null) {
        	return "";
        }
        return createDate.format(DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss"));
    }
}

1. Question 클래스에서 User와 관계를 정의해줍니다.

사용자와의 관계를 String이 아니라 User로 정의합니다.

 

2. @ManyToOne: Question과 User의 관계는 다 대 일

 

3. 외래키로 지정해줍니다.

 

4. 현재 날짜 및 시간을 생성해줍니다.

 

5. 날짜 출력 형식을 지정해줍니다.

 

Question이라는 데이터를 조회할 때 외래키로 연결되어 있는 User의 해당 사용자의 데이터를 조회해서 함께 로딩해줍니다.

 

//QuestionController.java

@Controller	
@RequestMapping("/questions")	
public class QuestionController {
	@Autowired	
    private QuestionRepository questionRepository;

	@GetMapping("/form")
    public String form(HttpSession session) {	
    	if(HttpSessionUtils.isLoginUser(session)) {
        	return "/users/loginForm";
        }
    	return "/qna/form";
    }
    
    @PostMapping("")
    public String create(String title, String contents, HttpSession session) {	
    	if(!HttpSessionUtils.isLoginUser(session)) {
        	return "/users/loginForm";
        }
        User sessionUser=HttpSessoinUtils.getUserFromSession(session);
        Question newQuestion=new Question(sessionUser, title, contents);	//1.	
        questionRepository.save(newQuestion);	
    	return "redirect:/";
    }
}

1. 객체에서 값을 꺼내오지 않고, 객체를 가져오도록 합니다.

 

<!--index.html-->

<div class="container" id="main">
		<div class="col-md-12 col-sm-12 col-lg-10 col-lg-offset-1">
			<div class="panel panel-default qna-list">
				<ul class="list">
					{{#questions}}	
					<li>
						<div class="wrap">
							<div class="main">
								<strong class="subject"> <a href="/questions/{{id}}">{{title}}</a>	<!--2.-->
								</strong>
								<div class="auth-info">
									<i class="icon-add-comment"></i> <span class="time">{{formattedCreateDate}}</span>	<!--1.-->
                                    <a href="./user/profile.html" class="author">{{writer.userId}}</a>	
								</div>
								<div class="reply" title="댓글">
									<i class="icon-reply"></i> <span class="point">8</span>
								</div>
							</div>
						</div>
					</li>
					{{/questions}}
				</ul>
				<div class="row">
					<div class="col-md-3"></div>
					<div class="col-md-6 text-center">
						<ul class="pagination center-block" style="display: inline-block;">
							<li><a href="#">«</a></li>
							<li><a href="#">1</a></li>
							<li><a href="#">2</a></li>
							<li><a href="#">3</a></li>
							<li><a href="#">4</a></li>
							<li><a href="#">5</a></li>
							<li><a href="#">»</a></li>
						</ul>
					</div>
					<div class="col-md-3 qna-write">
						<a href="/questions/form" class="btn btn-primary pull-right"
							role="button">질문하기</a>
					</div>
				</div>
			</div>
		</div>
	</div>

1. 현재 시간을 mustache 문법을 이용해 구현합니다.

 

2. /questions/{{id}} 경로를 이용해서 해당하는 질문의 상세보기 페이지로 이동할 수 있습니다.

 

//QuestionController.java

@Controller	
@RequestMapping("/questions")	
public class QuestionController {
	@Autowired	
    private QuestionRepository questionRepository;

	@GetMapping("/form")
    public String form(HttpSession session) {	
    	if(HttpSessionUtils.isLoginUser(session)) {
        	return "/users/loginForm";
        }
    	return "/qna/form";
    }
    
    @PostMapping("")
    public String create(String title, String contents, HttpSession session) {	
    	if(!HttpSessionUtils.isLoginUser(session)) {
        	return "/users/loginForm";
        }
        User sessionUser=HttpSessoinUtils.getUserFromSession(session);
        Question newQuestion=new Question(sessionUser, title, contents);		
        questionRepository.save(newQuestion);	
    	return "redirect:/";
    }
    
    @GetMapping("/{id}")
    public String show(@PathVariable Long id, Model model) {	//1.
    	model.addAttribute("question", questionRepository.findById(id).get());
    	return "/qna/show";
    }
    
    @GetMapping("/{id}/form")
    public String updateForm(@PathVariable Long id, Model model) {	//2.
    	return "/qna/updateForm";
    }
}

1. 질문 목록에서 하나의 데이터를 상세보기 할 수 있는 페이지로 이동하도록 해줍니다.

 

2. 질문 수정 페이지로 이동할 수 있게 해줍니다.

 

<!--show.html-->

<div class="container" id="main">
    <div class="col-md-12 col-sm-12 col-lg-12">
    	{{#question}}	<!--1.-->
        <div class="panel panel-default">
          <header class="qna-header">
              <h2 class="qna-title">{{title}}</h2>
          </header>
          <div class="content-main">
              <article class="article">
                  <div class="article-header">
                      <div class="article-header-thumb">
                          <img src="https://graph.facebook.com/v2.3/100000059371774/picture" class="article-author-thumb" alt="">
                      </div>
                      <div class="article-header-text">
                          <a href="/users/92/kimmunsu" class="article-author-name">{{writer.userId}}</a>	<!--2.-->
                          <a href="/questions/413" class="article-header-time" title="퍼머링크">
                              {{formattedCreateDate}}	<!--2.-->
                              <i class="icon-link"></i>
                          </a>
                      </div>
                  </div>
                  <div class="article-doc">
                      {{contents}}	<!--2.-->
                  </div>
                  <div class="article-util">
                      <ul class="article-util-list">
                          <li>
                              <a class="link-modify-article" href="/questions/{{id}}/form">수정</a>	<!--3.-->
                          </li>
                          <li>
                              <form class="form-delete" action="/questions/{{id}}" method="POST">	<!--4.-->
                                  <input type="hidden" name="_method" value="delete">	<!--5.-->
                                  <button class="link-delete-article" type="submit">삭제</button>
                              </form>
                          </li>
                          <li>
                              <a class="link-modify-article" href="/">목록</a>
                          </li>
                      </ul>
                  </div>
              </article>

              <div class="qna-comment">
                  <div class="qna-comment-slipp">
                      <p class="qna-comment-count"><strong>2</strong>개의 의견</p>
                      <div class="qna-comment-slipp-articles">
						  {{#answers}}
                          <article class="article" id="answer-1405">
                              <div class="article-header">
                                  <div class="article-header-thumb">
                                      <img src="https://graph.facebook.com/v2.3/1324855987/picture" class="article-author-thumb" alt="">
                                  </div>
                                  <div class="article-header-text">
                                      <a href="/users/1/자바지기" class="article-author-name">{{writer.userId}}</a>
                                      <a href="#answer-1434" class="article-header-time" title="퍼머링크">
                                          {{formattedCreateDate}}
                                      </a>
                                  </div>
                              </div>
                              <div class="article-doc comment-doc">
                                  <p>{{contents}}</p>
                              </div>
                              <div class="article-util">
                                  <ul class="article-util-list">
                                      <li>
                                          <a class="link-modify-article" href="/questions/413/answers/1405/form">수정</a>
                                      </li>
                                      <li>
										  <a class="link-delete-article" href="/api/questions/{{question.id}}/answers/{{id}}">삭제</a>
									</li>
                                  </ul>
                              </div>
                          </article>
                          {{/answers}}
                          <form class="answer-write" method="post" action="/api/questions/{{id}}/answers">
                              <div class="form-group" style="padding:14px;">
                                  <textarea class="form-control" placeholder="Update your status" name="contents"></textarea>
                              </div>
                              <input type="submit" class="btn btn-success pull-right" value="답변하기"/>
                              <div class="clearfix" />
                          </form>
                      </div>
                  </div>
              </div>
          </div>
        </div>
        {{/question}}
    </div>
</div>

1. mustache를 이용해서 question정보를 연결합니다.

 

2. 글쓴이, 시간, 내용 등을 mustache로 전달합니다.

 

3. /questions/{{id}}/form 경로를 통해 수정 페이지로 이동하게 합니다.

 

4. /questions/{{id}} 경로를 통해 삭제할 질문을 받아옵니다.

 

5. delete를 이용해서 삭제 기능을 구현합니다.

 

<!--updateForm.html-->

<div class="container" id="main">
   <div class="col-md-12 col-sm-12 col-lg-10 col-lg-offset-1">
      <div class="panel panel-default content-main">
      	{{#question}}
          <form name="question" method="post" action="/questions/{{id}}">	<!-- 질문하기 버튼을 클릭하면 questions라는 URL을 통해서 제목과 내용이 전달됨 -->
              <input type="hidden" name="_method" value="put" />	<!--1.-->
              <div class="form-group">
                  <label for="title">제목</label>
                  <input type="text" class="form-control" id="title" name="title" value="{{title}}" placeholder="제목"/>
              </div>
              <div class="form-group">
                  <label for="contents">내용</label>
                  <textarea name="contents" id="contents" rows="5" class="form-control">{{contents}}</textarea>
              </div>
              <button type="submit" class="btn btn-success clearfix pull-right">질문하기</button>
              <div class="clearfix" />
          </form>
          {{/question}}
        </div>
    </div>
</div>

1. put을 이용해서 질문을 등록할 수 있게 해줍니다.

 

//Question.java

@Entity
public class Question {	
	@Id
    @GeneratedValue
    private Long id;
    
    @ManyToOne	
    @JoinColumn(foreignKey=@ForeignKey(name="fk_question_writer"))	
    private User writer;	
    private String writer;
    private String title;
    private String contents;
    private LocalDateTime createDate;	
    
    public Question() {}	
    
    public Question(String writer, String title, String contents) {
    	super();
        this.writer=writer;
        this.title=title;
        this.contents=contents;
        this.createDate=LocalDateTime.now();	
    }
    
    public String getFormattedCreateDate() {	
    	if(createDate==null) {
        	return "";
        }
        return createDate.format(DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss"));
    }
    
    public void update(String title, String contents) {	//1.
    	this.title=title;
        this.contents=contents;
    }
}

1. 질문을 수정하는 update 메소드를 만들어줍니다.

 

//QuestionController.java

@Controller	
@RequestMapping("/questions")	
public class QuestionController {
	@Autowired	
    private QuestionRepository questionRepository;

	@GetMapping("/form")
    public String form(HttpSession session) {	
    	if(HttpSessionUtils.isLoginUser(session)) {
        	return "/users/loginForm";
        }
    	return "/qna/form";
    }
    
    @PostMapping("")
    public String create(String title, String contents, HttpSession session) {	
    	if(!HttpSessionUtils.isLoginUser(session)) {
        	return "/users/loginForm";
        }
        User sessionUser=HttpSessoinUtils.getUserFromSession(session);
        Question newQuestion=new Question(sessionUser, title, contents);		
        questionRepository.save(newQuestion);	
    	return "redirect:/";
    }
    
    @GetMapping("/{id}")
    public String show(@PathVariable Long id, Model model) {	
    	model.addAttribute("question", questionRepository.findById(id).get());
    	return "/qna/show";
    }
    
    @GetMapping("/{id}/form")
    public String updateForm(@PathVariable Long id, Model model) {	
    	return "/qna/updateForm";
    }
    
    @PutMapping("/{id}")	//2.
    public String update(@PathVariable Long id, String title, String contents) {	//1.
    	Question question=questionRepository.findById(id).get();
        question.update(title, contents);
        questionRepository.save(question);
    	return String.format("redirect:/questions/%d", id);
    }
    
    @DeleteMapping("/{id}")	//4.
    public String delete(@PathVariable Long id) {	//3.
    	questionRepository.delete(id);
        return "redirect:/";
    }
}

1. 수정한 내용을 갱신하고, questionRepository에  갱신한 내용을 저장한 후, 수정한 페이지를 리턴합니다.

 

2. 수정은 'put'을 이용합니다.

 

3. 삭제할 질문의 id를 받아와서 questionRepository에서 삭제합니다.

 

4. 삭제는 'delete'를 이용합니다.

 

 

반응형