Day 48 - Spring Security
이 글은 2026년 05월 11일 작성된 글입니다.
오늘은 Thymeleaf 기반 폼 처리 개선, 게시글 목록과 상세 리다이렉트, 그리고 Spring Security와 회원가입 기능까지 정리했다.
1. Thymeleaf 도입
기존에는 컨트롤러에서 HTML을 직접 만들거나 문자열을 조립하는 방식에 가까웠지만, 이제는 Thymeleaf 템플릿 파일을 사용해서 화면을 분리했다.
<form action="/posts/write" method="post">
<input type="text" name="title">
<textarea name="content"></textarea>
<button type="submit">등록</button>
</form>- Controller는 요청을 처리한다.
- Thymeleaf는 화면을 렌더링한다.
- 역할이 더 명확해진다.
2. th:text와 th:utext
Thymeleaf에서 값을 출력할 때는 th:text와 th:utext를 사용할 수 있다.
| 문법 | 설명 |
|---|---|
th:text | HTML 특수문자를 이스케이프해서 출력 |
th:utext | HTML 태그를 해석해서 출력 |
<span th:text="${user.name}"></span>
<span th:utext="${htmlContent}"></span>일반 텍스트는 th:text를 사용하는 것이 안전하다.
th:utext는 HTML을 그대로 렌더링하므로 신뢰할 수 있는 데이터에만 사용해야 한다.
3. GET 요청에서도 Form 객체가 필요한 이유
글 작성 페이지에 처음 들어올 때도 폼 객체가 필요하다.
@GetMapping("/posts/write")
public String showWrite(WriteForm form) {
return "post/write";
}Thymeleaf에서 form.title 같은 값을 사용하려면
처음 화면을 보여줄 때도 form 객체가 존재해야 한다.
- GET 요청에서는 빈 form 객체가 필요하다.
- POST 실패 시에는 사용자가 입력한 form 객체가 필요하다.
4. writeForm?.title 과 writeForm.title
Thymeleaf에서는 null 안전 접근을 위해 ?.를 사용할 수 있다.
<input th:value="${writeForm?.title}">| 표현식 | 특징 |
|---|---|
writeForm?.title | writeForm이 null이어도 에러를 막는다. |
writeForm.title | writeForm이 null이면 에러가 발생할 수 있다. |
초기 GET 요청에서 form 객체가 없을 수 있다면 ?.를 쓰면 안전하다.
하지만 form 객체를 항상 모델에 넣는 구조라면 직접 접근해도 된다.
5. @ModelAttribute("form")
폼 객체 이름을 명확하게 지정하기 위해 @ModelAttribute("form")을 사용했다.
public String write(@ModelAttribute("form") WriteForm form) {
return "post/write";
}이렇게 하면 Thymeleaf에서 writeForm 대신 form이라는 이름으로 접근할 수 있다.
<input th:value="${form.title}">6. POST URL 정리
처음에는 작성 처리 URL을 따로 두었다.
POST /posts/doWrite하지만 POST 자체에 이미 생성이라는 의미가 있기 때문에 다음처럼 정리하는 것이 더 자연스럽다.
GET /posts/write
POST /posts/write같은 URL이라도 HTTP 메서드가 다르면 스프링은 서로 다른 액션으로 구분할 수 있다.
- GET
/posts/write는 작성 폼을 출력한다. - POST
/posts/write는 작성 처리를 담당한다.
7. th:object와 th:field
폼 관련 중복을 줄이기 위해 th:object와 th:field를 사용했다.
<form th:object="${form}" method="post">
<input th:field="*{title}">
<textarea th:field="*{content}"></textarea>
</form>th:object를 사용하면 내부에서 form. 접두어를 생략할 수 있다.
th:field는 다음 속성들을 자동으로 처리해준다.
- name
- id
- value
8. #fields로 에러 메시지 출력
Validation 실패 시 Thymeleaf에서 에러 메시지를 출력했다.
<div th:if="${#fields.hasAnyErrors()}">
<div th:each="err : ${#fields.allErrors()}" th:text="${err}"></div>
</div>서버에서 검증한 결과를 사용자에게 다시 보여줄 수 있다.
- 검증 실패 메시지를 출력할 수 있다.
- 폼 화면을 유지할 수 있다.
- 사용자 입력 흐름이 더 자연스러워진다.
9. 등록 후 상세 페이지로 리다이렉트
게시글 등록이 끝나면 목록이 아니라 생성된 게시글의 상세 페이지로 이동하도록 처리했다.
return "redirect:/posts/" + post.getId();등록 결과를 바로 확인할 수 있어서 사용자 흐름이 더 자연스럽다. HTTP 응답 코드 기준으로 보면 리다이렉트는 3xx 응답에 해당한다.
10. 게시글 목록 구현
게시글 목록 페이지를 구현했다.
<tr th:each="post : ${posts}">
<td th:text="${post.id}"></td>
<td>
<a th:href="@{|/posts/${post.id}|}" th:text="${post.title}"></a>
</td>
</tr>목록에서 상세 페이지로 이동할 수 있도록 링크도 함께 연결했다.
11. Member 도메인 추가
회원 기능을 위해 Member 도메인을 추가했다.
- Member 엔티티
- MemberRepository
- MemberService
게시글 기능과 마찬가지로 회원 기능도 도메인 단위로 분리해서 관리한다.
12. Spring Security 의존성 추가
회원 기능과 인증 처리를 위해 Spring Security를 추가했다.
implementation("org.springframework.boot:spring-boot-starter-security")Security를 추가하면 기본적으로 인증/인가 기능이 적용된다.
13. SecurityConfig 설정
Security 설정을 직접 구성하기 위해 SecurityConfig를 만들었다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
}기본 보안 설정을 프로젝트에 맞게 수정하기 위한 단계이다.
14. h2-console 접근 허용
Spring Security가 적용되면 h2-console 접근도 막힐 수 있다. 그래서 개발 중에는 h2-console에 접근할 수 있도록 SecurityConfig에서 허용했다.
- 개발 편의성을 확보할 수 있다.
- DB 상태를 브라우저에서 확인할 수 있다.
15. 회원가입 기능 구현
회원가입 기능을 구현하고 테스트 회원 3명을 생성했다.
회원가입에서는 다음 값을 받는다.
- username
- password
- nickname 또는 name
16. 회원가입 폼 구현
회원가입 입력 화면을 만들었다.
<form th:object="${form}" method="post">
<input th:field="*{username}">
<input th:field="*{password}" type="password">
<input th:field="*{nickname}">
<button type="submit">회원가입</button>
</form>17. 회원가입 Validation
회원가입 폼에도 validation을 적용했다.
@NotBlank
private String username;
@NotBlank
private String password;빈 값이나 잘못된 값이 들어오지 않도록 서버에서 검증했다.
18. 회원가입 데이터 검증
단순히 빈 값만 확인하는 것이 아니라 회원가입 처리 과정에서 필요한 데이터 검증도 추가했다.
예를 들면 다음과 같은 검증이 필요하다.
- 아이디 중복 여부
- 비밀번호 조건
- 필수 입력값 확인
19. 회원가입 검증 리팩토링
회원가입 검증 로직을 정리하여 컨트롤러가 너무 복잡해지지 않도록 개선했다.
검증과 저장 로직을 적절히 분리하면 회원 기능이 커져도 유지보수하기 쉬워진다.
✅ 정리
- Thymeleaf를 사용하면서 컨트롤러와 화면의 역할을 더 명확하게 나눌 수 있었다.
th:object와th:field를 사용하면 폼 코드를 훨씬 간결하게 작성할 수 있다.- Validation과
#fields를 함께 사용하면 사용자에게 에러 메시지를 자연스럽게 보여줄 수 있다. - GET과 POST를 같은 URL로 두고 메서드로 구분하면 REST 흐름에 더 가까운 구조가 된다.
- Spring Security를 추가하면서 회원가입과 인증 기능을 위한 기반을 만들 수 있었다.