개인프로젝트/[스프링] SJBoard 개발일지

[개인 프로젝트] SJBoard (댓글 및 파일 업로드 게시판) 7일차 개발 일지

Seojoo21 2022. 3. 16. 16:40

2022.03.16.수요일 7일차 

 

어제 스프링 시큐리티를 이용하여 회원 정보 및 로그인 처리를 담당하는 영속 영역과 로그인 컨트롤러를 작성한 것에 이어, 오늘은 사용자들이 실제 게시물과 댓글을 작성하는 view 페이지에 스프링 시큐리티를 적용하여 로그인 처리를 구현하였다. 

 

그리고 최종 로그아웃 처리까지 구현하였다. 

 

1.  제작 과정 

    회원  비회원
게시물 전체 목록 조회 (list.jsp) O O
특정 게시물 조회 (get.jsp) O O
작성 (register.jsp) O X
수정 (modify.jsp) only 작성자 X
삭제 (modify.jsp) only 작성자 X
댓글 작성 (get.jsp 내 Ajax처리) O X
수정 (get.jsp 내 Ajax처리) only 작성자 X
삭제 (get.jsp 내 Ajax처리) only 작성자 X

* 비회원도 게시판 조회는 가능하나 게시물 및 댓글 작성은 회원만 가능

 

1) 로그인 페이지(memberLogin.jsp) 처리 

- 로그인 페이지(memberLogin.jsp)는 SjBoard가 사용하는 부트스트랩 무료 템플릿에 있는 login.html 를 jsp 페이지로 변환하여 활용

- <form> 태그 내의 <input> 태그의 name 속성을 스프링 시큐리티에 맞게 수정하고 CSRF 토큰 항목을 <input type='hidden'>으로 추가

 

2) 게시물 작성 시 스프링 시큐리티 처리 

- 사용자가 '글쓰기'를 눌렀을 때 게시물 작성 페이지(register.jsp)로 이동하게 되는데 이때 로그인 하지 않은 경우에는 로그인 페이지(memberLogin.jsp)로, 로그인을 한 경우에는 원래의 게시물 작성 페이지(register.jsp)로 이동하도록 BoardController의 게시물 작성 메소드인 register()에 @PreAuthroize("isAuthenticated()") 어노테이션 추가 

- 게시물 작성 시에는 사용자의 이름이 출력되도록 구현

- 게시물 작성 또한 POST 방식의 전송이므로 CSRF 토큰을 사용하도록 CSRF 토큰 값을 <input type='hidden'>으로 추가 

 

3) 게시물 조회 및 로그인 처리 

- 게시물 조회 화면에서 현재 로그인한 사용자 중 실제 작성자만이 수정/삭제 작업을 할 수 있도록 게시물 조회 페이지(get.jsp)를 수정

- 게시물 작성자와 현재 로그인한 사용자의 사용자 이름(userName)이 일치할 때만 '수정'버튼이 보일 수 있게 처리

(<sec:authorize access="isAuthenticated()"> 이용) 

 

- 댓글 목록 하단에 있는 댓글 작성창의 경우, 로그인한 사용자만 댓글을 작성할 수 있도록 댓글 작성창이 로그인 상태에 따라 활성화/비활성화 될 수 있도록 처리 (<sec:authorize access="isAuthenticated()"> 이용) 

 

4) 게시물 수정/삭제 처리 

- 사용자가 URL을 조작해서 접근할 수 없도록 화면과 POST방식으로 처리되는 부분에서 CSRF 토큰과 스프링 시큐리티 적용

- 게시물 수정/삭제 처리는 현재 로그인한 사용자와 작성자가 동일한 경우에만 처리될 수 있도록 BoardController 내 게시물 수정을 처리는 메서드 modify()와 삭제 처리를 담당하는 메서드 remove()에 @PreAuthrorized 어노테이션을 추가 

 

5) 댓글 작성/수정/삭제 처리 및 게시물 첨부파일 처리 (Ajax 스프링 시큐리티 처리) 

- Ajax에 스프링 시큐리티가 적용되면 POST, PUT, PATCH, DELETE와 같은 방식으로 데이터를 전송하는 경우 반드시 추가적으로 'X-CSRF-TOKEN'와 같은 헤더 정보를 추가하여 CSRF 토큰값을 전달하도록 수정해야하므로 자바스크립트를 이용하여 CSRF 토큰과 관련된 값을 변수로 선언하고, 전송 시 포함시켜주는 방식으로 처리 

 

5-1) 첨부 파일 등록, 삭제 처리

-  게시물 작성 페이지 (register.jsp) 내 첨부파일 동작 관련 자바스크립트에 csrfHeaderName과 csrfTokenValue 변수를 추가

- Ajax 데이터 전송 시 beforeSend를 이용해 setRequestHeader() 메서드로 RequestHeader에 csrfHeaderName과 csrfTokenValue 전달

- 첨부파일 처리를 담당하는 UploadController 내 첨부파일 등록 메서드 uploadAjaxPost()와 삭제 메서드 deleteFile()에 @PreAuthorize 어노테이션 추가 

 

5-2) 댓글 등록, 삭제, 수정 처리 

- 앞의 게시물 작성과 마찬가지로 댓글 등록은 로그인한 사용자만이 할 수 있고, 댓글 수정 및 삭제는 로그인한 사용자와 댓글 작성자의 사용자 이름(userName)을 비교해서 같은 경우에만 처리할 수 있도록 구현

- 댓글 등록의 경우 앞의 첨부파일 처리와 마찬가지로 댓글 동작 관련 자바스크립트에 CSRF 토큰을 같이 전송하도록 처리 

- 댓글 수정과 삭제의 경우 기존에 댓글 번호만을 이용하던 방식에서 사용자 이름 일치 여부 확인을 위해 추가로 댓글 작성자를 같이 서버로 전송하도록 처리 

- 댓글 처리를 담당하는 ReplyController 내의 댓글 등록, 수정, 삭제를 담당하는 메서드에 @PreAuthorize 어노테이션 추가 

 

6) 로그아웃 페이지 (memberLoout.jsp) 처리 

- 로그인 페이지(memberLogin.jsp)를 응용하여 로그아웃 페이지(memberLogout.jsp) 생성  

 

3. 발생 에러 및 해결 방법

 

1) 에러:

로그아웃 페이지 memberLogout.jsp를 구현하고 브라우저에서 실제 로그아웃 처리를 확인하는데 아래와 같은 오류를 만났다. 

 

PreparedStatementCallback; bad SQL grammar [delete from persistent_logins where username = ?]; nested exception is java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist

오라클 SQL 관련 에러라니... 여태까지 데이터베이스 관련 처리를 하면서도 한 번도 보지 못했던 에러가 로그아웃 페이지에서 나오는 것이 잘 이해되지 않았고 무엇보다 오류 메시지에서 보여주는 bad SQL grammar도 내가 직접 작성한 것이 아니라서 이상했다. 

 

무엇보다도 공부한대로라면 로그아웃 버튼 클릭 시 '/memberLogout' 에서 POST방식으로 로그아웃 후 내부적으로 자동으로 로그인 페이지를 호출해야하고 이때 '/memberLogin?logout' 식으로 ?와 logout 파라미터가 나와야 하는데 전혀 나오지 않았다. 

 

2) 원인:

알고보니 자동 로그인(remember-me) 기능을 처리하기 위해 데이터베이스를 조회하는 방식을 사용하려고 security-context.xml에 <security:remember-me> 코드를 넣었다. 그런데 문제는 이렇게 코드만 넣어놓고 실제로 자동 로그인 정보를 기록해둘 데이터베이스를 만들지 않았었다. (그러니 delete from persistent_logins~ 에 나오는 persistent_logins 를 보고 낯설어했겠지...) 

 

당연히 데이터베이스에 자동 로그인 관련 테이블이 없었으므로 로그아웃 처리 시 쿠키를 삭제하기 위해 remember-me를 체크하던 서버는 관련 테이블이 없다는 오라클 에러를 보낼 수 밖에 없었다.

 

3)해결

자동로그인 정보를 저장해놓는 테이블 persistent_logins를 오라클 데이터베이스에 만들어주었다. 그리고 다시 테스트를 해보니 로그아웃이 처리되었다. 

 

4. 오늘 프로젝트 진행하면서 추가로 배운 내용

1) 자바스크립트로 로그인 버튼 클릭 뿐만 아니라 엔터키로도 로그인 처리 할 때

 

- 먼저 자바스크립트 작성 시 id 값을 쓰기 위해 로그인 form, button에 id 값을 넣어준다. 그리고 button의 type은 submit으로 넣는다.

- 그 다음, 자바스크립트는 아래와 같이 작성한다. keyCode 13은 enter키를 의미한다. 

 

2) 자동 로그인 기능을 처리하는 방식 중에서 가장 많이 사용되는 방식은 로그인이 되었던 정보를 데이터베이스를 이용해서 기록해 두었다가 사용자의 재방문 시 세션에 정보가 없으면 데이터베이스를 조회해서 사용하는 방식이다. 서버의 메모리상에만 데이터를 저장하는 방식보다 좋은 점은 데이터베이스에 정보가 공유되기 때문에 좀 더 안정적으로 운영이 가능하다는 점이다. 

 

스프링 시큐리티에서 'remember-me' 기능 역시 JDBC를 이용하는 경우처럼 지정된 이름의 테이블(persistent_logins)을 생성하면 지정된 SQL문이 실행되면서 이를 처리하는 방식과 직접 구현하는 바식이 있다. 생성된 테이블은 로그인을 유지하는데 필요한 정보를 보관하는 용도일 뿐이므로, 커스터마이징 하기 보다는 지정된 형식의 테이블을 생성한다.

 

스프링 시큐리티의 공식 문서에 나오는 로그인 정보를 유지하는 테이블은 아래와 같은 스크립트로 구성된다.

create table persistent_logins (
username varchar(64) not null,
series varchar(64) primary key,
token varchar(64) not null,
last_used timestamp not null);

테이블을 생성하는 스크립트는 특정한 데이터베이스에 맞게 테이블 이름과 칼럼명을 제외한 칼럼의 타입 등을 적당히 조정해서 사용하면 된다. 오라클에서는 varchar를 그대로 이용하거나 varchar2로 변경해서 사용하면 된다. 

 

자동 로그인에서 데이터베이스를 이용하는 설정은 별도의 설정 업이 data-source-ref만을 지정하면 된다.