BasicBoard2는 Spring Boot 기반의 간단한 게시판 프로젝트로, 회원 가입, 로그인, 게시글 CRUD, JWT 인증 및 보안 기능을 포함합니다.
- Backend: Java 21, Spring Boot 3.4.3, Spring Security, MyBatis, JWT
- Database: MySQL (MariaDB 호환)
- Frontend: HTML, CSS, JavaScript
회원가입 시 사용자 정보를 데이터베이스에 저장합니다.
POST /join
{
"userId": "user1",
"password": "password123",
"userName": "사용자 이름"
}
로그인 요청 시 TokenProvider가 JWT를 생성하여 반환합니다.
POST /login
{
"username": "user1",
"password": "password123"
}
응답:
{
"isLoggined": true,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI...",
"userId": "user1",
"userName": "사용자 이름"
}
모든 요청에서 Authorization 헤더를 확인하여 JWT를 검증합니다.
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = resolveToken(request);
if (token != null && tokenProvider.validToken(token) == 1) {
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}액세스 토큰이 만료되면, 클라이언트는 refreshToken을 이용해 새로운 액세스 토큰을 요청합니다.
POST /refresh-token
Cookie: refreshToken=eyJhbGciOiJIUzI1NiJ9...
응답:
{
"token": "새로운 액세스 토큰"
}
로그아웃 시 클라이언트의 쿠키에서 refreshToken을 삭제합니다.
CookieUtil.deleteCookie(request, response, "refreshToken");현재 로그인된 사용자의 정보를 반환합니다.
GET /user/info응답:
{
"id": 1,
"userId": "user1",
"userName": "사용자 이름",
"role": "ROLE_USER"
}
BasicBoard2의 게시판 기능은 로그인한 사용자가 게시글을 작성, 조회, 수정, 삭제할 수 있도록 구성되어 있습니다. 또한, 파일 업로드 및 다운로드 기능을 제공하며, 게시글 수정 및 삭제는 작성자 본인에게만 허용됩니다.
로그인한 사용자는 게시판 목록을 확인할 수 있습니다.
GET /api/board?page=1&size=10
- 응답 예시:
{
"articles": [
{
"id": 1,
"title": "첫 번째 게시글",
"content": "게시글 내용입니다.",
"author": "user1",
"created": "2024-03-13T10:00:00"
}
],
"last": false
}
게시글을 클릭하면 상세 페이지에서 내용을 확인할 수 있습니다.
GET /api/board/{id}
- 응답 예시:
{
"title": "첫 번째 게시글",
"content": "게시글 내용입니다.",
"userId": "user1",
"filePath": "/uploads/sample.jpg",
"created": "2024-03-13T10:00:00"
}
작성자가 아닌 경우 수정/삭제 버튼 비활성화 게시글 상세 페이지에서 로그인한 사용자 ID와 게시글 작성자의 ID가 다를 경우, 수정 및 삭제 버튼이 비활성화됩니다.
if (hUserId !== response.userId) {
$('#editBtn').prop('disabled', true);
$('#deleteBtn').prop('disabled', true);
}로그인한 사용자는 게시글을 작성할 수 있으며, 파일을 첨부할 수도 있습니다.
POST /api/board
{
"title": "새로운 게시글",
"content": "게시글 본문",
"hiddenUserId": "user1",
"file": "첨부파일"
}
게시글 작성자는 자신의 게시글을 수정할 수 있습니다.
PUT /api/board
{
"title": "수정된 제목",
"content": "수정된 내용",
"hiddenUserId": "user1",
"hiddenId": 1,
"fileChanged": false,
"hiddenFilePath": "/uploads/sample.jpg"
}
게시글 작성자는 자신의 게시글을 삭제할 수 있습니다.
DELETE /api/board/{id}
{
"userId": "user1"
}
첨부된 파일이 있을 경우, 다운로드할 수 있습니다.
GET /api/board/file/download/{fileName}
- 파일 다운로드가 수행됩니다.
- 사용자가 로그인하면, 환영 메시지와 함께 숨겨진 필드에 사용자 정보가 저장됩니다.
- 이후, 로그인한 사용자가 게시판을 이용할 수 있습니다.
$(document).ready(() => {
getUserInfo().then((userInfo) => {
$('#welcome-message').text(userInfo.userName + '님 환영합니다!');
$('#hiddenUserId').val(userInfo.userId);
});
});게시글 목록은 페이지네이션을 포함하여 불러올 수 있습니다.
let getBoards = () => {
let currentPage = 1;
const pageSize = 10;
loadBoard(currentPage, pageSize);
$('#nextPage').on('click', () => {
currentPage++;
loadBoard(currentPage, pageSize);
});
$('#prevPage').on('click', () => {
if (currentPage > 1) {
currentPage--;
loadBoard(currentPage, pageSize);
}
});
};if (hUserId !== response.userId) {
$('#editBtn').prop('disabled', true);
$('#deleteBtn').prop('disabled', true);
}이와 같은 방식으로, 게시글 수정/삭제는 작성자만 가능하도록 구현되어 있습니다.
네이버 로그인 기능을 추가하여 사용자들이 네이버 계정으로 간편하게 로그인할 수 있도록 구현했습니다. 네이버 로그인 후, 네이버에서 제공하는 사용자 프로필(예: id, 이메일, 닉네임 등)을 받아 내부 JWT 토큰을 생성하고, 이 정보를 이용해 게시글 작성, 조회 등 기능에서 작성자 정보를 표시하도록 했습니다.
- WebSecurityConfig.java
- Spring Security 설정에서 네이버 로그인 관련 URL(
/api/social/**)과 정적 리소스(예:/naver-callback.html)를 인증 없이 접근할 수 있도록permitAll()설정을 추가했습니다. - JWT 인증 필터(
TokenAuthenticationFilter)를 등록하여 모든 요청에 대해 JWT를 검사합니다.
- Spring Security 설정에서 네이버 로그인 관련 URL(
- TokenProvider.java
- 내부 JWT 토큰 생성 로직을 구현했습니다.
- 네이버 로그인 시 반환받은 닉네임(별명)을
Member객체의userName필드로 매핑하고, 이를 기반으로 JWT를 생성하도록 수정하였습니다. - 토큰에 작성자 ID, 닉네임, 역할 등의 정보를 포함시켜 API 요청 시 인증에 사용합니다.
- TokenAuthenticationFilter.java
- 요청 헤더의
Authorization값을 추출하여 JWT 형식(“Bearer …”)인지 확인하고, 유효한 경우TokenProvider를 사용해 인증 정보를 SecurityContext에 설정합니다. - JWT 형식이 올바르지 않거나 없을 경우 경고 메시지를 로그에 남기고, 필요한 경우 오류 응답을 반환합니다.
- 요청 헤더의
- NaverAuthService.java
- 네이버 OAuth 2.0을 사용하여 네이버 로그인 URL을 생성하고, 인가 코드를 통해 접근 토큰을 요청하는 메서드를 구현했습니다.
- 네이버 API (
https://openapi.naver.com/v1/nid/me)를 호출하여 사용자 프로필 정보를 JSON으로 받아NaverUserResponseDTO에 매핑합니다.
- NaverUserResponse.java
- 네이버에서 반환하는 응답 JSON을 매핑하는 DTO로, 내부 클래스
NaverResponse에id,email,nickname등의 필드를 포함시켰습니다.
- 네이버에서 반환하는 응답 JSON을 매핑하는 DTO로, 내부 클래스
- SocialLoginRequestDTO.java / SocialLoginResponseDTO.java
- 소셜 로그인 요청 및 응답에 필요한 정보를 전달하기 위한 DTO를 생성했습니다.
- 중요 변경 사항: 네이버 로그인 시 네이버의
nickname값을SocialLoginResponseDTO에 포함하여 클라이언트에서 닉네임(별명)으로 표시되도록 했습니다.
- SocialLoginType.java
- 네이버 등 다양한 소셜 로그인 타입을 열거형(enum)으로 관리합니다. (현재는 네이버만 사용)
- SocialLoginService.java
- 소셜 로그인 요청을 처리하여, 네이버 로그인 시
NaverAuthService를 통해 사용자 정보를 가져오고,TokenProvider로 내부 JWT 토큰을 생성합니다. - 생성된 JWT 토큰과 함께 네이버의
id,email,nickname정보를SocialLoginResponseDTO에 담아 클라이언트로 반환합니다. - 핵심 변경 사항: 네이버에서 받은 닉네임을
member.userName에 저장하고, 이를 JWT 및 응답 DTO에 포함시켜 모든 화면(게시글 작성, 상세, 목록)에서 별명 대신 네이버 닉네임이 표시되도록 처리했습니다.
- 소셜 로그인 요청을 처리하여, 네이버 로그인 시
- SocialLoginController.java
/api/social/naver: 네이버 로그인 요청 시 상태 토큰을 생성하고, 네이버 로그인 페이지로 리다이렉트합니다./api/social/login/callback: 네이버 로그인 콜백 엔드포인트로, 네이버에서 전달된 인가 코드와 상태 값을 검증한 후 네이버 액세스 토큰을 받아 사용자 프로필 정보를 조회합니다.- 조회된 정보를 기반으로
Member객체를 생성하고, 내부 JWT 토큰을 발급합니다. - 최종적으로 JWT 토큰을 쿼리 파라미터에 포함하여 정적 콜백 페이지(예:
naver-callback.html)로 리다이렉트합니다.
- 조회된 정보를 기반으로
- naver-callback.html
- 네이버 로그인 후 내부 JWT 토큰을 URL 쿼리 파라미터로 받아 로컬 스토리지에 저장하고, 메인 페이지로 리다이렉트하는 정적 HTML 페이지입니다.
- 이 페이지는 Spring Security 필터링 대상에서 제외되어 있어 인증 없이 접근할 수 있습니다.
- naverCallback.js
naver-callback.html에서 실행되며, URL에서 JWT 토큰을 추출하여localStorage에 저장한 후 메인 페이지로 이동합니다.- 개발자 도구의 콘솔 로그를 통해 JWT 토큰이 올바르게 저장되는지 확인할 수 있습니다.
- Article.java
- 게시글 모델에
nickname필드를 추가하여, 소셜 로그인 시 네이버에서 받아온 닉네임(별명)을 저장하도록 수정했습니다. toBoardDetailResponseDTO()메서드에nickname값을 포함시켜 게시글 상세 정보에 표시되도록 했습니다.
- 게시글 모델에
- BoardMapper.xml
- 게시글 저장 시
nickname컬럼을 포함시키도록 INSERT SQL을 수정했습니다. - 게시글 조회 시
nickname컬럼을 매핑하는 ResultMap을 수정하여, DB에 저장된 닉네임(별명)이 조회될 수 있도록 했습니다.
- 게시글 저장 시
- boardList.js / boardDetail.js / boardWrite.js
- 사용자 정보 조회 API(
/user/info)를 통해 네이버 로그인 시 내부 JWT에 포함된 사용자 정보를 가져옵니다. - 화면의 작성자 정보 표기를
response.nickname(우선 순위),response.userName, 또는response.userId순으로 표시하여 네이버 닉네임(별명)이 보이도록 수정했습니다. - 글쓰기 화면에서는 사용자 입력 필드에 기본값으로 네이버 로그인 시 반환된 닉네임을 보여주도록 설정했습니다.
- 사용자 정보 조회 API(
-
네이버 로그인 시, 네이버 오픈 API에서 아래와 같이 응답이 반환됨을 확인했습니다.
{ "response": { "id": "xHHVIL0rgAfXa03aPX-ZUQaZbt7On3xhnph6mx5Q52k", "email": "ygpanda@naver.com", "nickname": "너구리" } } -
내부 JWT 토큰이 정상적으로 생성되어
/user/infoAPI에서 사용자 정보에 네이버 닉네임(예: "너구리")가 포함되어 반환되는지 확인했습니다. -
게시글 작성 시 네이버 로그인 사용자의 닉네임(별명)이 DB의
nickname컬럼에 저장되고, 게시글 목록 및 상세 페이지에서 올바르게 표시되는지 테스트했습니다. -
글쓰기 화면에서 작성자 입력 필드에 네이버 닉네임이 기본값으로 표시되는지 확인했습니다.
- 문제점: 네이버 로그인 후 DB의
article테이블에 저장될 때, 작성자 아이디(userId)만 저장되고,nickname컬럼이 비어 있거나 내부 JWT 토큰에 포함되지 않는 문제 발생. - 해결 과정:
- 네이버 API 응답에서
nickname을 올바르게 추출하여Member객체의userName필드에 매핑하도록 수정. - 게시글 저장 시, BoardMapper의 INSERT SQL에
nickname컬럼을 포함하도록 수정. - 프론트엔드에서 게시글 상세 및 목록 화면에 작성자 정보를 표시할 때,
response.nickname을 우선 사용하도록 수정. - 최종적으로, 네이버 로그인 시 "너구리"와 같이 네이버에서 제공된 닉네임이 정상적으로 표시되는 것을 확인함.
- 네이버 API 응답에서
네이버 로그인 기능을 추가하면서, 백엔드와 프론트엔드 양쪽에서 다음과 같은 주요 수정 작업을 진행했습니다.
- 백엔드:
- 네이버 OAuth 2.0을 통한 토큰 발급 및 사용자 정보 조회 기능 구현
- 내부 JWT 생성 시 네이버에서 받아온
nickname을 포함하여 생성하도록 TokenProvider와 SocialLoginService 수정 - BoardMapper.xml에서 INSERT 및 SELECT SQL에
nickname컬럼을 추가하여 DB에 저장 및 조회가 가능하도록 수정
- 프론트엔드:
- 사용자 정보 조회 및 글쓰기 화면에서 작성자 정보를 표시할 때, 네이버 로그인 시 받은 닉네임(별명)을 우선적으로 사용하도록 수정
- 네이버 콜백 페이지 및 관련 JS 파일을 통해 내부 JWT 토큰을 클라이언트에 전달
이 과정을 통해 기존 프로젝트에 네이버 로그인 기능을 성공적으로 추가하고, 네이버 로그인 시 반환되는 닉네임(별명)이 올바르게 표시되도록 개선했습니다.


