최원종의 개발 블로그

V3-7 게시글 쓰기(로그인한 사용자와 게시글 연결) 본문

Spring boot 입문

V3-7 게시글 쓰기(로그인한 사용자와 게시글 연결)

chl6698 2026. 5. 13. 17:22

게시글 글쓰기 화면


save-form.mustache파일

{{> layout/header}}
<div class="container p-5 flex-grow-1">
    <div class="card">
        <div class="card-header"><b>글쓰기 화면입니다</b></div>
        <div class="card-body">
            <form action="/board/save" method="post">
                <div class="mb-3">
                    <input type="text" name="title" class="form-control" placeholder="enter title">
                </div>
                <div class="mb-3">
                    <textarea type="text" name="content" rows="5" class="form-control" placeholder="enter content"></textarea>
                </div>
                <button class="btn btn-primary">글쓰기 완료</button>
            </form>
        </div>
    </div>
</div>

{{> layout/footer}}

 


BoardPersistRepository 코드

    // 게시글 저장
    @Transactional
    public Board save(Board board) {
        em.persist(board); // insert 처리 완료
        return board;
    }

 


BoardRequest 주요 메서드 상세 분석

toEntity(User user) - 객체 변환의 기술
역할: DTO에 담긴 정보를 바탕으로 DB에 저장할 Board 엔티티 객체를 생성함.

특징: 게시글은 반드시 작성자 정보가 필요하므로,
파라미터로 User 객체를 전달받아 연관관계를 맺어주는 것이 핵심.

장점: 서비스 레이어나 컨트롤러에서 복잡한 빌더 코드를 작성할 필요 없이
saveDTO.toEntity(sessionUser) 한 줄로 깔끔하게 처리가 가능.

② validate() - 데이터 무결성 보장
제목 검증: null 체크 및 공백 제거(trim()) 후 필수 입력 여부를 확인함.

내용 검증: 단순 존재 여부를 넘어 length() < 3 조건을 통해 최소 글자 수 제한 로직을 포함함.

BoardRequest 코드

package com.tenco.blog.board;

import com.tenco.blog.user.User;
import lombok.Builder;
import lombok.Data;

// 요청 데이터를 담는 DTO 클래스
// 컨트롤러.. 비즈니스 .. 데이터 계층 사이에서 데이터 전송 역할 객체
public class BoardRequest {

    @Data
    @Builder
    public static class SaveDTO {

        private String title;
        private String content;

        // 편의 기능 설계 가능
        // DTO 에서 Entity로 변환해주는 편의 메서드
        public Board toEntity(User user) {
            return Board.builder()
                    .title(title)
                    .user(user)
                    .content(content)
                    .build();
        }
        
        public void validate() {
            if(title == null || title.trim().isEmpty()) {
                throw new IllegalArgumentException("제목은 필수입니다");
            }
            if(content == null || content.length() < 3) {
                throw new IllegalArgumentException("내용은 3글자 이상 작성해야 합니다.");
            }
        }
    }

    // 내부 정적 클래스 게시글 수정 DTO 설계
    @Data
    public static class UpdateDTO {
        private String username;
        private String title;
        private String content;

        // 게시글 수정시 유효성 검사 편의 메서드
        public void validate() {
            if(username == null ||username.trim().isEmpty()) {
                throw new IllegalArgumentException("작정자 이름은 필수입니다");
            }
            if(title == null || title.trim().isEmpty()) {
                throw new IllegalArgumentException("제목은 필수입니다");
            }
            if(content== null || content.length() < 3) {
                throw new IllegalArgumentException("내용은 3글자 이상 작성해야 합니다.");
            }
        }

    }

}

 

BoardController 주요 메서드 분석

saveForm (GET): 작성 화면 제공
역할: 로그인된 사용자에게만 글쓰기 양식(board/save-form)을 보여줌.

흐름: 요청 접수 → 세션 확인 → (성공 시) 뷰 반환 / (실패 시) 로그인 페이지로 이동.

② saveProc (POST): 작성 기능 수행
사용자가 입력한 데이터를 실제로 DB에 저장하는 핵심 로직.

세션 재확인: 보안을 위해 데이터를 서버에 전송하는 시점에도 한 번 더 로그인 여부를 체크.

데이터 검증 (validate): 제목이나 내용이 비어있지 않은지 DTO 내부 로직으로 검사.

엔티티 변환 (toEntity): DTO 데이터를 엔티티로 바꾸되,
세션에서 가져온 sessionUser 정보를 함께 넘겨 "누가 썼는지" 연결해 줌.

예외 처리 (try-catch): 저장 중 오류가 발생하면 에러 메시지를 출력하고,
다시 작성 폼 페이지로 돌려보내 사용자가 내용을 수정할 수 있게 함.

 


BoardController 코드

/**
     * 게시글 작성 화면 요청
     * @return 페이지 반환
     * 주소설계 : http://localhost:8080/board/save-form
     */
    @GetMapping("/board/save-form")
    public String saveForm(HttpSession httpSession) {
       // 로그인 여부 체크 - 즉 로그인 한 사용자만 이 페이지 안에 들어 올 수 있음.
       // 1. 인증 검사  
       User sessionUser =  (User)httpSession.getAttribute("sessionUser");
       if(sessionUser == null) {
           return "redirect:/login-form";
       }

        return "board/save-form";
    }

    /**
     * 게시글 작성 기능 요청
     * @return 페이지 반환
     * 주소설계 : http://localhost:8080/board/save-form
     */
    @PostMapping("/board/save")
    // 사용자 요청 -> HTTP 요청 메시지(Post)
    public String saveProc(BoardRequest.SaveDTO saveDTO, HttpSession session) {

        log.info("=== 게시글 저장 요청 ===");
        // 이 요청 시 사용자가 로그인을 했다면 로그인 정보를 세션 메모리에서 가져오면 된다.
        // 1. 세션에서 로그인한 사용자 정보 가져오기
        User sessionUser = (User) session.getAttribute("sessionUser");
        // 2. 로그인 여부 확인
        if(sessionUser == null) {
            return "redirect:/login-form";
        }

        try {
            // 3. 로그인 된 사용자
            // 3.1 유효성 검사
            saveDTO.validate();
            Board board = saveDTO.toEntity(sessionUser);
            boardPersistRepository.save(board);
            return "redirect:/";
        } catch (Exception e) {
            System.out.println("에러 발생 : " + e.getMessage());
            return "board/save-form";
        }

    }

핵심 학습 포인트 정리

 

1. 세션 사용자 정보 활용

// 세션에서 로그인한 사용자 가져오기
// 로그인 여부 체크 - 즉 로그인 한 사용자만 이 페이지 안에 들어 올 수 있음.
// 1. 인증 검사
User sessionUser =  (User)httpSession.getAttribute("sessionUser");

// 게시글과 사용자 연관관계 설정
public Board toEntity(User user) {
    return Board.builder()
            .title(title)
            .user(user)
            .content(content)
            .build();
}

 

 

2. 연관관계 저장의 자동화

// Board 저장 시 user_id 외래키 자동 설정
// User 객체의 ID가 board_tb.user_id 컬럼에 저장됨
em.persist(board);