[1부 계정 관리] 프로젝트 만들기, 계정 도메인, 회원 가입 컨트롤러

yuri·2020년 10월 8일

프로젝트 만들기

(1)sts, java version 8, maven, jar을 사용 (추후에 java 11을 사용하지 않으면 에러가 발생하는 것을 발견함)

(2)인텔리제이, java version 11, maven, jar 사용

Spring Configuration Processor: application.properties 파일에다가 에다가 원하는 커스터마이징, 추출해서 사용할 수 있음

  • id: user
  • Using generated security password: 396b78b5-65a9-4a0d-8a17-4a1f95020b53

app을 실행시켰을 때 기본적으로 스프링 시큐리티가 작동하므로다음과 같은 페이지가 나온다.

Account 도메인 클래스

  • 로그인
  • 프로필
  • 알림설정

팀이 커지면 모듈화 해도 되지만, 지금은 혼자 개발하는 것이므로 패스 😀

package com.goodmoim.domain;

import lombok.*;

import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Getter @Setter @EqualsAndHashCode(of = "id") //id만 사용하도록 함 
public class Account {

    @Id @GeneratedValue
    private Long id;

    @Column(unique = true)
    private String email;

    @Column(unique = true)
    private String nickname;

    private String password;

    private boolean emailVerified;

    private String emailCheckToken; //이메일을 검색할 때 사용할 토큰값을 db에 저장해놓고 매치하는지 확인하기 위해 

    private LocalDateTime joinedAt; //인증이 된 사용자를 그제야 가입될 수 있도록!

    private String bio; //짧은 자기소개

    private String url;

    private String occupation; //직업

    private String location;

    @Lob @Basic(fetch = FetchType.EAGER)
    private String profileImage;

    private boolean meetCreatedByEmail;

    private boolean meetCreatedByWeb;

    private boolean meetEnrollmentResultByEmail;

    private boolean meetEnrollmentResultByWeb;

    private boolean meetUpdatedByEmail;

    private boolean meetUpdatedByWeb;
}

Id만 사용하는 이유는?

연관관계가 복잡해질 때 @EqualsAndHashCode 에서 서로 다른 연관관계를 순환참조 하느라 무한루프가 발생하고, 결국에는 stackoverflow 가 발생할 수 있다. 그래서 id 를 주로 사용한다.

생성자 자동 생성이란?

Lombok을 사용하면 생성자도 자동으로 생성할 수 있다. @NoArgsConstructor 어노테이션은 파라미터가 없는 기본 생성자를 생성해주고, @AllArgsConstructor 어노테이션은 모든 필드 값을 파라미터로 받는 생성자를 만들어준다. 마지막으로 @RequiredArgsConstructor 어노테이션은 final이나 @NonNull인 필드 값만 파라미터로 받는 생성자를 만들어준다.

  • 이메일, 닉네임으로도 로그인을 할 수 있도록 지원함. 값은 유니크 해야 한다. 중복되어서는 안된다.
  • 일반적으로 스트링은 varchar(255) 타입으로 맵핑으로 되어있다. 하지만 프로필 이미지와 같은 경우는 더 커질 수 있으므로 lob로 맵핑한다. 그럼 텍스트 타입에 맵핑된다. 현재 기본 패치 타입은 lazy이지만 일단 명시적으로 eager로 주자. profileImage는 분명 유저를 로딩할 때 같이 쓰일 것
  • Tip: 시크릿 모드로 들어가야 캐시가 없어져서 더 수월하다!!!!

회원 가입: 컨트롤러

  • account 관련된 것들을 모아놓기 위해 account 패키지를 만든다.

  • 스프링 시큐리티 설정하기 위해 config 패키지를 만든다.

import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/", "/login", "/sign-up", "/check-email", "/check-email-token",
                        "/email-login", "/check-email-login", "/login-link").permitAll()
                .mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
                .anyRequest().authenticated();
    }
}
  • @ EnableWebSecurity 커스텀할 수 있도록 함
  • HttpSecurity 를 오버라이딩 해서 원하는 특정한 요청들은 스프링 시큐리티 인증 체크를 하지 않도록 걸러낼 수 있다.
  • profile 은 그냥 허용하면 안된다. httpmethod 중 get만 허용하도록! 나머지 요청은 로그인 해야만 사용할 수 있도록 한다.
MockHttpServletResponse:
           Status = 403
    Error message = Access Denied
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
  • AccountControllerTest 단위테스트 실행!
    (// .mvcMatchers("/", "/login", "/sign-up", "/check-email", "/check-email-token",
    // "/email-login", "/check-email-login", "/login-link").permitAll() 주석처리를 하고 unit test를 돌려봄)

에러반환! 그럼 다시 주석을 해지하고 돌려보면?

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Language:"en", Content-Type:"text/html;charset=UTF-8", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = text/html;charset=UTF-8
             Body = <!DOCTYPE html>

200이 나온다. 즉 실제 뷰가 제공 된다는 것을 알 수 있음 😀 여기서 잠깐..! Mock 이 무엇일까?

Mock

  • Mock이라는 단어를 사전에서 찾아보면 '테스트를 위해 만든 모형'을 의미한다.

  • 테스트를 위해 실제 객체와 비슷한 모의 객체를 만드는 것을 모킹(Mocking)이라고 하며, 모킹한 객체를 메모리에서 얻어내는 과정을 목업(Mock-up)이라고 한다.

  • 객체를 테스트하기 위해서는 당연히 테스트 대상 객체가 메모리에 있어야 한다. 하지만 생성하는 데 복잡한 절차가 필요하거나 많은 시간이 소요되는 객체는 자주 테스트하기 어렵다. 또는 웹 애플리케이션의 컨트롤러처럼 WAS나 다른 소프트웨어의 도움이 반드시 필요한 객체도 있을 수 있다. 이런 복잡한 객체는 당연히 테스트 과정도 복잡하고 어려울 수 밖에 없다.

  • 따라서 테스트 하려는 실제 객체와 비슷한 가짜 객체를 만들어서 테스트에 필요한 기능만 가지도록 모킹을 하면 테스트가 쉬워진다.

  • 테스트하려는 객체가 복잡한 의존성을 가지고 있을 때, 모킹한 객체를 이용하면, 의존성을 단절시킬 수 있어서 쉽게 테스트할 수 있다.

  • 웹 애플리케이션에서 컨트롤러를 테스트할 때, 서블릿 컨테이너를 모킹하기 위해서는 @WebMvcTest를 사용하거나 @AutoConfigureMockMvc를 사용하면 된다.

  • 웹 환경에서 컨트롤러를 테스트하려면 반드시 서블릿 컨테이너가 구동되고 DispatcherServlet 객체가 메모리에 올라가야 하지만, 서블릿 컨테이너를 모킹하면 실제 서블릿 컨테이너가 아닌 테스트용 모형 컨테이너를 사용하기 때문에 간단하게 컨트롤러를 테스트할 수 있다.

출처 : 인프런 백기선님의 스프링과 JPA 기반 웹 애플리케이션 개발
https://elevatingcodingclub.tistory.com/61
https://www.daleseo.com/lombok-popular-annotations/

profile
Step by step goes a long way ✨

0개의 댓글