Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
a96fa38
feat: 댓글 추가 DTO 구현
won-seoop Oct 12, 2025
47347bb
feat: 댓글 엔티티 구현
won-seoop Oct 12, 2025
425dc93
feat(comment): 댓글 등록 컨트롤러 구현
won-seoop Oct 12, 2025
67ff9c7
feat(comment): 댓글 등록 DTO 구현
won-seoop Oct 12, 2025
6091ff7
feat(comment): 댓글 등록 Repo 구현
won-seoop Oct 12, 2025
c4c8a72
feat(comment): 댓글 등록 Service 구현
won-seoop Oct 12, 2025
6a749ca
chore : GlobalExceptionHandler 가독성 개선
won-seoop Oct 12, 2025
5242a8d
config : yml ddl-auto 수정
won-seoop Oct 12, 2025
becbc28
chore(post) : postDtos 가독성 개선
won-seoop Oct 12, 2025
6eda430
chore(post) : PostService 가독성 개선
won-seoop Oct 12, 2025
2b9fd72
chore(global) : SuccessCode 가독성 개선
won-seoop Oct 12, 2025
cfe432a
chore: no meaningful changes
won-seoop Oct 12, 2025
45233e2
Merge branch 'feature/comment-create' into develop
won-seoop Oct 12, 2025
fd8e14c
feat(comment): 댓글 조회 Controller 완성
won-seoop Oct 12, 2025
cfe55e2
feat(comment): 댓글 조회 DTO 완성
won-seoop Oct 12, 2025
4b34c40
feat(comment): 댓글 미존재 커스텀 예외 완성
won-seoop Oct 12, 2025
8ef44ca
feat(comment): 댓글 조회 JPA 추가
won-seoop Oct 12, 2025
891dd71
feat(comment): 댓글 조회 Service 완성
won-seoop Oct 12, 2025
a2e9a99
feat(커서 DTO): 커서 기반 DTO 생성
won-seoop Oct 12, 2025
853d37b
feat(커서 DTO): 커서 기반 DTO 구현
won-seoop Oct 12, 2025
4c80128
feat(커서 DTO): 페이징 기반 DTO 구현
won-seoop Oct 12, 2025
c602a21
chore: meaningless commit
won-seoop Oct 12, 2025
2e20813
chore: meaningless commit
won-seoop Oct 12, 2025
bba27ad
feat(comment): 댓글 조회 성공 Status Code 추가
won-seoop Oct 12, 2025
2955f6f
Merge branch 'feature/comment-read' into develop
won-seoop Oct 12, 2025
e3e8813
chore: meaningless commit
won-seoop Oct 12, 2025
ea6ceb6
feat(comment): 댓글 삭제 및 수정 성공 컨트롤러 구현
won-seoop Oct 12, 2025
bb82a1a
feat(comment): 댓글 삭제 및 수정 성공 Service 구현
won-seoop Oct 12, 2025
0710d5b
feat(comment): 댓글 삭제 및 수정 성공 Service 구현
won-seoop Oct 12, 2025
0117714
feat(comment): 댓글 삭제 및 수정 성공 SuccessCode 구현
won-seoop Oct 12, 2025
0c193ce
Merge branch 'feature/comment-updateAndDelete' into develop
won-seoop Oct 12, 2025
6911f83
test(post,comment): userID 1로 고정
won-seoop Oct 12, 2025
9adb70c
chore(gitignore): 불필요한 폴더 추가로 Git 추적 제외
won-seoop Oct 14, 2025
6717615
feat(front): static 폴더 및 도메인별 구조 세팅
won-seoop Oct 14, 2025
80c3a22
feat : cors 허용
won-seoop Oct 21, 2025
6fb0fce
Merge branch 'front/feature/auth' into develop
won-seoop Oct 21, 2025
ac775df
test : 회원가입 기능 단위 테스트 이상 무
won-seoop Oct 21, 2025
5d205d8
Merge branch 'test/post-create-unit' into develop
won-seoop Oct 21, 2025
5a3f33b
feat : JwtFilter 구현중 ..
won-seoop Oct 22, 2025
74637a5
feat : JwtFilter 구현중 ..
won-seoop Oct 22, 2025
127c50d
feat : 로그인 및 회원가입 관심사 분리
won-seoop Oct 29, 2025
f646253
feat : 사용자 로그인 시, 세션 ID 발급 기능 구현 with Redis
won-seoop Oct 29, 2025
0fa875a
feat : 사용자 로그인 시, User Id만 발급받게 설정
won-seoop Oct 29, 2025
959b579
feat : 사용자 로그인 시, User Id만 발급하는 로직 SessionManger Class로 모듈화
won-seoop Oct 29, 2025
b7bc1c4
feat : 세션 인증 필터 구현
won-seoop Oct 29, 2025
b8d3d15
feat : Post 인가 기능 구현
won-seoop Oct 29, 2025
2e7873b
merge : 세션-쿠키 기반 인증 및 인가 기능 구현 완료
won-seoop Oct 29, 2025
ac908ac
feat : 사용자의 인증정보를 담을 Context 구현
won-seoop Oct 30, 2025
73c544c
chore: meaningless commit
won-seoop Nov 1, 2025
70ae31c
feat : 스프링 시큐리티 미사용, 세션 기반 로그인 기능 구현
won-seoop Nov 1, 2025
530c33d
Merge branch 'feature/session-cookie-auth-without-security' into feat…
won-seoop Nov 1, 2025
e80e567
chore: SimpleGrantAuthorities 제거로 인한 Spring Security 의존성 제거
won-seoop Nov 1, 2025
25fdc90
Merge branch 'feature/session-cookie-auth-without-security' into feat…
won-seoop Nov 1, 2025
c152477
chore: meaningless commit
won-seoop Nov 1, 2025
a1791a4
feat : Jwt를 사용한 로그인, 회원가입 그리고 인가 구현
won-seoop Nov 2, 2025
61acfcf
Merge branch 'feature/jwt-auth-without-spring-security' into feature/…
won-seoop Nov 2, 2025
18fae3a
feat : 토큰 없는 경우 401 api 응답 포맷 반환하는 Hanlder 구현
won-seoop Nov 2, 2025
5aaf4d2
feat : 필터에서 토큰이 만료된 경우 에러 처리하는 로직 구현
won-seoop Nov 2, 2025
e0ece17
feat : 로그아웃 구현
won-seoop Nov 2, 2025
bca148b
chore: meaningless commit
won-seoop Nov 2, 2025
cc98741
feat : 리프레시 토큰 재발급 기능 구현
won-seoop Nov 2, 2025
cd3acd0
fix : JPA EntityManger 및 Jwt 설정 누락 문제 해결
won-seoop Nov 4, 2025
9c26edb
Merge branch 'feature/auth-springSecurity-jwt' into feature/auth
won-seoop Nov 4, 2025
6185b22
feat : cretedAt 컬럼 JPA 매핑
won-seoop Nov 4, 2025
5ec9279
feat(comment) : 현재 유저 정보 기반 댓글 작성 기능 추가
won-seoop Nov 4, 2025
ef71945
chore: meaningless commit
won-seoop Nov 4, 2025
cf3ec5c
feat : createAt 오타 수정
won-seoop Nov 4, 2025
fee748a
feat : 액세스 토큰 재발급 성공 응답메세지 수정
won-seoop Nov 4, 2025
3842cb1
chore: meaningless commit
won-seoop Nov 4, 2025
4357f2f
Merge branch 'feature/auth' into develop
won-seoop Nov 4, 2025
c225c53
feat : 대댓글 기능 추가
won-seoop Nov 4, 2025
adb9148
feat : 대댓글 조회, 단건조회 그리고 수정 기능 추가
won-seoop Nov 4, 2025
21846d2
chore : comment Dto 행위별로 모듈화
won-seoop Nov 4, 2025
bb384cc
feat(auth) : 유저 회원가입 시, 프로필 이미지 넣는 부분 제거 (익명 커뮤니티 프로필 사이트 특정상, 프로필이미지…
won-seoop Nov 4, 2025
3522684
Merge branch 'feature/comment' into develop
won-seoop Nov 4, 2025
bf718b3
fix (auth) : 로그아웃시, 클라이언트 쿠키에서 리프레시 토큰이 사라지지 않는 문제 해결
won-seoop Nov 5, 2025
c35f8a1
feat (user) : 유저 기능 DTO 구현
won-seoop Nov 5, 2025
b082506
feat (user) : DTO 이름 오입력으로 인한 에러 해결
won-seoop Nov 5, 2025
61a4ab8
Merge branch 'feature/user' into develop
won-seoop Nov 6, 2025
9319508
feat : SuccessCodeInterface 추가
won-seoop Nov 7, 2025
b5ae4ec
chore : actuator 추가
won-seoop Nov 7, 2025
362e10f
feat(post) : PostLiker Builder 추가
won-seoop Nov 7, 2025
afa7e49
chore: meaningless commit
won-seoop Nov 7, 2025
cdce0e5
feat : 게시글 좋아요 기능 구현
won-seoop Nov 8, 2025
fa05b20
Chore : 사용하지 않은 Import 제거
won-seoop Nov 8, 2025
7d93fb9
Merge branch 'feature/post' into develop
won-seoop Nov 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ out/

### VS Code ###
.vscode/
/src/empty/
70 changes: 48 additions & 22 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,50 +1,76 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.5.6'
id 'io.spring.dependency-management' version '1.1.7'
id 'java'
id 'org.springframework.boot' version '3.5.6'
id 'io.spring.dependency-management' version '1.1.7'
}

group = 'springboot'
version = '0.0.1-SNAPSHOT'
description = 'Demo project for Spring Boot'

java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}





repositories {
mavenCentral()
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'


// JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

// MySQL
implementation 'com.mysql:mysql-connector-j'
// MySQL
implementation 'com.mysql:mysql-connector-j'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

// Lombok (compileOnly + annotationProcessor 조합)
// Lombok (compileOnly + annotationProcessor 조합)
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'

// QueryDSL
implementation 'com.querydsl:querydsl-jpa:5.1.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
// QueryDSL
implementation 'com.querydsl:querydsl-jpa:5.1.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

// Spring Security (웹 필터 및 인증 X)
implementation 'org.springframework.security:spring-security-crypto'
implementation 'org.springframework.boot:spring-boot-starter-security'

//test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation platform('org.junit:junit-bom:5.10.2') // 최신 BOM
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
testImplementation 'org.assertj:assertj-core:3.22.0'

//Jwt
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5'

// Redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.session:spring-session-data-redis'

// Spring Security (웹 필터 및 인증 X)
implementation 'org.springframework.security:spring-security-crypto'
// Password Encode Algorithm
implementation 'org.mindrot:jbcrypt:0.4'


}


tasks.named('test') {
useJUnitPlatform()
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@SpringBootApplication(
exclude = {ValidationAutoConfiguration.class,
SecurityAutoConfiguration.class } // ← Security 자동설정 제외

)

@EnableJpaAuditing
public class KakaoBootCampApplication {

public static void main(String[] args) {
SpringApplication.run(KakaoBootCampApplication.class, args);
}
public static void main(String[] args) {
SpringApplication.run(KakaoBootCampApplication.class, args);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package springboot.kakao_boot_camp.domain.auth.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springboot.kakao_boot_camp.domain.auth.util.Manager.login.jwt.RefreshTokenCookieManager;
import springboot.kakao_boot_camp.domain.auth.dto.loginDtos.LoginReq;
import springboot.kakao_boot_camp.domain.auth.dto.loginDtos.LoginRes;
import springboot.kakao_boot_camp.domain.auth.service.LoginService;
import springboot.kakao_boot_camp.global.api.ApiResponse;
import springboot.kakao_boot_camp.global.api.SuccessCode;

@RequestMapping("/api/v1/auth/login")
@RequiredArgsConstructor
@RestController
public class LoginController {

private final LoginService loginService;
private final RefreshTokenCookieManager refreshTokenCookieManager;


@PostMapping
public ResponseEntity<ApiResponse<LoginRes>> login(
@RequestBody @Valid LoginReq req,
HttpServletRequest servletRequest,
HttpServletResponse servletResponse) {

// 1. 액세스 토큰 포함 DTO 생성
LoginRes res = loginService.login(req, servletRequest);

// 2. 리프레시 토큰 포함
refreshTokenCookieManager.addRefreshTokenCookie(servletResponse, res.refreshToken());

// 3. Login response Dto에서 Refresh token 빼기
LoginRes result = LoginRes.fromWithoutRefreshToken(res.userId(), res.accessToken());
return ResponseEntity
.ok(ApiResponse.success(SuccessCode.LOGIN_SUCCESS, result));
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package springboot.kakao_boot_camp.domain.auth.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springboot.kakao_boot_camp.domain.auth.service.LogoutService;
import springboot.kakao_boot_camp.global.api.ApiResponse;
import springboot.kakao_boot_camp.global.api.SuccessCode;
import springboot.kakao_boot_camp.security.CustomUserDetails;

@RestController
@RequestMapping("/api/v1/auth/logout")
@RequiredArgsConstructor
public class LogoutController {

private final LogoutService logoutService;

@PostMapping
public ResponseEntity<ApiResponse<Void>> logout(HttpServletRequest request, HttpServletResponse response,
@AuthenticationPrincipal CustomUserDetails currentUser) {

logoutService.logout(currentUser, request, response);

return ResponseEntity.ok(ApiResponse.success(SuccessCode.LOGOUT_SUCCESS, null));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,24 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springboot.kakao_boot_camp.domain.auth.dto.AuthDtos.*;
import springboot.kakao_boot_camp.domain.auth.dto.signDtos.SignReq;
import springboot.kakao_boot_camp.domain.auth.dto.signDtos.SignRes;
import springboot.kakao_boot_camp.global.api.ApiResponse;
import springboot.kakao_boot_camp.global.api.SuccessCode;
import springboot.kakao_boot_camp.domain.auth.service.AuthService;
import springboot.kakao_boot_camp.domain.auth.service.SignUpService;

@RequiredArgsConstructor
@RestController // v1, v2 같은 버전은 추후 버전 관리를 위해 필요한 것인데 해당 프로젝트는 학습용 이므로 추후에 유지 보수 예정 X -> 따라서 버전 명 명시 안할 예정
@RequestMapping("/api/v1/auth")
public class AuthController {
@RequestMapping("/api/v1/auth/signup")
public class SignUpController {
private final SignUpService authService;

private final AuthService authService;

@PostMapping("/signup")
@PostMapping
public ResponseEntity<ApiResponse<SignRes>> signUp(@RequestBody @Valid SignReq req, HttpServletResponse servletRes) {
SignRes res = authService.signUp(req); //data 얻기

return ResponseEntity
.status(HttpStatus.OK)
.body(ApiResponse.success(SuccessCode.REGISTER_SUCCESS, res));
}

@PostMapping("/login")
public ResponseEntity<ApiResponse<LoginRes>> login(@RequestBody @Valid LoginReq req, HttpServletResponse servletRes) {
LoginRes res = authService.login(req); //data 얻기

return ResponseEntity
.status(HttpStatus.OK)
.body(ApiResponse.success(SuccessCode.LOGIN_SUCCESS, res));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package springboot.kakao_boot_camp.domain.auth.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springboot.kakao_boot_camp.domain.auth.util.Manager.login.jwt.RefreshTokenCookieManager;
import springboot.kakao_boot_camp.domain.auth.dto.loginDtos.LoginRes;
import springboot.kakao_boot_camp.domain.auth.service.TokenRefreshService;
import springboot.kakao_boot_camp.global.api.ApiResponse;
import springboot.kakao_boot_camp.global.api.SuccessCode;
import springboot.kakao_boot_camp.security.CustomUserDetails;

@RestController
@RequestMapping("/api/v1/auth/token")
@RequiredArgsConstructor
public class TokenRefreshController {

private final RefreshTokenCookieManager refreshTokenCookieManager;
private final TokenRefreshService tokenRefreshService;

/**
* 🔄 Refresh Token을 이용해 Access Token 재발급
*/
@PostMapping("/refresh")
public ResponseEntity<ApiResponse<LoginRes>> refreshAccessToken(
HttpServletRequest request,
HttpServletResponse response,
@AuthenticationPrincipal CustomUserDetails currentUser
) {

// 1️⃣ 쿠키에서 refresh token 추출
String refreshToken = refreshTokenCookieManager.getRefreshTokenFromCookie(request);

// 2️⃣ 새 access + refresh token 생성
LoginRes newTokens = tokenRefreshService.refreshTokens(currentUser, refreshToken);

// 3️⃣ 새 refresh token을 쿠키에 다시 설정
refreshTokenCookieManager.addRefreshTokenCookie(response, newTokens.refreshToken());

// 4️⃣ refresh token은 response에 포함하지 않음
LoginRes result = LoginRes.fromWithoutRefreshToken(newTokens.userId(), newTokens.accessToken());
return ResponseEntity.ok(ApiResponse.success(SuccessCode.TOKEN_REFERSH_SUCCESS, result));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package springboot.kakao_boot_camp.domain.auth.dto.loginDtos;

public record LoginReq(
String email,
String passWord
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package springboot.kakao_boot_camp.domain.auth.dto.loginDtos;

import springboot.kakao_boot_camp.security.CustomUserDetails;

public record LoginRes(
Long userId,
String accessToken,
String refreshToken
) {
public static LoginRes from(CustomUserDetails user, String accessToken, String refreshToken) {
return new LoginRes(
user.getId(),
accessToken,
refreshToken
);
}

public static LoginRes fromWithoutRefreshToken(Long userId, String accessToken) {
return new LoginRes(
userId,
accessToken,
null
);
}
}
Loading