-
Notifications
You must be signed in to change notification settings - Fork 56
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
[Spring Core] (배포) 신혜빈 미션 제출합니다. #113
base: shin378378
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 혜빈님~ 망쵸입니다 😀
혜빈님이 미션 잘 진행하고 있단 얘기를 많이 들었어요. 저도 리뷰어로 함께 미션 진행해 보고 싶었는데, 마지막에 운 좋게 매칭되었네요! 재밌게 미션해 보아요~
몇 가지 코멘트 달았는데, 혜빈님 의견이 궁금한 것도 있어요. 읽어보고 답변해 주시면 감사할 것 같아요.
다음은 질문에 대한 답변이에요!
질문1: 왜 "roomescape"도 붙여야하는 지 모르겠습니다. @SpringBootApplication가 이미 roomescape 패키지 내에 위치하고 있는 데 굳이 "roomescape"를 붙여주어야 돌아가는 이유는 뭔가요?
저도 @SpringBootApplication
가 이미 roomescape
패키지 내에 위치하고 있는데, 왜 컴포넌트 스캔을 안하는지 궁금하네요 🙄 그래도 어쩌겠어요.. 스프링이 그렇게 동작하는걸요. 알고 있는 지식을 확실히 하기 위해 한 가지 가설을 세웠어요!
@SpringBootApplication
에@ComponentScan
이 중첩되어 있으면@ComponentScan
적용된다.
@SpringBootApplication
내부에 @ComponentScan
를 포함하고 있는 걸 아시나요?
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@ComponentScan
을 좀더 자세히 봐볼까요? 다음은 @ComponentScan
javadocs의 일부에요!
![스크린샷 2025-01-16 오후 12 07 28](https://private-user-images.githubusercontent.com/64410384/403655434-27f94c6b-5425-4c05-b096-cc11828cfd81.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyOTczMjYsIm5iZiI6MTczOTI5NzAyNiwicGF0aCI6Ii82NDQxMDM4NC80MDM2NTU0MzQtMjdmOTRjNmItNTQyNS00YzA1LWIwOTYtY2MxMTgyOGNmZDgxLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjExVDE4MDM0NlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTM2NjRhNGNiMTQ5NzA0N2NkZGFhNWJjZDk4OGU4ZjRmMTE5ZTgzYzY4ODZlNTFkZTkwYzg2ZTU5ZTdkY2NlNGQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.PwA3Zu5qtljVltJjgZ9d02nkSyQoLdjKPxUl1vSIY_I)
basePackages
에 명시한 패키지는 컴포넌트 스캔을 시작하는 지점이에요. 만약 이 옵션이 명시되어 있지 않다면, 어노테이션이 붙은 클래스의 패키지부터 재귀적으로 스캔한다고 하네요.
@SpringBootApplication
안에 @ComponentScan
은 옵션이 명시되어 있지 않아서, Application.class가 속한 패키지부터 스캔한다는 걸 알 수 있죠. 이것은 경험적으로도 모두 알고 있을거예요!
하지만 이 클래스에 @ComponentScan
를 추가하고 basePackages
옵션까지 명시하면 위 내용이 적용되지 않는 것 같아요. @ComponentScan
가 중첩되면서 @SpringBootApplication
내부에 있는 @ComponentScan
가 무시된 게 아닐가 싶어요.
좀더 확인하고 싶어서 간단한 프로젝트를 만들어서 확인해 봤어요! 패키지 구조는 다음과 같이 작성했어요.
![스크린샷 2025-01-16 오후 12 13 44](https://private-user-images.githubusercontent.com/64410384/403657181-62b8639e-8827-42d8-b073-a52fd8f89560.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkyOTczMjYsIm5iZiI6MTczOTI5NzAyNiwicGF0aCI6Ii82NDQxMDM4NC80MDM2NTcxODEtNjJiODYzOWUtODgyNy00MmQ4LWIwNzMtYTUyZmQ4Zjg5NTYwLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjExVDE4MDM0NlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWNjMDVmOGUzOTc2NjU2ZDUzYTRmOTNkNzhkM2NhNTJjNmE0ZTI2MTBhZjQ3NWE5M2QyNDU5Y2IwMTcxMTA3N2QmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.iRe8VF5D1cwdmgm3KszAR2D6Vz9d8IAFaMBDzO_SWZY)
Application을 구동하는 시점에 등록된 빈을 확인해 보았어요.
@SpringBootApplication
// @ComponentScan(basePackages = "com.example.live")
public class DemoApplication {
private static final Logger log = LoggerFactory.getLogger(DemoApplication.class);
@Autowired
private ApplicationContext ac;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@PostConstruct
public void init() {
log.info("빈 개수: {}", ac.getBeanDefinitionCount());
for (String name : ac.getBeanDefinitionNames()) {
log.info("빈 이름: {}", name);
}
}
}
@ComponentScan
의 basePackages
옵션을 변경해 보며 로그를 진행했어요.
- 옵션을 적용하지 않은 경우
- 빈 개수: 56
- 포함한 애플리케이션 빈:
demoApplication
,catService
- basePackages = "com.example.live" 옵션을 적용한 경우
- 빈 개수: 56
- 포함한 애플리케이션 빈:
demoApplication
,personService
- basePackages = "com.example.demo; com.example.live" 옵션을 적용한 경우
- 빈 개수: 57
- 포함한 애플리케이션 빈:
demoApplication
,catService
,personService
정확히 하기 위해서는 좀더 파봐야 할 것 같은데 이 정도면 충분하다고 생각해요!
질문2: 토큰으로부터 멤버를 찾는 로직은 제가 생각할 땐 member패키지보단 jwt패키지에 있는 게 더 어울린다고 생각하는 데 리뷰어님 의견은 어떤 지 궁금합니다!! jwt패키지에 넣는 것과 member패키지에 넣는 것 이외에도 좋은 의견이 있으시면 편하게 말씀해주세요 :)
저의 개인적인 생각을 공유해 볼게요!
member와 jwt 패키지 둘 중에 선택하자면 jwt를 선택하겠어요. 토큰이 member와 jwt 중에 어떤 거에 가까울까요? 저는 jwt라고 생각해요! 토큰이라는 워딩이 jwt 토큰에서 나왔기 때문이에요.
토큰으로부터 멤버를 찾는 로직
를 member에 둔다고 가정하면 member 도메인은 토큰 도메인을 알게 되어요. member는 그 자체로 순수한 도메인이어야 하는데 토큰을 아는 게 바람직할까요?
만약 jwt 인증 방식에서 세션 방식으로 변경한다면 어떻게 될까요? member 도메인 쪽에도 변경이 발생할 거예요. 인증 방식의 변경이 member 도메인에도 영향을 미치는 것은 바람직하지 않다고 생각해요. 그래서 저는 jwt 패키지를 선택할 것 같아요!
추가로 얘기해 보고 싶은 게 있다면 코멘트 또는 디스코드 DM을 보내주세요 😀
src/main/java/jwt/JwtUtils.java
Outdated
@Value("${jwt.secret}") | ||
public void setSecretKey(String secretKey) { | ||
this.secretKey = secretKey; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
메서드의 인자에 값을 주입할 수 있네요? 이렇게도 할 수 있군요~ 하나 배웠어요!
질문이에요. 이렇게 하면 어떤 장점이 있나요? 또 다른 방법은 무엇이 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<인자에 값을 주입하는 방법>
- 메서드 주입방식
@Value("${jwt.secret}")
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
- 장점 :
캡슐화
유지 가능, 값을 넣을 때 추가적인 로직 포함 가능 - 단점 : 필드 초기화 시점이 불명확. 외부에서 set 호출하지 않으면 값이 설정되지 않을 가능성 존재
--> 새롭게 알게된 점 :
- 1 :
캡슐화
란 무엇인가?
객체의 내부 상태(필드)를 외부에서 직접 접근하지 못하도록 보호하고,
접근을 제어하는 메서드를 통해 안전하게 값을 설정하거나 가져오도록 하는 것
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 필드주입 방식
ex)
@Value("${jwt.secret}")
private String secretKey;
- 장점 : 코드 간결, 직관적
- 단점 : 테스트하기 어려움, 의존성 확인이 어렵고 불변성 유지가 안 됨
--> 새롭게 알게된 점 :
-
1-1 : 왜 불변성 유지가 안 되는 가?
필드주입 방식은final
키워드를 사용하지 못 함
즉, 객체 생성 이후에도 값이 변경될 가능성이 있기 때문에 불변성이 보장 안 됨 -
1-2 : final 키워드를 왜 사용하지 못 하는 가?
-->@Value
어노테이션은 Spring이 객체를 생성한 후,Reflection
을 사용해서 값을 주입함
-->final
은 객체 생성 시 반드시 초기화가 되어야 하는 데,@Value
어노테이션은 객체를 생성할 당시 초기화를 시키지 않기 때문에final
과@Value
는 함께 사용할 수 없음 -
1-3 :
Reflection
기능이란 무엇인가?
Reflection
? Java에서 제공하는 컴파일 타임이 아니라, 런타임에 정보를 동적으로 조회하는 기능
(그래서 임의로 값 변경 로직 추가도 불가능) -
2-1 : 필드주입 방식은
캡슐화
인가?
필드주입 방식은캡슐화
의 일종이라고 볼 수 있으나 완전한캡슐화
는 아님. -
2-2 : 왜 필드주입 방식은 완전한
캡슐화
라고 보기 힘든가?
--> Spring 컨테이너가 강제로 값을 주입하기 때문
객체 스스로 값을 설정하는 구조가 아니라면,캡슐화
가 완전하다고 보기 어려움
--> 변경과 테스트가 어려움
값을 직접 설정할 수 있는 방법이 없고 단위 테스트에서 값을 주입하는 게 어려움
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 생성자 주입방식 (추천 방식)
ex)
public JwtProvider(@Value("${jwt.secret}") String secretKey) {
this.secretKey = secretKey;
}
- 장점 : 불변성 보장, 테스트가 용이
- 단점 : 주입할 인자가 많아질 수록 코드가 길어짐
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ConfigurationProperties
사용 방식 (추천 방식)
ex) 설정
@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class JwtConfiguration {
}
ex) 클래스 정의
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
private final String secret;
public JwtProperties(String secret) {
this.secret = secret;
}
}
ex) 사용 예시
@Component
public class JwtUtils {
private final JwtProperties jwtProperties;
public JwtUtils(JwtProperties jwtProperties) {
this.jwtProperties = jwtProperties;
}
- 장점 : 설정 값이 많을 때 깔끔하게 관리 가능, 유지보수 쉬움, 객체화 가능, 테스트 용이
- 단점 : 사용하려면 별도의 프로퍼티 클래스를 만들어야 함
--> 새롭게 알게된 점 :
-
1 :
@Configuration
이란?
해당 클래스를 Spring의 설정 클래스(Configuration Class)로 등록하는 어노테이션
설정클래스는 개발자가 직접 객체를 생성하고, 관리하도록 할 수 있음 -
2-1 :
@EnableConfigurationProperties(JwtProperties.class)
이란?
JwtProperties 클래스를 Spring Boot의 구성 속성(Properties)으로 활성화하는 어노테이션 -
2-2 :
@Bean
사용이 아니라@EnableConfigurationProperties(JwtProperties.class)
를 사용하는 이유?
Spring Boot는@EnableConfigurationProperties
를 사용하여@ConfigurationProperties
가 붙은 클래스를 감지하고, application.yml의 값을 읽어 자동으로 주입함. 그러나@Bean
을 사용하면 Spring이 해당 객체를 직접 생성하므로,@ConfigurationProperties
바인딩 과정이 수행되지 않음. -
3 :
@ConfigurationProperties(prefix = "jwt")
란?
Spring Boot에서 외부 설정 파일(application.yml 또는 application.properties)의 값을 객체로 매핑해주는 어노데이션
-> 즉, jwt.로 시작하는 설정 값을 자동으로 필드에 주입
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<결과>
주로 설정값이 적을 때는 생성자 주입방식이,
설정값이 많을 때는 @ConfigurationProperties
사용 방식이 추천됨
-->
확실히 생성자 주입방식과 @ConfigurationProperties
사용 방식이 큰~ 단점이 없어 사용하기 좋다는 생각이 듭니다.
현재 제 코드에서는 설정값이 적어 생성자 주입방식이 적합하지만, 제게 @ConfigurationProperties
사용 방식이 생소하기 때문에, 이번 기회에 @ConfigurationProperties
를 사용해 코드를 수정해주었습니다!!
망초님 덕분에 여러가지 값 주입법과 다양한 어노테이션을 많이 알아갑니당~ 감사합니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<질문>
- 제가 찾아본 이 4가지 방법 외에 망초님께서 알고계신 값 주입방식이 있나요? 제가 추가적으로 알아두면 좋을 만한 것들이요!
- 망초님은 주로 인자에 값을 저장할 때 어떤 방식을 사용하시나요? 그 방식을 주로 사용하는 특별한 이유가 있나요?
src/main/java/jwt/JwtUtils.java
Outdated
import java.util.Date; | ||
|
||
public class JwtUtils { | ||
private static final long EXPIRATION_TIME = 86400000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
상수 분리 👍👍 86400000은 하루를 밀리초로 표현한 것 같아요. 다음은 제안사항인데 혜빈님의 의견이 궁금해요!
86400000의 단위가 밀리초라는 걸 표현하면 어떨까요?
private static final long EXPIRATION_TIME = 86400000; | |
private static final long EXPIRATION_TIME_MILLIS = 86400000; |
86400000가 하루라는 걸 표현하면 어떨까요?
private static final long EXPIRATION_TIME = 86400000; | |
private static final long EXPIRATION_TIME_MILLIS = 1000 * 60 * 60 * 24; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오! 굉장히 좋은 생각인 거 같아요!!
저는 86400000라는 숫자가 밀리초를 의미한다는 걸 알지만 코드를 처음 보는 사람들은 헷갈릴 수 있겠다는 생각이 드네요!!
EXPIRATION_TIME_MILLIS = 1000 * 60 * 60 * 24;
확실히 이렇게 표시해주는 게 다른 개발자들을 배려해주는 표기법인 거 같습니다!!
좋은 제안 감사합니다!! 망초님 조언대로 코드 변경하였습니다 :)
@Configuration | ||
public class JwtConfiguration { | ||
|
||
@Value("${jwt.secret}") | ||
private String secretKey; | ||
|
||
@Bean | ||
public JwtUtils jwtUtils() { | ||
return new JwtUtils(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요구사항대로 @Configuration
과 @Bean
을 사용해서 빈 등록을 잘 해주었어요~
추가로 빈(Bean) 자동 등록과 수동 등록에 대해 알아볼까요?
- 둘은 어떤 차이가 있을까요?
- 어떤 장단점이 있을까요?
현재 jwt 패키지에서 등록하는 빈은 자동 등록과 수동 등록이 혼재되어 있어요. 일관성 있게 수정해 볼까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<빈(Bean) 등록방식>
- 자동 등록
@Component
,@Service
,@Repository
,@Controller
등의 어노테이션을 사용하여 클래스에 명시하면
Spring이 자동으로 빈으로 등록
(참고 :@ComponentScan
을 사용하면 특정 패키지를 스캔하여 자동으로 빈을 찾아 등록)
-> 장점
- 직관적
간단한 어노테이션만 추가하면 자동으로 빈 등록 - 코드 간결
따로@Bean
메서드를 만들 필요가 없어 코드가 깔끔해짐 - 유지보수 용이
@ComponentScan
을 통해 여러 빈을 한 번에 등록할 수 있어 수동으로 관리할 필요가 없음
개발자가 별도로 빈을 등록하지 않아도 되므로 빠르게 개발 가능
-> 단점
- 제어하기 어려움
특정한 빈이 등록되지 않는 경우 원인을 파악하기 어려움 - 명확성이 떨어짐
빈의 로직이 코드에서 드러나지 않으므로, 빈의 역할을 이해하기 어려움
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 수동 등록
@Configuration
클래스 내에서@Bean
어노테이션을 사용하여 직접 빈을 등록하는 방식
-> 장점
- 명확한 빈 관리 가능
어떤 빈이 언제, 어떻게 생성되는지@Bean
메서드를 통해 명시적으로 정의 가능함. - 조건부 빈 등록이 가능
@Conditional
,@Profile
등을 활용해 특정 환경에서만 빈을 등록할 수 있음. - 복잡한 조작 가능
-> 단점
- 코드가 길어짐
@Configuration
클래스 내에서@Bean
을 일일이 선언해야 해서 코드가 길어짐 - 일관성 유지가 어려움
여러 개발자가 협업하는 경우에 빈마다 자동 등록할지 또는 수동 등록할지를 정해두지 않으면 관리가 어려워질 수 있음 - 새로운 빈을 추가할 때마다 설정 클래스를 수정
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<두 방식의 "주요" 차이점>
자동등록 : Spring이 빈을 등록
수동등록 : 개발자가 직접 빈을 등록
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<결과>
현재 저의 코드에서는 딱히 복잡한 조작이 필요하지 않기 때문에 jwt 패키지에서 등록하는 빈은 전부 자동 등록으로 처리해주었습니다!!
생각하지 못한 포인트였는 데 짚어주셔서 감사합니다. 덕분에 수동등록, 자동등록에 대해 더 깊이 이해하게 된 거 같아요 ㅎㅎ
@Component | ||
@Profile("test") | ||
public class TestDataLoader implements ApplicationRunner { | ||
|
||
@Autowired | ||
private EntityManager entityManager; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Profile에 따라 초기 데이터를 잘 세팅했네요 👍 프로젝트할 때 Profile 다루는 건 문제 없겠어요!
제안 사항이에요! TestDataLoader는 테스트에 필요한 데이터를 세팅하는 클래스 같아요. 테스트에 필요한 클래스가 프로덕트 코드보다는 테스트 코드 영역으로 옮기면 좋을 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오! TestData를 test패키지로 뺄 생각은 못 했네요 ㅎㅎ
TestData는 테스트코드에서만 쓰니까 테스트 코드 영역으로 가는 게 맞겠어요!!
생각지 못한 관점이예요 감사합니다 :)
src/main/java/jwt/JwtService.java
Outdated
Member member = memberRepository.findById(userId).get(); | ||
if (member == null) { | ||
throw new IllegalArgumentException("토큰으로부터 유저를 찾을 수 없습니다."); | ||
} | ||
return member; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional의 orElseThrow 메서드를 사용해서 가독성을 개선해 볼까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제가 Optional
을 많이 안 써봐서 Optional
로도 처리할 수 있는 지 몰랐네요 ㅜㅠ
Optional
과 orElseThrow()
메소드를 사용하여 아래와 같이 코드를 리팩토링해주었습니다!!
덕분에 새로운 방법을 또 알아가네용 :)
return memberRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("토큰으로부터 유저를 찾을 수 없습니다."));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<새롭게 알게된 점>
-
1-1 :
Optional<T>
란 무엇인가?
Optional<T>
는 값이 있을 수도 있고 없을 수도 있는 객체를 감싸는 컨테이너 클래스
(참고)null
을 직접 다루지 않고, 안전하게 값을 처리할 수 있도록 도와주는 도구 -
1-2 :
Optional
은null
을 직접 다루지 않는 데 그럼에도null
이 들어오면 어떻게 되는 가?
null
이 들어오면 아무 일도 일어나지 않음, 그냥 지나감 -
1-3 :
null
이 들어왔을 때 따로 처리할 수 있는 방안이 있는가?
Optional
메소드들을 통해 따로 처리해줄 수 있음
ex)
ifPresentOrElse()
→ 값이 있으면 실행, 없으면 다른 동작 수행
orElseThrow()
→ 값이 없을 때 예외 발생
orElse()
→ 값이 없을 때 기본값 반환
orElseGet()
→ 값이 없을 때 동적으로 기본값 생성
< 망초님 답변에 대한 리뷰 >
제가 좀 더 찾아봤는 데 생각보다 간단한 원리였어요.
직접 실험까지 해주시고 너무 감사합니다!! 이건 절대 안 까먹을 거 같아요 ㅎㅎ 뇌리에 박히네용
|
안녕하세요 :) 망초 리뷰어님!! ☘️☘️
벌써 마지막 pr이라니... 시원섭섭하네요 ㅜㅠ
주환님께 한 번쯤 pr받아보고 싶었는 데 마지막에 받게 되게 영광입니다 ><
방금 제 1번째 PR을 다시 보고 왔는데, 정말 엉망이더라고요 ㅎㅎ☺️
아직 많이 부족하긴 하지만 그래도 여기까지 성장할 수 있었던 건 리뷰어님 덕분입니다 :)
마지막 pr도 잘 부탁드립니다!!
<구현과정>
7단계 -
@Configuration
8단계 -
Profile과 Resource
9단계 -
배포 스크립트
(아직 안 함, 이번 주 스터디에서 같이 한다고 공지 받음)<궁금한 점>
1) 메인클래스의
@ComponentScan(basePackages = {"roomescape", "jwt"})
부분문제상황 1
JWT 관련 로직을
roomescape패키지
와 같은 계층의 패키지의 클래스로 분리했는 데 코드가 돌아가지 않았고메인코드(RoomescapeApplication.class)가
roomescape패키지
안에 있어서 이 패키지 밖에 있는 컴포넌트를 탐색하지 않기 때문이라는 점을 알게 되었습니다.--> 해결
메인클래스(RoomescapeApplication.class) 위에
@ComponentScan(basePackages = {"jwt"})
를 붙여 스프링 빈으로 등록할 컴포넌트들을 탐색하도록 지시해주었습니다.문제상황 2
하지만 이 컴포넌트를 붙인 이후로 http://localhost:8080/login 페이지가 열리지 않습니다.
--> 해결
임시방편으로
@ComponentScan(basePackages = {"roomescape", "jwt"})
를 붙여주어 코드를 잘 돌아가게는 해두었습니다.질문
하지만 왜 "roomescape"도 붙여야하는 지 모르겠습니다.
@SpringBootApplication
가 이미roomescape 패키지
내에 위치하고 있는 데 굳이 "roomescape"를 붙여주어야 돌아가는 이유는 뭔가요?2) Jwt의 DB 접근을 부분
문제상황
이번 미션에서는 Jwt의 DB접근 부분을 최소화하라는 요구조건이 있었는 데요.
최대한 Jwt가 DB접근을 최소화하도록 구현했지만
JwtService
클래스에서의getMemberFromToken
메소드 때문에 MemberRepository를 참조하게 됩니다.질문
토큰으로부터 멤버를 찾는 로직은 제가 생각할 땐 member패키지보단 jwt패키지에 있는 게 더 어울린다고 생각하는 데 리뷰어님 의견은 어떤 지 궁금합니다!! jwt패키지에 넣는 것과 member패키지에 넣는 것 이외에도 좋은 의견이 있으시면 편하게 말씀해주세요 :)