게시판 실습 복기 (ft. Spring Security, Junit )

2022. 2. 25. 09:55개발/실습

해당 게시글에선 전체 코드에 대한 설명은 하지 않고 배운점만 간략하게 작성합니다. 자세한 정보는 아래 저장소를 참고해주세요.

 

GitHub - 92SooJong/Toy-Project-Board

Contribute to 92SooJong/Toy-Project-Board development by creating an account on GitHub.

github.com

 

 

 

I. Front-End 환경

1. Boostrap

 간단한 프로젝트에서 많이 사용되는 Bootstrap을 통해 화면을 디자인 했다. 레퍼런스도 많고 샘플도 제공해줘서 좋다. Bootstrap을 커스터마이징 하기 위해서 local에 내려 받았다. 

 

Bootstrap

The most popular HTML, CSS, and JS library in the world.

getbootstrap.com

Bootstrap을 다운로드하고 파일을 확인해보면 *.min.css로 끝나는 파일을 볼수 있다.  *.min.css 파일들은 *.css 파일에서 불필요한 개행문자나 공백을 제거한 파일이다. 

 

Bootstrap 예제를 참고해서 필요한 화면들을 만들었다.

로그인
게시판
게시글 읽기

2. JQuery

동적화면 구성과 간단한 Ajax 사용을 위해 Jquery를 사용했다. ( 구글링 해보니까 JQuery의 사용률은 높으나 인기가 예전만 못한거같긴하다. ㅠㅠ)

게시글 목록을 불러오는 ajax코드

 

II. Back-End

backend 환경은 아래와 같이 구성했다

- Spring Web MVC

- Spring Security

- Spring Data JPA

- MariaDB

- Junit5

 

1. RDS의 MariaDB 사용

 여러 PC에서 개발을 하다보니 DB를 로컬에 설치하지 않고, RDS를 사용하도록했다. 사용중인 저장소가 Public으로 관리되기 때문에 MariaDB에 대한 접속정보는 별도의 profile로 분리후 local에서만 관리할 수 있도록 했다.

application.yml
application-db.yml

commit시 반영되지 않도록 .gitignore에 application-db.yml을 추가하자!

 

2. Spring Security를 통한 로그인 및 세션 관리

 WebSecurityConfigurerAdapter의 configure(HttpSecurtiy http)를 통해 프로젝트 리소스에 대한 접근 권한 및 로그인 처리, 세션관리를 설정했다.

[37 Line] CSRF 방지를 disable 처리했으나 Spring Security의 공식문서에서 브라우저를 통한 서비스를 구축하고 있을때 CSRF 방지기능을 사용하라고 권하고 있다. 만약 CSRF 방지 기능을 사용하려면 [38 Line]의 주석을 해제하고, 서버에서 받은 CSRF 토큰을 request시에 항상 함께 보내도록 화면의 스크립트를 수정하면된다.

 

Cross Site Request Forgery (CSRF) :: Spring Security

When should you use CSRF protection? Our recommendation is to use CSRF protection for any request that could be processed by a browser by normal users. If you are only creating a service that is used by non-browser clients, you will likely want to disable

docs.spring.io

 

[39~43 Line] 로그인 화면 및 API와 디자인을 위한 리소스는 권한없이 접근이 가능하도록 했으며 그 외에는 인증을 받아야하도록 셋팅했다. 

 

[45~49 Line] 사용자 인증 메커니즘으론 form based Auth를 따르도록 했다. ( formLogin() 사용 ) 로그인 성공시 이동할 경로와 로그인 요청시 request body에서 username과 password가 어떤 파라미터에 담겨있는지 알려준다.

사용자 인증은 데이터베이스를 활용했다. 오버라이드 받은 configure(AuthenticationManagerBuilder auth) 메소드를 통해 인증방법을 제공한다.

 

[51~55 Line] 사용자가 브라우저를 재시작해도 로그인을 요청하지 않도록 하기 위해 remember-me 쿠키를 사용할 수 있도록 했다. 사용자가 로그인의 Request Body에 remeber-me : on을 담아 보내면 서버는 응답으로 remeber-me 쿠키를 보낸다.

로그인 요청시 Request Body

 

사용자가 rember-me데이터를 보냈다면 서버는 rember-me 쿠키를 응답에 담아 보낸다. 사용자는 아래 그림처럼 JSESSIONID 쿠키와 remeber-me 쿠키를 로컬에 저장하게 된다.

remeber-me를 체크하고 로그인을 수행한 후의 로컬 쿠키

이제부터 사용자는 Request를 보낼때 2개의 쿠키 정보를 담아서 서버로 보낸다.

리소스를 요청할때 쿠키

로컬에 있는 쿠키중 JSESSIONID를 제거했다. 그리고 다시 Request 요청을 해보자.

 

서버는 사용자가 보낸 JSESSIONID 쿠키를 확인함으로써 사용자의 세션을 관리하는데 만약 Request시 JSESSIONID 쿠키가 없다면 서버는 사용자를 다시 로그인 화면으로 보낼것이다. 하지만 remeber-me 쿠키를 서버로 보낸다면 서버는 다시 사용자에게 JSESSIONID를 보내줌으로써 로그인 없이 JSESSIONID 쿠키를 가질 수 있도록 해준다.

remeber-me쿠키를 통해 JSESSIONID를 다시 할당 받는다.

 

 

[57~62 Line] 사용자가 로그아웃 버튼을 클릭하면 사용자의 JSESSIONID와 remeber-me에 대한 데이터를 삭제한다.

 

3. Junit5 테스트 

1) 테스트용 DB 구축

테스트용 In-Memory DB를 추가로 구성했다. 아무래도 테스트후 데이터를 선택적으로 지울필요가 없어 꽤 편리하다. ( @Transactional을 사용해서 테스트후 rollback을 할수 있도록해도 되겠다. ) MariaDB를 사용하는게 가장 이상적이겠지만, 테스트의 목적이 단순히 서비스단 레벨의 로직을 테스트하는 것이기 때문에 H2 DB를 선택했다.

application-test.yml

아래와 같이 application-test.yml을 위치 시켰는데 test 수행시 인식을 하지 못한다면(이름을 application.yml로 했는데도 인식을 하지 못했다.) @ActiveProfiles 어노테이션을 통해 사용자가 직접 테스트에 대한 설정을 구성하면 된다.

resouces 하위에 야믈(YAML)파일을 생성했다.

 

profile 인식을 위해 @ActiveProfiles 어노테이션 사용

2) 가상 사용자

게시글 작성을 테스트할땐 회원가입된 사용자 정보가 필요했다. @WithMockUser를 사용하면 파라미터로 작성한 testUsername으로 인증을 성공한 사용자가 접속했다고 가정하고 테스트를 수행할수 있다.

만약 testUsername으로 사용자 정보를 불러와야하는 테스트 케이스라면 테스트 시작전 사용자를 등록해주는 작업이 필요하다. ( 이 작업을 생략하면 Entitiy Null 에러가 발생했다. )

testUsername이 로그인했다고 가정하고 테스트 수행
테스트전 사용자 등록