Day 43 - Spring Boot pt.3
이 글은 2026년 04월 30일 작성된 글입니다.
오늘은 답변 등록 기능, Bootstrap 화면 구성, 질문 등록 기능과 validation 처리까지 정리했다.
1. 답변 등록 폼 추가
질문 상세 페이지에서 답변을 입력할 수 있도록 폼을 추가했다.
<form action="/answer/create/" method="post">
<textarea name="content" id="content" rows="15"></textarea>
<input type="submit" value="답변등록">
</form>처음에는 답변 내용만 서버로 보내는 구조로 시작했다.
- textarea로 답변 내용 입력
- POST 방식으로 답변 데이터 전송
2. 질문과 답변 연결
답변은 특정 질문에 달려야 하므로, 질문 id를 URL에 포함해서 전달하도록 수정했다.
<form th:action="@{|/answer/create/${question.id}|}" method="POST">
<textarea name="content" cols="30" rows="10"></textarea>
<input type="submit" value="답변등록">
</form>이렇게 하면 서버는 어떤 질문에 대한 답변인지 알 수 있다.
- 질문 id를 path variable로 전달
- 답변과 질문을 연결
3. 답변 목록 출력
질문 상세 페이지에서 해당 질문에 달린 답변 목록을 출력했다.
<h5 th:text="|${#lists.size(question.answerList)}개의 답변이 있습니다.|"></h5>
<ul>
<li th:each="answer : ${question.answerList}" th:text="${answer.content}"></li>
</ul>question.answerList를 사용하면
질문에 연결된 답변들을 화면에 출력할 수 있다.
- 답변 개수 출력
- 답변 목록 반복 출력
4. Bootstrap으로 화면 꾸미기
기본 HTML 화면에 Bootstrap을 적용해서 화면을 더 보기 좋게 구성했다.
<input type="submit" value="답변등록" class="btn btn-primary">Bootstrap을 사용하면 직접 CSS를 많이 작성하지 않아도 버튼, 폼, 여백 등을 빠르게 정리할 수 있다.
- 버튼 스타일 적용
- 폼 디자인 개선
- 화면 가독성 향상
5. 표준 HTML 구조 적용
페이지 구조를 더 명확하게 만들기 위해 표준 HTML 구조로 변경했다.
<html>
<head>
<title>질문 게시판</title>
</head>
<body>
<main>
<!-- content -->
</main>
</body>
</html>기본 구조를 잡아두면 이후 공통 레이아웃을 적용하기도 쉬워진다.
6. 질문 등록 폼 추가
새 질문을 작성할 수 있도록 질문 등록 폼을 만들었다.
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container">
<h5 class="my-3 border-bottom pb-2">질문등록</h5>
<form th:action="@{/question/create}" method="post">
<div class="mb-3">
<label for="subject" class="form-label">제목</label>
<input type="text" name="subject" id="subject" class="form-control">
</div>
<div class="mb-3">
<label for="content" class="form-label">내용</label>
<textarea name="content" id="content" class="form-control" rows="10"></textarea>
</div>
<input type="submit" value="저장하기" class="btn btn-primary my-2">
</form>
</div>
</html>질문 등록 폼에서는 제목과 내용을 입력받는다.
- 제목 입력
- 내용 입력
- POST 방식으로 서버 전송
7. 질문 등록 구현
폼에서 전달된 제목과 내용을 받아 질문 데이터를 저장하도록 구현했다.
흐름은 다음과 같다.
- 사용자가 질문 등록 폼 작성
- submit
- Controller에서 요청 처리
- Service를 통해 Question 저장
- 저장 후 목록 또는 상세 페이지로 이동
8. Validation 의존성 추가
질문 등록 시 빈 값이 들어오지 않도록 validation을 적용했다.
implementation("org.springframework.boot:spring-boot-starter-validation")validation을 적용하면 폼 데이터가 서버에 들어온 뒤 조건에 맞는지 검사할 수 있다.
- 제목 필수 입력
- 내용 필수 입력
- 잘못된 요청 방지
9. 질문 등록 폼 에러 처리
폼에서 에러가 발생했을 때 사용자에게 에러 메시지를 보여주도록 처리했다.
<form th:action="@{/question/create}" th:object="${questionForm}" method="post">
<div class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}">
<div th:each="err : ${#fields.allErrors()}" th:text="${err}"></div>
</div>
<input type="text" th:field="*{subject}" class="form-control">
<textarea th:field="*{content}" class="form-control" rows="10"></textarea>
<input type="submit" value="저장하기" class="btn btn-primary my-2">
</form>th:object와 th:field를 사용하면
폼 객체와 입력 필드를 자연스럽게 연결할 수 있다.
- 에러 메시지 출력
- 입력 필드와 Form 객체 연결
- 사용자 입력 검증
10. 답변 등록 폼 validation 적용
답변 등록 폼에도 validation을 적용했다.
<form th:action="@{|/answer/create/${question.id}|}" th:object="${answerForm}" method="post" class="my-3">
<div class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}">
<div th:each="err : ${#fields.allErrors()}" th:text="${err}"></div>
</div>
<textarea th:field="*{content}" rows="10" class="form-control"></textarea>
<input type="submit" value="답변등록" class="btn btn-primary my-2">
</form>질문 등록과 답변 등록 모두 폼 객체를 기준으로 유효성 검사를 처리하는 구조가 되었다.
11. 공통 템플릿 적용
여러 화면에서 반복되는 레이아웃을 공통 템플릿으로 분리했다.
<html layout:decorate="~{layout}">
<div layout:fragment="content">
<!-- 페이지별 내용 -->
</div>
</html>공통 레이아웃을 사용하면 전체 페이지 구조를 일관성 있게 유지할 수 있다.
- 중복 코드 감소
- 화면 구조 통일
- 유지보수 편의성 증가
✅ 정리
- 답변 등록 기능은 질문 id와 함께 요청을 보내야 특정 질문에 연결할 수 있다.
- Thymeleaf의
th:action,th:object,th:field를 사용하면 폼 처리가 훨씬 깔끔해진다. - Bootstrap을 적용하면 화면을 빠르게 정리하고 가독성을 높일 수 있다.
- Validation을 적용하면 잘못된 입력을 서버에서 안정적으로 막을 수 있다.
- 공통 템플릿을 사용하면 여러 페이지의 레이아웃을 일관되게 관리할 수 있다.