공부 내용을 정리하고 앞으로의 학습에 이해를 돕기 위해 작성합니다.
변경사항
로그인 페이지를 만들기에 앞서, 기존 코드의 수정사항과 추가된 코드에 대한 리뷰를 먼저 진행한다.
변경사항: ERD 반영
새로운 UserAccount 도메인을 반영하여 ERD를 업데이트하였다.
변경사항: AuditingFields 클래스 변경
- 변경 내용: AuditingFields 클래스를 추상 클래스로 변경
- 목적: 엔티티에서 상속을 통해 사용하기 위함
- 설명: AuditingFields 클래스는 엔티티의 생성일시, 생성자, 수정일시, 수정자를 자동으로 관리하기 위한 클래스이다. 이 클래스를 추상 클래스로 변경하여 엔티티 클래스들이 이를 상속받아 사용하도록 하였다.
package org.example.projectboard.domain;
import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import lombok.ToString;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Getter
@ToString
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class AuditingFields { // 추상 클래스 변경
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
@CreatedDate
@Column(nullable = false, updatable = false)
private LocalDateTime createdAt; // 생성일시
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
@CreatedBy
@Column(nullable = false, updatable = false, length = 100)
private String createdBy; // 생성자
@LastModifiedDate
@Column(nullable = false)
private LocalDateTime modifiedAt; // 수정일시
@LastModifiedBy
@Column(nullable = false, length = 100)
private String modifiedBy; // 수정자
}
추가사항: 회원 계정 도메인 구현
- 추가 내용: UserAccount 도메인 생성 및 ERD 반영
- 목적: 회원 계정을 관리하기 위한 도메인 생성
- 설명: UserAccount 클래스는 사용자 계정 정보를 관리하는 엔티티이다. MySQL 예약어와의 충돌을 피하기 위해 필드명을 주의하여 지정하였다. 이 클래스는 AuditingFields 클래스를 상속받아 생성일시, 생성자, 수정일시, 수정자를 자동으로 관리한다.
package org.example.projectboard.domain;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.Objects;
@Getter
@ToString
@Table(indexes = {
@Index(columnList = "userId"),
@Index(columnList = "email", unique = true),
@Index(columnList = "createdAt"),
@Index(columnList = "createdBy")
})
@Entity
public class UserAccount extends AuditingFields {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Setter @Column(nullable = false, length = 50) private String userId;
@Setter @Column(nullable = false) private String userPassword;
@Setter @Column(length = 100) private String email;
@Setter @Column(length = 100) private String nickname;
@Setter private String memo;
protected UserAccount() {}
private UserAccount(String userId, String userPassword, String email, String nickname, String memo) {
this.userId = userId;
this.userPassword = userPassword;
this.email = email;
this.nickname = nickname;
this.memo = memo;
}
public static UserAccount of(String userId, String userPassword, String email, String nickname, String memo) {
return new UserAccount(userId, userPassword, email, nickname, memo);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof UserAccount userAccount)) return false;
return id != null && id.equals(userAccount.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
추가사항: 회원 계정 레포지토리 생성
- 추가 내용: UserAccountRepository 인터페이스 생성
- 목적: 회원 계정 데이터를 데이터베이스에서 관리하기 위함
- 설명: UserAccountRepository 인터페이스는 JpaRepository를 상속받아 UserAccount 엔티티에 대한 CRUD 연산을 처리할 수 있도록 한다.
package org.example.projectboard.repository;
import org.example.projectboard.domain.UserAccount;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserAccountRepository extends JpaRepository<UserAccount, Long> {
}
변경사항: 테스트 데이터 추가
- 변경 내용: 테스트 데이터를 위한 SQL 파일 수정
- 목적: 테스트용 계정 데이터를 데이터베이스에 삽입하기 위함
- 설명: 테스트 데이터를 삽입하는 SQL 파일에 임의의 계정 데이터를 추가하였다. 패스워드가 노출되는 방식이므로 개선이 필요하다.
-- 테스트 계정
-- TODO: 테스트용이지만 비밀번호가 노출된 데이터 세팅. 개선하는 것이 좋을 지 고민해 보자.
insert into user_account (user_id, user_password, nickname, email, memo, created_at, created_by, modified_at, modified_by) values
('eunchan', 'asdf1234', 'Eunchan', 'eunchan@mail.com', 'I am Eunchan.', now(), 'eunchan', now(), 'eunchan');
변경사항: 회원 관련 API 테스트
- 변경 내용: 회원 관련 API가 제공되지 않는지 확인하는 테스트 추가
- 목적: 회원 계정 정보가 API로 노출되지 않도록 하기 위함
- 설명: 모든 HTTP 메소드에 대해 회원 계정 API가 404 Not Found를 반환하는지 확인하는 테스트를 추가하였다.
@Disabled("Spring Data REST 통합테스트는 불필요하므로 제외시킴")
@DisplayName("Data REST -API 테스트")
@Transactional
@AutoConfigureMockMvc
@SpringBootTest
public class DataRestTest {
private final MockMvc mvc;
// ...
@DisplayName("[api] 회원 관련 API 는 일체 제공하지 않는다.")
@Test
void givenNothing_whenRequestingUserAccounts_thenThrowsException() throws Exception {
// Given
// When & Then
mvc.perform(get("/api/userAccounts")).andExpect(status().isNotFound());
mvc.perform(post("/api/userAccounts")).andExpect(status().isNotFound());
mvc.perform(put("/api/userAccounts")).andExpect(status().isNotFound());
mvc.perform(patch("/api/userAccounts")).andExpect(status().isNotFound());
mvc.perform(delete("/api/userAccounts")).andExpect(status().isNotFound());
mvc.perform(head("/api/userAccounts")).andExpect(status().isNotFound());
}
}
로그인 페이지 만들기
로그인 페이지를 구현하는 과정을 단계별로 설명한다.
1. 의존성 추가
로그인 기능을 구현하기 위해 필요한 의존성을 추가한다. Spring Security와 Thymeleaf Extras를 사용하여 뷰를 간편하게 구성한다.
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
- spring-boot-starter-security
- Spring Security 기능을 사용할 수 있게 하는 의존성이다.
- thymeleaf-extras-springsecurity6
- Thymeleaf 템플릿 엔진과 Spring Security를 통합하여, 뷰에서 보안 관련 기능을 간편하게 사용할 수 있게 해준다.
2. SecurityConfig 클래스 생성
현재 상태에서는 아래 사진과 같이 아무 작업도 할 수 없으므로, Spring Security 설정을 위한 SecurityConfig 클래스를 생성한다.
package org.example.projectboard.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
.formLogin(Customizer.withDefaults()) // Deprecated된 formLogin() 메서드 대신 사용
.build();
}
}
- @Configuration
- 이 클래스가 Spring 설정 클래스임을 나타낸다.
- securityFilterChain(HttpSecurity http)
- Spring Security의 필터 체인을 설정하는 메서드다.
- authorizeHttpRequests
- 모든 HTTP 요청을 허용한다.
- formLogin(Customizer.withDefaults())
- 기본 로그인 폼 설정을 적용한다.
이제 정상적으로 로그인 페이지에 접근할 수 있다.
3. 로그인 페이지 테스트 추가
로그인 페이지가 정상적으로 호출되는지 테스트를 추가한다.
package org.example.projectboard.controller;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@DisplayName("View 컨트롤러 - 인증")
@WebMvcTest
public class AuthControllerTest {
private final MockMvc mvc;
AuthControllerTest(@Autowired MockMvc mvc) {
this.mvc = mvc;
}
@DisplayName("[view][GET] 로그인 페이지 - 정상 호출")
@Test
public void givenNothing_whenTryingToLogin_thenReturnsLogInView() throws Exception {
// given
// when & then
mvc.perform(get("/login"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML));
}
}
- @WebMvcTest
- MVC 테스트를 위한 어노테이션으로, Spring MVC 컴포넌트만 로드한다.
- @Import(SecurityConfig.class)
- 테스트에 필요한 SecurityConfig 설정을 임포트 한다.
- MockMvc
- Spring MVC 테스트를 위한 클래스로, HTTP 요청 및 응답 테스트를 수행할 수 있다.
- mvc.perform(get("/login"))
- /login URL에 GET 요청을 보낸다.
- andExpect(status().isOk())
- HTTP 상태 코드 200(OK)이 반환되는지 확인한다.
- andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML))
- 응답의 컨텐츠 타입이 text/html인지 확인한다.
- andExpect(view().name("login"))
- 반환된 뷰의 이름이 login인지 확인한다.
모든 테스트가 정상적으로 통과했음을 확인한다.
프로젝트 변경 사항을 GitHub에 푸시하고 프로젝트 보드에서 해당 작업을 완료로 표시한다.
정리
1. Spring Security 설정
- SecurityConfig 클래스를 통해 기본적인 보안 설정을 구성하였다.
2. Thymeleaf 통합
- Thymeleaf Extras를 활용하여 뷰에서 보안 기능을 간편하게 통합하였다.
3. 테스트 작성
- MockMvc를 활용하여 로그인 페이지 및 게시글 페이지에 대한 테스트를 작성하고 성공적으로 통과하였다.
4. 프로젝트 관리
- GitHub 프로젝트 보드를 활용하여 작업 진행 상황을 체계적으로 관리하였다.
이번 로그인 기능을 구현하면서 Spring Security의 기본적인 설정과 Thymeleaf를 활용한 뷰 통합을 경험할 수 있었다.
로그인 페이지를 통해 사용자 인증 절차를 구현하고, 이를 테스트를 통해 검증함으로써 애플리케이션의 보안 기능을 강화했다.
'BackEnd > Project' 카테고리의 다른 글
[Board] Ch03. 게시판 페이지 기능 테스트 정의 (0) | 2024.08.09 |
---|---|
[Board] Ch02. 뷰에 디자인 붙이기 (0) | 2024.08.08 |
[Board] Ch02. 게시글 페이지 만들기 (0) | 2024.08.08 |
[Board] Ch02. 게시판 페이지 만들기 (0) | 2024.08.08 |
[Board] Ch02. 뷰 엔드포인트 테스트 정의 (0) | 2024.08.08 |