logoStephen's 기술블로그

포스트 검색

제목, 태그로 포스트를 검색해보세요

#블로그만들기_09 #게시글 상세보기

#블로그만들기_09 #게시글 상세보기
SpringWebApp
성훈 김
2024년 2월 6일
목차

1️⃣ 프론트 확인

💡
🔹 Board 필요요소 - id, title, content 🔹 User 필요요소 - id, username 🔹 User의 id로 권한이라든지 세션이라든지 인증해야될 것들이 많기 때문에 보이지 않더라도 필요하다. → 그래서 비즈니스 이해가 중요하다.
notion image
 
 

2️⃣ 컨트롤러 작성

💡
🔹 URL에 테이블 명 뒤에 들어오는 값은 PK 또는 Unique이다. 🔹 나머지는 다 queryString이다.
notion image
 
 
💡
🔹 @GetMapping("/board/{id}")에서 사용된 {id}는 스프링 프레임워크(Spring Framework)에서의 "경로 변수(Path Variable)"라는 문법이다. 🔹 문법을 완성하기 위해서는 매개변수에 (@PathVariable 필요한 변수)를 넣어주면 된다. 🔹 board/ + 숫자 만들면 들어오는 숫자에 따라 자동으로 처리된다.
Java
@GetMapping("/board/{id}")
public String detail(@PathVariable int id) {
	    System.out.println("id : "+id); // PathVariable이 잘 작동하는 지 확인
	    return "board/detail";
}
💡
🔹 먼저 PathVariable이 잘 작동하는지 테스트한다.
notion image
 
 

3️⃣ 응답 DTO만들기

💡
🔹 아까 프론트에서 파악했던 요소들을 담아줄 DTO 객체를 만든다. 🔹 Board 필요요소 - id, title, content 🔹 User 필요요소 - id, username 🔹 조회된 데이터를 담고 상세보기 내부 게시판에 필요한 정보를 응답 받았기 때문에 BoardResponse 객체 안에 DetailDTO를 구현한다. 🔹 화면에 안보이더라도 userId 와 id 구해야된다.
Java
package shop.mtcoding.blog.board;

import lombok.Data;

public class BoardResponse {

    @Data
    public static class DetailDTO {
        private int id;
        private String title;
        private String content;
        private int userId;
        private String username;
    }
}
 
 

4️⃣findById()메소드 만들기

💡
🔹 board테이블과 user테이블의 요소가 혼합되어 필요하므로, inner join 쿼리문이 필요하다 🔹 1차원적으로 제일 기본기를 쌓기 위해서 아래와 같이 직접 파싱하도록 한다. 🔹 join할꺼라면 *사용하면 안된다. 🔹 스트링 짤때 바로 자바 콘솔에다가 적지말고 H2에서 시험해보고 작동하는 지 확인 먼저한다.
notion image
Java
public BoardResponse.DetailDTO findById(int idx) {
		// 이 코드를 사용하려면 의존성주입이 되어야 한다. EntityManager
    Query query = em.createNativeQuery("select b.id, b.title, b.content, b.user_id, u.username from board_tb b inner join user_tb u on b.user_id = u.id where b.id = ?");
    query.setParameter(1, idx);
		
		// 객체 각 요소들을 배열로 받는다. 한 행의 요소로 배열로 들어간다.
    Object[] row = (Object[]) query.getSingleResult();
		
		// H2에 받은 결과값을 그대로 확인하고 타입과 순서를 모두 지켜줘야 된다. 1차원적 설계
    Integer id = (Integer) row[0];
    String title = (String) row[1];
    String content = (String) row[2];
    int userId = (Integer) row[3];
    String username = (String) row[4];
		
		//확인용 코드
    System.out.println("id : "+ id);
    System.out.println("title : "+ title);
    System.out.println("content : "+ content);
    System.out.println("userId : "+ userId);
    System.out.println("username : "+ username);

		//객체를 생성하여서 DetailDTO로 옮겨담는 작업
    BoardResponse.DetailDTO responseDTO = new BoardResponse.DetailDTO();
    responseDTO.setId(id);
    responseDTO.setTitle(title);
    responseDTO.setContent(content);
    responseDTO.setUserId(userId);
    responseDTO.setUsername(username);

    return responseDTO;
}
 
 

🔹 테스트 하기

💡
🔹 아래 주소 입력전에 아래 코드를 BoardContoller >> deatil 메소드 내부에 구현하고 테스트해본다.
Java
@GetMapping("/board/{id}")
    public String detail(@PathVariable int id) {
				 // 테스트용 코드
        boardRepository.findById(id);  
        return "board/detail";
    }
}
Java
// 테스트용 URL 주소
http://localhost:8080/board/1
notion image
 
 

5️⃣ 화면에 렌더링 하기

💡
🔹 HttpServletRequest 을 통해 요청가방을 들고다닌다 생각하면 좋다. 정보를 담아서 브라우저까지 가져다 주는 객체이다. 🔹 가지고 브라우저로 다시 나가기 때문에 response라고 해야되는 거 아닌 싶지만, 이 request가방은 클라이언트가 들어올 때부터 가지고 들어온 가방이라 생각하자. 🔹
Java
@GetMapping("/board/{id}")
    public String detail(@PathVariable int id, HttpServletRequest request) {
        
        // 모델에게 위임하고 반환된 값을 responseDTO에 저장
        BoardResponse.DetailDTO responseDTO = boardRepository.findById(id);
       
        // request가방에 담아서 화면에 렌더링할 수 있게해준다.
        // 아래 코드 예시로는 mustache에서 {{board}} 같이 문법을 쓰면 정보를 가져다 쓸 수 있다. 
        request.setAttribute("board", responseDTO);
        return "board/detail";
}
 
 
💡
🔹 detail.mustache 문서로 가서 아래 부분을 변수화 시킬 수 있는 mustache 문법이 필요하다. 🔹 request.setAttribute 로 지정된 키워드를 사용해서 동적으로 연결 할 수 잇다. 🔹 request.setAttribute("board", responseDTO); → 이렇게 지정해서 board테이블의 username을 전달하고 싶다면 mustache 문서에 {{board.username}} 로 표시하면 렌더링이된다.
notion image
 
 

6️⃣ 테스트 확인

💡
각 게시물마다 바뀌는 사용자와 제목/내용을 확인 할 수 있다.
notion image
notion image
 
 

🔹 detail.mustache 전체코드

Java
{{> /layout/header}}

<div class="container p-5">

    <!-- 수정삭제버튼 -->
    <div class="d-flex justify-content-end">
        <button class="btn btn-warning me-1">수정</button>
        <button class="btn btn-danger">삭제</button>
    </div>
    <div class="d-flex justify-content-end">
        <b>작성자</b> : {{board.username}}
    </div>

    <!-- 게시글내용 -->
    <div>
        <h2><b>{{board.title}}</b></h2>
        <hr/>
        <div class="m-4 p-2">
            {{board.content}}
        </div>
    </div>

    <!-- 댓글 -->
    <div class="card mt-3">
        <!-- 댓글등록 -->
        <div class="card-body">
            <form action="/reply/save" method="post">
                <textarea class="form-control" rows="2" name="comment"></textarea>
                <div class="d-flex justify-content-end">
                    <button type="submit" class="btn btn-outline-primary mt-1">댓글등록</button>
                </div>
            </form>
        </div>

        <!-- 댓글목록 -->
        <div class="card-footer">
            <b>댓글리스트</b>
        </div>
        <div class="list-group">
            <!-- 댓글아이템 -->
            <div class="list-group-item d-flex justify-content-between align-items-center">
                <div class="d-flex">
                    <div class="px-1 me-1 bg-primary text-white rounded">cos</div>
                    <div>댓글 내용입니다</div>
                </div>
                <form action="/reply/1/delete" method="post">
                    <button class="btn">🗑</button>
                </form>
            </div>
            <!-- 댓글아이템 -->
            <div class="list-group-item d-flex justify-content-between align-items-center">
                <div class="d-flex">
                    <div class="px-1 me-1 bg-primary text-white rounded">ssar</div>
                    <div>댓글 내용입니다</div>
                </div>
                <form action="/reply/1/delete" method="post">
                    <button class="btn">🗑</button>
                </form>
            </div>
        </div>
    </div>
</div>

{{> /layout/footer}}
 
 
 

📝
추가 노트.. 🔹 URL 테이블 명 뒤에 들어오는 값은 PK or Unique 나머지는 다 QueryString 🔹 주소 들어가는 모든 데이터는 where절에 들어간다. 🔹 서로가 알고 있는 표준을 잘 지켜줘야 된다. 🔹 바디데이터가 없으면 유효성 검사는 없다. 🔹 파일에 맞는 DTO를 생성하면 직관적이고 편하다. 두번 생각하지 않아도 된다.