Skip to content

[그리디] 이창희 Spring Core (배포) 7,8,9 단계 제출합니다 #182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 22, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,77 @@



<br>

---
## 🚀 7단계 - @Configuration

### 📝 기능 요구사항
✅ JWT 관련 로직을 roomescape와 같은 계층의 auth 패키지의 클래스로 분리하세요.
✅ 불필요한 DB 접근을 최소화 하세요.



### 💻 구현 전략

1. jwt 모듈 분리
➡️ Jwt 토큰을 발급, 파싱했던 `JwtTokenProvider`와
➡️ Jwt관련 환경변수를 담은 `JwtProperties` 클래스를 외부 jwt 패키지로 분리하고
➡️ 해당 객체들을 `JwtConfig`를 통해 빈으로 등록

이렇게 분리된 설정을 Application 클래스에 `@Import`




<br>

---
## 🚀 8단계 - Profile과 Resource

### 📝 기능 요구사항
✅ schema.sql 대신 데이터베이스를 초기화 해주기 위해 실행하는 클래스를 만드세요.
✅ token 생성에 필요한 비밀키값을 외부 파일로 분리하세요



### 💻 구현 전략

1. DB 초기화
➡️ Production용과 DataLoader
➡️ Test용 TestDataLoader를 따로 만들어 DB 초기화

2. prod 환경과 test 환경 분리
➡️ test 에 관한 설정은 test 패키지 하위 application.yml 파일 생성





<br>

---
## 🚀 9단계 - 배포 스크립트

### 📝 기능 요구사항
✅ ec2나 서버에서 배포를 할 수 있게 배포 스크립트를 작성하세요.



### 💻 구현 전략

1. EC2 인스턴스 생성



2. 인스턴스에 ssh 접속 후 메모리 스왑 설정
➡️ 부족한 인스턴스의 메모리를 디스크를 활용해 메모리의 내용을 스왑함으로써, 사용가능한 메모리 상한 올림


3. github 에 코드를 pull


4. 프로젝트 빌드


5. 빌드된 jar 파일 백 그라운드로 실행
15 changes: 15 additions & 0 deletions src/main/java/jwt/JwtConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package jwt;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class JwtConfig {

@Bean
public JwtTokenProvider jwtTokenProvider(JwtProperties jwtProperties) {
return new JwtTokenProvider(jwtProperties);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roomescape.auth;
package jwt;

import org.springframework.boot.context.properties.ConfigurationProperties;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package roomescape.auth;
package jwt;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;
import roomescape.auth.AuthException;
import roomescape.exception.ApplicationException;
import roomescape.member.domain.Member;

import java.util.Date;

@Component
public class JwtTokenProvider {

private final String secretKey;
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/roomescape/DataLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package roomescape;

import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import roomescape.member.domain.Member;
import roomescape.member.domain.MemberRepository;
import roomescape.member.domain.Role;

@Profile("prod")
@Component
public class DataLoader implements CommandLineRunner {

private final MemberRepository memberRepository;

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

@Override
@Transactional
public void run(String... args) throws Exception {
Member admin = new Member("어드민", "admin@email.com", "password", Role.ADMIN);
Member user = new Member("브라운", "brown@email.com", "password", Role.USER);
memberRepository.save(admin);
memberRepository.save(user);
}
}
5 changes: 3 additions & 2 deletions src/main/java/roomescape/RoomescapeApplication.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package roomescape;

import jwt.JwtConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.Import;

@SpringBootApplication
@ConfigurationPropertiesScan(basePackages = "roomescape")
@Import(JwtConfig.class)
public class RoomescapeApplication {
public static void main(String[] args) {
SpringApplication.run(RoomescapeApplication.class, args);
Expand Down
1 change: 1 addition & 0 deletions src/main/java/roomescape/auth/AdminAuthInterceptor.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package roomescape.auth;

import jwt.JwtTokenProvider;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package roomescape.auth;

import jwt.JwtTokenProvider;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
Expand Down
1 change: 1 addition & 0 deletions src/main/java/roomescape/common/CookieUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class CookieUtil {
public static void setToken(String accessToken, int cookieExpirationSeconds, HttpServletResponse response) {
Cookie cookie = new Cookie("token", accessToken);
cookie.setHttpOnly(true);
cookie.setSecure(true);
cookie.setPath("/");
cookie.setMaxAge(cookieExpirationSeconds);
response.addCookie(cookie);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import roomescape.auth.AuthenticatedMember;
import roomescape.auth.JwtProperties;
import roomescape.auth.JwtTokenProvider;
import jwt.JwtProperties;
import jwt.JwtTokenProvider;
import roomescape.auth.LoginMember;
import roomescape.common.CookieUtil;
import roomescape.member.application.MemberService;
Expand Down
14 changes: 0 additions & 14 deletions src/main/resources/application.properties

This file was deleted.

27 changes: 27 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
server:
port: 8080

spring:
profiles:
active: prod

h2:
console:
enabled: true
path: /h2-console

datasource:
url: jdbc:h2:mem:database

jpa:
show-sql: true
properties:
hibernate:
format_sql: true
ddl-auto: create

Choose a reason for hiding this comment

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

ddl-auto 값을 운영 쪽은 create로, 테스트는 create-drop으로 잡으셨는데
create를 쓰더라도 애플리케이션 시작할 때 기존 테이블이 있으면 drop돼요!
의도하신 로직이실까요?

Copy link
Author

Choose a reason for hiding this comment

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

그렇게 큰 뜻은 없고,

테스트가 끝나면 테이블을 정리 하는 것이
흐름이 더 깔끔한것 같아서 create-drop을 썼어요! 😅
(인 메모리 DB를 쓰고 있으니까 create을 써도 크게 차이가 없을 것 같긴 하네요)

Choose a reason for hiding this comment

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

ㅎㅎ이해했습니다!


roomescape:
auth:
jwt:
secret: Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E=
expiration: 1800000
24 changes: 0 additions & 24 deletions src/main/resources/data.sql

This file was deleted.

13 changes: 12 additions & 1 deletion src/test/java/roomescape/JpaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import roomescape.time.domain.Time;
import roomescape.time.domain.TimeRepository;

import static org.assertj.core.api.Assertions.*;


@DataJpaTest
public class JpaTest {

@Value("${roomescape.auth.jwt.secret}")
private String secretKey;

@Autowired
private TestEntityManager entityManager;

Expand All @@ -26,6 +32,11 @@ public class JpaTest {

Time persistTime = timeRepository.findById(time.getId()).orElse(null);

Assertions.assertThat(persistTime.getValue()).isEqualTo(time.getValue());
assertThat(persistTime.getValue()).isEqualTo(time.getValue());
}

@Test
void 팔단계() {
assertThat(secretKey).isNotBlank();
}
}
20 changes: 20 additions & 0 deletions src/test/java/roomescape/MissionStepTest.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package roomescape;

import jwt.JwtTokenProvider;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.stereotype.Component;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import roomescape.reservation.presentation.response.MyReservationResponse;
import roomescape.reservation.presentation.response.ReservationResponse;
import roomescape.waiting.presentation.response.WaitingResponse;
Expand All @@ -20,8 +25,17 @@

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
@ActiveProfiles("test")
public class MissionStepTest {

@LocalServerPort
private int port;

@BeforeEach
void setUp() {
RestAssured.port = port;
}

@Test
void 일단계() {
String token = createToken("admin@email.com", "password");
Expand Down Expand Up @@ -154,4 +168,10 @@ private static String createToken(String email, String password) {

assertThat(status).isEqualTo("1번째 예약대기");
}

@Test
void 칠단계() {
Component componentAnnotation = JwtTokenProvider.class.getAnnotation(Component.class);
assertThat(componentAnnotation).isNull();
}
}
Loading