Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
d0eac68
test: 1단계,2단계 추가
chaehyunL Jun 28, 2026
53bba1c
feat:LoginController 생성
chaehyunL Jun 28, 2026
24ee8ee
feat:LoginRequest 생성
chaehyunL Jun 28, 2026
54aee6d
feat: findById 메서드 추가
chaehyunL Jun 28, 2026
0365054
feat: LoginService 생성
chaehyunL Jun 28, 2026
143eb7d
feat: JwtProvider 생성
chaehyunL Jun 29, 2026
ecc8025
feat: LoginMember 생성
chaehyunL Jun 29, 2026
80053ba
feat: LoginMember 핸들러 생성
chaehyunL Jun 29, 2026
ab18aef
feat: LoginMember create 메서드에 추가
chaehyunL Jun 29, 2026
66728ba
feat: String name 이용
chaehyunL Jun 29, 2026
74f36bd
feat: loginMember 생성
chaehyunL Jun 29, 2026
f0ce4c0
style: ctrl + alt+ l 개행정리
chaehyunL Jun 29, 2026
44ad820
feat: WebConfig 생성
chaehyunL Jun 29, 2026
c5ae254
docs:README 파일 생성
chaehyunL Jun 29, 2026
05a06d3
feat: createToken 메서드 생성
chaehyunL Jun 30, 2026
7e0c71b
feat: token에 관한 책임 분리
chaehyunL Jun 30, 2026
95f915d
feat: resolveArgument 메서드 책임 분리
chaehyunL Jun 30, 2026
165b552
refector: checklogin 메서드 변경
chaehyunL Jun 30, 2026
40aff17
refector: http 관련 기능을 controller 로 이동
chaehyunL Jun 30, 2026
578d9a3
style: 개행정리
chaehyunL Jun 30, 2026
af5ff58
refector: 메서드 입력 객체 loginMember로 변경
chaehyunL Jul 1, 2026
b0692c5
test: 3단계 테스트 생성
chaehyunL Jul 3, 2026
d87a302
feat:resorce.properties 생성
chaehyunL Jul 4, 2026
4836e92
refector: secretKey 숨김
chaehyunL Jul 4, 2026
d808ed6
refector: extractToken 제거
chaehyunL Jul 4, 2026
4c1ffe7
feat:Login 핸들러 인터셉터 등록
chaehyunL Jul 4, 2026
f77ce23
feat:Login 핸들러 인터셉터 등록
chaehyunL Jul 4, 2026
0de02ba
refector: preHandle 내용 수정
chaehyunL Jul 4, 2026
2e26756
refector: 뭔래 있던 properties 중복되어서 삭제함
chaehyunL Jul 4, 2026
4730128
refector: jwt.secret 주석 처리 해제
chaehyunL Jul 4, 2026
0eae6c3
refector: jwt.secret->roomescape.auth.jwt.secret
chaehyunL Jul 4, 2026
653e960
refector:admin/** path 추가
chaehyunL Jul 4, 2026
8bb5385
style: 개행 정리
chaehyunL Jul 4, 2026
488f861
refector: Component 등록
chaehyunL Jul 4, 2026
aa2b5fa
feat: expiretime 구현
chaehyunL Jul 4, 2026
8895666
feat: TokenNotFoundException 생성
chaehyunL Jul 5, 2026
4184b79
refector: Exception 패키지 내로 이동
chaehyunL Jul 5, 2026
3bd4251
feat: Token 검증 로직 추가
chaehyunL Jul 5, 2026
ec8004e
feat: token 추출 방식 변경
chaehyunL Jul 6, 2026
8957601
feat: accessToken과 refreshToken으로 나눔
chaehyunL Jul 6, 2026
20a948c
feat: accessToken과 refreshToken으로 나눔
chaehyunL Jul 6, 2026
f948434
feat: accessToken이 정보를 받아오도록 변경
chaehyunL Jul 6, 2026
93465ad
feat: findById 메서드 구현
chaehyunL Jul 6, 2026
2fba555
feat: token->accessToken/refreshToken으로 구분
chaehyunL Jul 6, 2026
49b6d7f
docs:ReadMe 파일 작성
chaehyunL Jul 6, 2026
13b8925
refector: token 관련 수정
chaehyunL Jul 10, 2026
715960c
feat: cookieManager 생성
chaehyunL Jul 10, 2026
a272863
refector: cookieManager 이용해서 수정
chaehyunL Jul 10, 2026
cee6ff6
refector: 오류검증 로직 추가
chaehyunL Jul 10, 2026
9fda054
Merge branch 'chaehyunl' into mvc3
chaehyunL Jul 10, 2026
767742f
refector: gradle 의존성 대체
chaehyunL Jul 8, 2026
a3eebbe
feat:JPA 관련 설정 추가
chaehyunL Jul 8, 2026
262a651
feat: Entity 생성
chaehyunL Jul 8, 2026
c5cc7a3
feat: Entity 생성
chaehyunL Jul 8, 2026
6fff1f3
refector:JPA 방식으로 변경
chaehyunL Jul 8, 2026
4518389
refector: JPA 변경에 따른 수정
chaehyunL Jul 8, 2026
b9cc04b
refector: JPA 변경에 따른 수정
chaehyunL Jul 8, 2026
31d685e
refector: JPA 변경에 따른 수정
chaehyunL Jul 8, 2026
9c9ae75
refector: value->time
chaehyunL Jul 8, 2026
e3f65ef
refector: value->time
chaehyunL Jul 8, 2026
35ebc38
test: Jpa test와 기존 테스트 분리
chaehyunL Jul 8, 2026
0f3d928
findByEmail 메서드 추가
chaehyunL Jul 8, 2026
77abb17
refector:data.spl로 변경
chaehyunL Jul 8, 2026
cae9a52
refector:value->time_value
chaehyunL Jul 8, 2026
dd40b5a
refector: schema 삭제
chaehyunL Jul 8, 2026
07c4f55
refector: protected 추가
chaehyunL Jul 8, 2026
1ff8645
refector: time_value 수정
chaehyunL Jul 8, 2026
1373c71
refector: entityManager
chaehyunL Jul 15, 2026
3db99b7
style:ctrl_alt_l
chaehyunL Jul 15, 2026
80797fa
style:ctrl_alt_l
chaehyunL Jul 15, 2026
cd1a819
feat: entity매니저로 변경
chaehyunL Jul 15, 2026
74c0595
Merge branch 'chaehyunl' into JPA2
chaehyunL Jul 15, 2026
313e5ee
feat: entity매니저로 변경
chaehyunL Jul 15, 2026
fd3e903
docs:README 파일 생성
chaehyunL Jul 15, 2026
12e9014
feat: 필요 메서드 생성
chaehyunL Jul 15, 2026
7bb3fe5
feat: 필요 메서드 생성
chaehyunL Jul 15, 2026
72b8311
feat: 필요 메서드 생성
chaehyunL Jul 15, 2026
63b2eb1
refector:네이밍 변경
chaehyunL Jul 17, 2026
13f3d39
refector:네이밍 변경
chaehyunL Jul 17, 2026
5405303
refector:time_value->timeValue 이름 변경
chaehyunL Jul 17, 2026
6c6a2e9
refector:cookieManager 사용
chaehyunL Jul 17, 2026
e52407f
refector:JPA 식으로 변경
chaehyunL Jul 18, 2026
cb1863e
refector:Reservation 수정
chaehyunL Jul 18, 2026
2fb95bf
refector:JPA 수정
chaehyunL Jul 19, 2026
afcde38
refector: entity 삭제
chaehyunL Jul 19, 2026
a6013d1
refector: member 구조 변경
chaehyunL Jul 19, 2026
7be91b0
refector: 5단계 spl 변경
chaehyunL Jul 19, 2026
235214c
style: 개행정리
chaehyunL Jul 19, 2026
8fca327
test:6단계 추가
chaehyunL Jul 20, 2026
a610636
feat:6단계 관련 추가
chaehyunL Jul 20, 2026
f188d27
feat:6단계 관련 추가
chaehyunL Jul 20, 2026
aabe511
docs: README 수정
chaehyunL Jul 20, 2026
b930ef6
refector:waiting 추가
chaehyunL Jul 20, 2026
10eb09f
feat:waiting 관련 java 추가
chaehyunL Jul 20, 2026
129ab0d
feat:waiting 관련 java 추가
chaehyunL Jul 20, 2026
bcc5974
refecter: 메서드 형식 한개로 바꿈
chaehyunL Jul 21, 2026
9e41a00
feat:ThemeService 생성
chaehyunL Jul 21, 2026
315b5ae
feat:ThemeService 생성
chaehyunL Jul 21, 2026
1c74bc1
refector: Reservation dto 객체 가지지 않도록 수정
chaehyunL Jul 24, 2026
0b719fd
refector: 개행정리
chaehyunL Jul 24, 2026
4d167b5
feat: unique 제약 조건 추가
chaehyunL Jul 25, 2026
6c2cee4
feat: 어노테이션 추가
chaehyunL Jul 25, 2026
e152c74
feat: @Getter 어노테이션 추가
chaehyunL Jul 25, 2026
7477b48
feat: WaitingRepository 생성
chaehyunL Jul 25, 2026
d6e9530
refector: dto 형태 record로 변경
chaehyunL Jul 25, 2026
1cc41fb
refector: dto class->record 수정에 따른 변경
chaehyunL Jul 25, 2026
418bb7c
feat:Builder 생성
chaehyunL Jul 25, 2026
415b116
refector: Builder 사용
chaehyunL Jul 25, 2026
ce208a9
feat: JwtUtils 생성
chaehyunL Jul 26, 2026
343886c
feat: JwtConfig 생성
chaehyunL Jul 26, 2026
95b0f3d
refector:JwtProvider 삭제
chaehyunL Jul 26, 2026
e338ce7
refector:JwtUtils 관련 변경
chaehyunL Jul 26, 2026
1d08094
test: 7단꼐 생성
chaehyunL Jul 26, 2026
c153761
test: 8단계 생성
chaehyunL Jul 26, 2026
0b1be93
feat:dataloader 생성
chaehyunL Jul 26, 2026
787759e
docs:README 파일 생성
chaehyunL Jul 26, 2026
2a8661e
feat: 배포용 스크립트 생성
chaehyunL Jul 26, 2026
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
8 changes: 7 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

implementation 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.0.0'

Expand All @@ -27,6 +27,12 @@ dependencies {
testImplementation 'io.rest-assured:rest-assured:5.3.1'

runtimeOnly 'com.h2database:h2'

compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
}

test {
Expand Down
14 changes: 14 additions & 0 deletions script/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
git pull code JPA4
./gradlew clean build
PID=$(cat application.pid 2>/dev/null)

if [ -n "$PID" ]; then
kill -15 "$PID"
sleep 5
fi

JAR=$(find build/libs -name "*.jar" | head -n 1)

nohup java -jar "$JAR" > application.log 2>&1 &

echo $! > application.pid
Comment on lines +1 to +14

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 과정이 어떤 과정인지 옆에 주석으로 달아주셔도 좋을 것 같아요!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분을 ai 도움을 많이 받은 것 같습니다.

아직 script 파일이 어떻게 작성되어야 하고 어떻게 쓰여야 하는지 잘 감이 안 잡힙니다.

제가 봤을때는 docs README 파일과 차이를 잘 모르겠다는 느낌이 드는 것 같아요

주석은 달았습니다 ~~!!.

Empty file added script/setup.sh
Empty file.
29 changes: 15 additions & 14 deletions src/main/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,28 @@
안녕하세요. 세종대학교 그리디 백엔드 4기 이채현입니다.


---
## Spring MVC 미션 3단계 - 관리자 기능

어드민 페이지 진입은 admin 권한이 있는 사람만 할 수 있도록 제한한다.
만약 진입 권한이 없다면 401 코드를 응답하도록 한다.

---
### 고민한 내용 🤔
## Spring JPA Spring Core 배포

- HandlerInterceptor에서 기본적으로 preHandle/postHandle/afterCompletion으로 나뉘어져있던데,
이번의 경우에는 admin페이지 접속만을 확인하면 되는거라서 preHandle만을 사용했습니다.
이경우에는 postHandle과 afterCompletion을 어떻게 구성하면 좋을지..? 알고 싶네요.
### 🚀 7단계 - @Configuration
JWT 관련 로직을 roomescape와 같은 계층의 auth 패키지의 클래스로 분리하고,
불필요한 DB 접근을 최소화하기.


- WebConfig 같은 경우에는, admin 페이지를 접속할때의 role만을 확인해주면 되므로 /admin/***을 사용해주었습니다.
### 🚀 8단계 - Profile과 Resource

Comment on lines +15 to 16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Profile과 Resource는 각각 어떤 의미인가요?

profile은 어떤거고 언제 사용하나요~?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Profile의 경우, 실행 환경에 따라 다른 설정을 사용하기 위한 기능이고,
Resource는, 프로젝트 안에 있는 파일이나 외부 파일을 의미합니다.

Profile은 어디에서 사용하냐면요, 애플리케이션을 실행하는 환경을 구분해야할때 사용합니다.

- token 만료시간도 있어야할 것 같아서 expire_time을 이용해서,만료시간을 구현해보았습니다. 만료시간을 구현할 더 좋은
방법이 있을지?? 궁금합니다.
schema.sql 대신 데이터베이스를 초기화 해주기 위해 실행하는 클래스를 생성.
스프링이 실행될 때 동작해야 함.
token 생성에 필요한 비밀키값을 외부 파일로 분리하기.

- token을 accessToken과 refreshToken으로 나눠서 구현해주었는데, 구현하다보니 너무 어려워서 제대로 구현된건지 잘
모르겠습니다.
### 🚀 9단계 - 배포 스크립트

ec2나 서버에서 배포를 할 수 있게 배포 스크립트를 작성하기.

---
### 고민한 내용 🤔
1. JwtProvider를 auth package를 생성해서,JwtConfig와 JwtUtils로 분리해줬는데, 이렇게 구성하면 장점이 궁금합니다.

배포스크립트와 초기 설정의 시나리오가 어떻게 사용되는지 잘 모르겠습니다...

18 changes: 18 additions & 0 deletions src/main/java/auth/JwtConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package auth;

import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.nio.charset.StandardCharsets;
import java.security.Key;

@Configuration
public class JwtConfig {
@Bean
public JwtUtils jwtUtils(@Value("${roomescape.auth.jwt.secret}")String secret){
Key key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
return new JwtUtils(key);
}
Comment on lines +13 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config에서 하고 있는 것은 무엇일까요? 그럼 그것을 할때 이점은 없을까요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config의 역할

소프트웨어 설정 저장/ 시스템 및 네트워크 구성 관리/ 보안 및 접근 제어 입니다.

비밀키의 변경이 용이하게 config를 통해 가능하게 됩니다

}
54 changes: 54 additions & 0 deletions src/main/java/auth/JwtUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package auth;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import roomescape.member.Member;

import java.security.Key;
import java.util.Date;


public class JwtUtils {
private final Key secretKey;
private final long expireAccessTime = 30L * 60 * 1000;
private final long expireRefreshTime = 14L * 24 * 60 * 60 * 1000;

public JwtUtils(Key secretKey) {
this.secretKey = secretKey;
}

public String createAccessToken(Member member) {
return createToken(member.getId(),expireAccessTime);
}

public String createRefreshToken(Member member) {
return createToken(member.getId(),expireRefreshTime);
}
private String createToken(Long memberId, long expireTime){
Date now= new Date();

return Jwts.builder()
.setSubject(memberId.toString())
.setIssuedAt(now)
.setExpiration(new Date(now.getTime()+expireTime))
.signWith(secretKey)
.compact();
}

public Long getMemberId(String token) {
Claims claims = parseClaims(token);
return Long.parseLong(claims.getSubject());
}

private Claims parseClaims(String token) {
try {
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
throw new IllegalArgumentException("유효하지 않거나 만료된 토큰입니다.", e);
}
}
}
4 changes: 2 additions & 2 deletions src/main/java/roomescape/CookieManager.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package roomescape;

import jakarta.servlet.http.Cookie;
import org.springframework.stereotype.Component;
import roomescape.exception.TokenNotFoundException;

import java.util.Arrays;

@Component
public class CookieManager {


public String extractToken(Cookie[] cookies, String cookieName) {
return Arrays.stream(cookies)
.filter(cookie -> cookieName.equals(cookie.getName()))
Expand Down
60 changes: 0 additions & 60 deletions src/main/java/roomescape/JwtProvider.java

This file was deleted.

2 changes: 1 addition & 1 deletion src/main/java/roomescape/RoomescapeApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@SpringBootApplication(scanBasePackages={"roomescape","auth"})
public class RoomescapeApplication {
public static void main(String[] args) {
SpringApplication.run(RoomescapeApplication.class, args);
Expand Down
41 changes: 41 additions & 0 deletions src/main/java/roomescape/dataLoader/DataLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package roomescape.dataLoader;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import roomescape.member.Member;
import roomescape.member.MemberRepository;


@Component
public class DataLoader implements CommandLineRunner {

@Autowired
private MemberRepository memberRepository;

public DataLoader(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}

@Override
public void run(String ...args){
if(memberRepository.count()>0){
return;
}

Member admin= Member.builder()
.name("어드민")
.email("admin@email.com")
.password("password")
.role("ADMIN").build();

Member brown= Member.builder()
.name("브라운")
.email("brown@email.com")
.password("password")
.role("USER").build();

memberRepository.save(admin);
memberRepository.save(brown);
}
}
109 changes: 109 additions & 0 deletions src/main/java/roomescape/dataLoader/TestDataLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package roomescape.dataLoader;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import roomescape.member.Member;
import roomescape.member.MemberRepository;
import roomescape.reservation.Reservation;
import roomescape.reservation.ReservationRepository;
import roomescape.theme.Theme;
import roomescape.theme.ThemeRepository;
import roomescape.time.Time;
import roomescape.time.TimeRepository;

@Component
public class TestDataLoader implements CommandLineRunner {
Comment on lines +16 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 이 TestDataLoader는 어떤 역할을 하고 있나요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트용 데이터를 대신 생성해주고 있습니다


@Autowired
private final ThemeRepository themeRepository;
private final TimeRepository timeRepository;
private final ReservationRepository reservationRepository;
private final MemberRepository memberRepository;

public TestDataLoader(ThemeRepository themeRepository, TimeRepository timeRepository,
ReservationRepository reservationRepository, MemberRepository memberRepository) {
this.themeRepository = themeRepository;
this.timeRepository = timeRepository;
this.reservationRepository = reservationRepository;
this.memberRepository = memberRepository;
}

@Override
public void run(String... args) {
Member admin = memberRepository.findByName("ADMIN")
.orElseThrow();
Member brown = memberRepository.findByName("브라운")
.orElseThrow();
if (themeRepository.count() > 0) {
return;
}
Theme theme1 = Theme.builder()
.name("테마1")
.description("테마1입니다.")
.build();
Theme theme2 = Theme.builder()
.name("테마2")
.description("테마2입니다.")
.build();
Theme theme3 = Theme.builder()
.name("테마3")
.description("테마3입니다.")
.build();

themeRepository.save(theme1);
themeRepository.save(theme2);
themeRepository.save(theme3);

if (timeRepository.count() > 0) {
return;
}
Time time1 = Time.builder()
.value("10:00")
.build();
Time time2 = Time.builder()
.value("12:00")
.build();
Time time3 = Time.builder()
.value("14:00")
.build();
Time time4 = Time.builder()
.value("16:00")
.build();
Time time5 = Time.builder()
.value("18:00")
.build();
Time time6 = Time.builder()
.value("20:00")
.build();
timeRepository.save(time1);
timeRepository.save(time2);
timeRepository.save(time3);
timeRepository.save(time4);
timeRepository.save(time5);
timeRepository.save(time6);

reservationRepository.save(
Reservation.builder()
.member(admin)
.date("2024-03-01")
.time(time1)
.theme(theme1)
.build());
reservationRepository.save(
Reservation.builder()
.member(admin)
.date("2024-03-01")
.time(time2)
.theme(theme2)
.build());
reservationRepository.save(
Reservation.builder()
.member(brown)
.date("2024-03-01")
.time(time3)
.theme(theme3)
.build());
}
}
Loading