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.
전반적으로 코드가 많이 깔끔해졌네
이번 브랜치 PR은 목적의식도 뚜렷하고 노력한 결과가 보였어
특히 PR 처음에 기사 스크린샷으로 어떤 작업을 명시한건지가 좋았어
리뷰 한 내용부터 우선순위를 높게해서 회신주면 확인해볼께~
@RestController | ||
public class ArticleController { | ||
|
||
private final ArticleService articleService; |
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.
final 키워드를 사용한 의도가 무엇일까?
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.
final 키워드 적용된 타입의 빈이 생성됨과 동시에 주입되는 것을 보장해서
NPE를 방지하는것과 immutable한 필드를 만드는 의도가 있어
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.
articleService를 final키워드로 만들면 안되는 상황,
articleService를 immutable하게 하지않으면 발생하는 상황 예시를 구체적으로 들어주라.
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.
articleService를 final키워드로 만들면 안되는 상황,
필드 주입 방식과 setter 주입 방식인 경우 final 키워드로 만들면 안돼
선택적 빈 주입의 경우(공식문서는 optional dependencies
라고 표현)
final 키워드가 없으니 null 체크를 해주면 사용할 수 있겠네
실사용 예시는
애플리케이션 모니터링 API인 JMX에서 설정을 관리하는 리소스인 Mbean(Managed Bean)이 있어
설정 변경이 가능하게 getter, setter를 두고 Setter Injection을 하는거야
public interface HelloMBean {
public void sayHello();
public int add(int x, int y);
public String getName();
public int getCacheSize();
public void setCacheSize(int size);
}
JMX 예제 코드의 setCacheSize
메서드가 그 예시겠네
이 내용은 내가 JMX 모니터링 실습까지 해보진 못해서 이해도가 낮은점 참고해줘
그래서 다른 분이 진행한 실습 결과를 가져와봤어
위의 private int cacheSize
가 public synchronized void setCacheSize(int size)
덕분에 동적 설정이 가능해
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.
articleService를 immutable하게 하지않으면 발생하는 상황 예시를 구체적으로 들어주라.
immutable 하지 않아서 null로 변경 가능하게 되고
비즈니스 로직상에서 NPE가 발생할 상황이 생기게 돼
위 코드를 예시로 setService(Service service)
로 아직 주입을 하지 않은 상태에서
아래처럼 Service
메서드인 doSomething
의 호출이 가능하게 되고 이때 NPE가 발생하게 돼
controller.setService(new Service() {
@Override
public void doSomething() {
System.out.println("Anonymous class is doing something");
}
});
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.
articleService를 final키워드로 만들면 안되는 상황,
필드 주입 방식과 setter 주입 방식인 경우 final 키워드로 만들면 안돼
선택적 빈 주입의 경우(공식문서는
optional dependencies
라고 표현)
final 키워드가 없으니 null 체크를 해주면 사용할 수 있겠네실사용 예시는
애플리케이션 모니터링 API인 JMX에서 설정을 관리하는 리소스인 Mbean(Managed Bean)이 있어
설정 변경이 가능하게 getter, setter를 두고 Setter Injection을 하는거야public interface HelloMBean { public void sayHello(); public int add(int x, int y); public String getName(); public int getCacheSize(); public void setCacheSize(int size); }
JMX 예제 코드의
setCacheSize
메서드가 그 예시겠네이 내용은 내가 JMX 모니터링 실습까지 해보진 못해서 이해도가 낮은점 참고해줘
그래서 다른 분이 진행한 실습 결과를 가져와봤어위의
private int cacheSize
가public synchronized void setCacheSize(int size)
덕분에 동적 설정이 가능해
내용은 맞는거같은데, 저 이해한 실습 자체를 복붙하는건 안좋은거같에
말로 풀어서 상대방이 이해하기 쉽게 말해주면 더 좋을거같에.
} | ||
|
||
@Value("${spring.webservice.newsHome}") | ||
private String newsHomeURL; |
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.
이런 config성 설정값들은 Controller 클래스보단
config class를 따로 만들어서 첫 로드시에 설정되게 하는게 가독성 유지보수성 면에서 더좋아~
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.
이 내용은 'URLPatternConfig 클래스 생성' 이슈 #22 로 분리해서 반영할게
@PostMapping("/articles") | ||
public String addArticle(@Valid @RequestBody ArticleDTO article, BindingResult result) { | ||
if (result.hasErrors()) { | ||
return "articles/createOrUpdateArticleForm"; |
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.
BindingResult 클래스에 hasErrors가 true가 되는 조건은 무엇일까??
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.
BindingResult 파라미터 바로 앞에 오는 Valid 어노테이션이 적용된 클래스의 필드들을
JSON HTTP 메시지 바디에 있는 각각의 'Key-Value'와 바인딩하는 도중
유효성 검사 어노테이션이 적용된 필드의 애트리뷰트 조건과 비교해 맞지 않으면
MethodArgumentNotValidException 예외가 발생하면서
BindingResult 의 hasErrors가 true가 돼
테스트
예외 발생 테스트로 위 JSON 데이터 중 content key의 value값이 "가나다"로 3글자인데 최소 10글자가 되도록 @Length를 적용했어
@Length(min = 10, max = 10000, message = "기사 내용은 {min} ~ {max}자로 작성해야 합니다.")
@NotBlank(message = "기사 내용을 입력해주세요.")
private String content;
그래서 위처럼 content 필드와 관련된 메시지가 List<ObjectError> errors 에 담겨져 오는걸 볼 수 있어
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.
이 피드백 회신은 지금까지 작성한것중에서 가장 명확하고 정확하고 깔끔하네!
} | ||
|
||
@GetMapping("/articles/{id}") | ||
public ArticleDTO findArticleById(@PathVariable long id) { |
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.
@PathVariable
가 http 통신시에 어떻게 동작하는지 최대한 자세하게 설명 요망
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.
자세한 설명을 위해서 디버깅 스택 트레이스를 참고했고 중심 코드라고 판단한 부분만 가져와봤어
-
우선 HTTP 통신의 첫 처리를
DispatcherServlet.doService()
에서DispatcherServlet.doDispatch()
로 위임하고
Handler에 Request를 전달하기 위해서
Request(GET /articles/3
), Response, Handler(ArticleController.findArticleById(long)
) 로handle()
을 호출해
-
그럼
RequestMappingHandlerAdapter
에서는 실행할 Handler 메서드에 Request를 지정(Adapt)하고
-
InvocableHandlerMethod
에서는getMethodArgumentValues()
에서HandlerMethodArgumentResolver
를 활용해
argument(3)를 가져와서 바인딩해
-
doInvoke()
를 모두 거치면 Handler(ArticleController.findArticleById(@PathVariable long id)
)의 파라미터에
'3'이 전달된 후 컨트롤러 단 로직이 실행돼
결국 조사해보니 3. 에서 언급한 HandlerMethodArgumentResolver
가 @PathVariable
파라미터 바인딩의 핵심 역할을 하는거였네
Strategy interface for resolving method parameters into argument values in the context of a given request.
해석 : 주어진 Request 컨텍스트에서 메서드 parameter(정의)에 argument(실제 값)를 적용하기 위한 전략을 갖는 인터페이스입니다.
이해가 잘 안되는 부분이 있다면 회신해줘
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.
HTTP 통신의 첫 처리를 DispatcherServlet.doService()에서 한다고 했는데
대상 클라이언트의 ip port정보나 패킷처리 정보는 설명이 부족한거같네.
@PathVariable
설명은 HandlerMethodArgumentResolver가 @PathVariable 파라미터 바인딩의 핵심 역할한다고
잘찾은거같에 추가적으로 설명 부탁해~
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.
HTTP 통신의 첫 처리를 DispatcherServlet.doService()에서 한다고 했는데
대상 클라이언트의 ip port정보나 패킷처리 정보는 설명이 부족한거같네.
위 코멘트의 설명 첫부분의 이전 단계까지 추가적으로 보완해서 과정을 설명해볼게
우선 서버로 온 URL 요청(Request)으로 서블릿 컨테이너(Spring Boot니까 Embedded Tomcat)에 의해
Request, Response 객체가 생성되고 DispatcherServlet
실행을 시도하게돼
메모리에 Servlet 인스턴스가 있는지 먼저 확인하고
만약 컨테이너에 DispatcherServlet
의 객체가 없다면, 즉 처음 실행한다면
HttpServlet에서 Override한 Init
메서드를 호출해 객체를 생성하고 초기화해
이제 Request는 웹 컨테이너의 스레드 풀로부터 하나의 스레드를 할당받아서
DispatcherServlet
이 HttpServlet
의 service(ServletRequest, ServletResponse)
메서드를 호출하게돼
테스트
아래의 Request 예시는 UserController
의 findUserById
메서드를 호출하기 위한 URI야
service(ServletRequest, ServletResponse)
메서드 에서
대상 클라이언트의 IP, Port 정보를 포함한 HTTP GET Request 정보와 Response 정보를 받아서
service(HttpServletRequest, HttpServletResponse)
메서드에서 doGet
메서드를 호출해
그러면 FrameworkServlet
클래스의 processRequest
메서드에서
DispatcherServlet
이 Request를 실제로 핸들링 할 수 있게 doService
추상 메서드를 호출하면
FrameworkServlet
를 상속한 DispatcherServlet
이
Request 정보를 실제 핸들러에 위임하기 위해서 doDispatch
메서드를 호출해
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.
@PathVariable
설명은 HandlerMethodArgumentResolver가 @PathVariable 파라미터 바인딩의 핵심 역할한다고
잘찾은거같에 추가적으로 설명 부탁해~
HandlerMethodArgumentResolver 동작원리 핵심
아래 args 정보는 getMethodArgumentValues
메서드로 가져오는데
내부의 resolvers
인 HandlerMethodArgumentResolverComposite
타입으로
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
(생략)
// resolver이 지원하지 않는 파라미터면 예외처리
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// resolver에서 값을 바인딩
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
if(article != null) | ||
return article; | ||
else | ||
return null; |
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.
return null; 을 하면 클라이언트 입장에서
해당값이 에러가 있는건지 정상동작을 한건지
이해하지 못하지 않을까?
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.
연동된 서비스에 맞게 Model에다가 View에 사용할 데이터를 전달하고,
반환값으로 ViewResolve가 가능하도록 View 이름을 전달하고,
로직에 따른 HTTP 상태 코드, HTTP 엔티티 헤더/바디를 전달해야해
그래야 어떤 에러가 있는지, 정상동작을 한건지 알고 처리할 수 있어
ResponseEntity<T>
로 구현할 수 있겠네
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.
그러면 ResponseEntity 로 변경해야겠네~
|
||
@Override | ||
public String toString() { | ||
return "ArticleDTO{" + |
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.
toString() 메서드를 쓰는 이유에대해서 말해주라~
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.
후에 기사 정보에 대한 파일 출력, 디버깅에 사용하려면
결국 사람이 해석해서 활용 가능한 데이터로 표시해야한다고 생각했어
toString()을 Override하지 않으면 '클래스명@16진수 해시코드' 형식으로 표시되는데,
이 형식만 가지고는 특정 기사에 대한 데이터를 얻지 못해서야
|
||
private final ArticleMapper articleMapper; | ||
|
||
@Autowired |
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.
@Autowired
어노테이션 사용시
spring이 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.
자세한 설명을 위해서 디버깅 스택 트레이스를 참고했고 중심 코드라고 판단한 부분만 가져와봤어
-
DefaultListableBeanFactory
클래스의preInstantiateSingletons
메서드에서
생성자 타입인articleServiceImpl
빈의 이름을 토대로 빈을 생성하고
-
AbstractAutowireCapableBeanFactory
클래스의createBeanInstance
메서드에서
determineConstructorsFromBeanPostProcessors
메서드로
Autowire DI 대상이 될 수 있는 생성자 정보(생성자 클래스ArticleServiceImpl
, 파라미터ArticleMapper
)를 받아 반환하고
-
ConstructorResolver
클래스의autowireConstructor
메서드에서
생성자 정보를 토대로 빈을 생성하고 설정해두면
-
후에 리플렉션 기반으로 생성자 인스턴스를 생성(
newInstance
)해서
ArticleServiceImpl
생성자에ArticleMapper
를 DI 해주는거야
AbstractAutowireCapableBeanFactory
를 조사해보니 여기서 생성자 기반 빈 생성, 생성자 autowiring 을 제공하는거였어
아래 설명 3번째 문단과 4번째 문단에 내용에서 찾아볼 수 있었어
Abstract bean factory superclass that implements default bean creation,
with the full capabilities specified by the RootBeanDefinition class.
해석 : 기본 빈 생성을 구현하는 추상 빈 팩토리 상위클래스이며,
RootBeanDefinition 클래스에서 지정한 모든 기능을 사용할 수 있다.
Implements the AutowireCapableBeanFactory interface
in addition to AbstractBeanFactory's createBean(java.lang.Class<T>) method.
해석 : AutowireCapableBeanFactory 인터페이스를 구현하며
빈 생성을 담당하는 메서드인 AbstractBeanFactory 클래스의 createBean 메서드가 있다.
Provides bean creation (with constructor resolution), property population,
wiring (including autowiring), and initialization.
해석 : 어떤 생성자 기반의 빈 생성을 할지, 엔티티에서 프로퍼티 설정은 어떻게 할지,
빈 간의 관계 설정(자동설정 포함)과 초기화에 대한 기능을 제공한다.
Handles runtime bean references, resolves managed collections, calls initialization methods, etc.
Supports autowiring constructors, properties by name, and properties by type.
해석 : 런타임 빈 참조, 스프링에서 관리되는 컬렉션 타입 선택, 초기화 메서드 호출 등을 다룬다.
생성자 autowiring, 이름에 의한 프로퍼티 설정, 타입에 의한 프로퍼티 설정을 지원한다.
그리고 이게 가능했던 이유 중 가장 핵심이 되는 인터페이스인 SmartInstantiationAwareBeanPostProcessor
도 알게됐어
구글링하면 사람들이 BeanPostProcessor
가 Autowiring을 해준다고 설명하던데
이건 애플리케이션 단에서 구현할 때의 얘기고
내가 알아낸 이 인터페이스는 스프링 프레임워크 단에서 Autowiring을 제공해주는 거였어
아래 설명 2번째 문단에서 애플리케이션 단 구현 부분이 있다는 정보를,
1번째 문단에서 프레임워크 단 구현 부분이 있다는 정보를 찾아볼 수 있었어
NOTE: This interface is a special purpose interface,
mainly for internal use within the framework.
해석 : 이 인터페이스는 특별한 목적의 인터페이스이며,
주로 프레임 워크 내부에서 사용된다.
In general, application-provided post-processors
should simply implement the plain BeanPostProcessor interface
or derive from the InstantiationAwareBeanPostProcessorAdapter class.
해석 : 일반적으로, 애플리케이션 단에서 제공되는 post processor는
BeanPostProcessor 인터페이스를 구현하는 클래스거나
InstantiationAwareBeanPostProcessorAdapter 클래스를 상속하는 클래스다.
New methods might be added to this interface even in point releases.
해석 : 부수적인 릴리즈 버전에서도 새 메서드가 추가될 수 있다.
결국 2. 에서 언급한 AbstractAutowireCapableBeanFactory
와
후에 언급한 SmartInstantiationAwareBeanPostProcessor
가 -> ⚠이 코멘트에서 정정
@Autowired 생성자 DI의 핵심 역할을 하는거였네
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.
-
리플렉션 기반으로 생성자 인스턴스를 생성(newInstance)해서 ArticleServiceImpl 생성자에 ArticleMapper 를 DI 해주는거야
-> 이부분 리플렉션이 어떻게 DI해주는지 조금더 자세히 설명 부탁해 -
SmartInstantiationAwareBeanPostProcessor 이 인터페이스가 어떤 과정으로 Autowiring 해주는건데?
Note나 공식 도큐먼트를 통쨰로 가져오기 보단 너가 이해한걸 한글로 풀어쓰는게 리뷰어를 배려한다고 생각해.
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.
- 리플렉션 기반으로 생성자 인스턴스를 생성(newInstance)해서 ArticleServiceImpl 생성자에 ArticleMapper 를 DI 해주는거야
-> 이부분 리플렉션이 어떻게 DI해주는지 조금더 자세히 설명 부탁해
위 코멘트에 이어서 설명할게
-
우선 앞서 받은 생성자 정보에는 생성자 타입과 파라미터 타입 정보가 있으므로
ConstructorResolver
클래스의Instantiate
메서드에서 리플렉션 기반의 빈 생성을 시도해
-
그러면 빈 생성 헬퍼 클래스인
BeanUtils
의instantiateClass
메서드에서 생성자 정보를 받아
생성자 클래스인Constructor
의T newInstance
메서드를 호출하고
-
생성자 파라미터
ArticleMapper
, 생성자 타입ArticleServiceImpl
정보를
sun.reflect.ConstructorAccessor
에서 제공하는newInstance
메서드에 넘겨줘서
빈을 생성하는 네이티브 메서드를 호출하게해
-
이렇게 리플렉션을 통해 빈이 생성됐다면
그 빈을 읽어들이고 컨텍스트들의 검증하고 합친 다음 빈 간의 의존관계를 트리 형태로 관리해
빈 정보에는 생성자와 빈 간의 의존관계가 있으니
이걸 Dependency Injection 단계에서 최종적으로ArticleServiceImpl
빈을 생성하고ArticleMapper
빈을 DI 해줄 수 있겠네
위 코드의 설명 순서들은 생성자 주입 방식의 동작과정과 연관돼있어
먼저 빈을 생성하는게 아니라 생성자로 객체를 생성하는 시점에 필요한 빈을 주입해
좀 더 자세히 말하자면
먼저 생성자 파라미터에 사용되는 빈을 찾거나 빈 팩토리에서 빈을 만들고,
그 후에 찾은 파라미터 빈으로 DI 빈의 생성자를 호출하는 구조인거지
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.
Note나 공식 도큐먼트를 통쨰로 가져오기 보단 너가 이해한걸 한글로 풀어쓰는게 리뷰어를 배려한다고 생각해.
내가 이해한 내용이 들어있는 부분만 요약해서 가져온건데 실수로 번역을 안해뒀네
번역이랑 해석한 문단 위치까지 반영해뒀어
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.
- SmartInstantiationAwareBeanPostProcessor 이 인터페이스가 어떤 과정으로 Autowiring 해주는건데?
😰 우선 Autowiring 과정 설명 전에 내가 이전 코멘트에서
SmartInstantiationAwareBeanPostProcessor
가 생성자 DI 핵심 역할이라 정리한건 정정해야겠어
결국 2. 에서 언급한 AbstractAutowireCapableBeanFactory와
후에 언급한 SmartInstantiationAwareBeanPostProcessor가
@Autowired 생성자 DI의 핵심 역할을 하는거였네
왜냐하면 인터페이스 계층구조를 보니 결국엔 BeanPostProcessor
인터페이스를 상속하므로
역시 Autowiring에는 BeanPostProcessor
인터페이스가 핵심이라고 말하는게 맞는것 같아!
BeanPostProcessor
의 구현체인 AutowiredAnnotationBeanPostProcessor
가 빈의 초기화 라이프 사이클 이전,
즉 빈이 생성되기 전에 @Autowired가 붙어있으면 해당하는 빈을 찾아서 주입해주는 작업을 하는거야
아래 참고 이미지에서는 11번 단계가 되겠네
다시 SmartInstantiationAwareBeanPostProcessor
Autowiring 과정으로 돌아와서
테스트 대상 클래스인 ArticleServiceImpl
은 SmartInstantiationAwareBeanPostProcessor
메서드로
Autowiring 된게 아니지만 과정을 최대한 적어봤어
DefaultListableBeanFactory
클래스에 저장중인 모든 빈 정보들 중doGetBeanNamesForType
메서드로
Autowiring될 빈을 탐색하는 과정중에 타입으로 빈 이름을 찾는 단계가 있어
AbstractFactoryBean
의isTypeMatch
메서드로 해당 타입을 검사해서 예측되는 빈 타입이 있다면
AbstractAutowireCapableBeanFactory
클래스의predictBeanType
메서드에서
캐싱해둔AutowiredAnnotationBeanPostProcessor
로
SmartInstantiationAwareBeanPostProcessor
인터페이스의predictBeanType
메서드를 호출해
articleServiceImpl
이름을 가진 빈의 경우 3. 에서 설명한 메서드에서 예측되지 않아서
AbstractAutowireCapableBeanFactory
클래스의determineTargetType
메서드에서
팩토리 메서드로 타입을 찾을건지, 빈에 담긴 클래스 타입으로 찾을건지 검사해서 얻은 타입을 사용하게 됐어
articleServiceImpl
의 경우는 클래스 타입(ArticleServiceImpl
)으로 검사해서 타입을 얻었네
- 찾아낸
ArticleServiceImpl
타입으로
java.lang.reflect.Type
을 캡슐화한 리플렉션 타입인ResolvableType
의isAssignableFrom
메서드로
해당 타입이 할당가능한지 여부를 판단했더니 false가 나왔어
그래서 DefaultListableBeanFactory
클래스의 doGetBeanNamesForType
메서드 결과값 List에는
해당 타입을 추가할 수 없었어...
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.
머지해도 좋을것같네
고생했어~
@RestController | ||
public class ArticleController { | ||
|
||
private final ArticleService articleService; |
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.
articleService를 final키워드로 만들면 안되는 상황,
필드 주입 방식과 setter 주입 방식인 경우 final 키워드로 만들면 안돼
선택적 빈 주입의 경우(공식문서는
optional dependencies
라고 표현)
final 키워드가 없으니 null 체크를 해주면 사용할 수 있겠네실사용 예시는
애플리케이션 모니터링 API인 JMX에서 설정을 관리하는 리소스인 Mbean(Managed Bean)이 있어
설정 변경이 가능하게 getter, setter를 두고 Setter Injection을 하는거야public interface HelloMBean { public void sayHello(); public int add(int x, int y); public String getName(); public int getCacheSize(); public void setCacheSize(int size); }
JMX 예제 코드의
setCacheSize
메서드가 그 예시겠네이 내용은 내가 JMX 모니터링 실습까지 해보진 못해서 이해도가 낮은점 참고해줘
그래서 다른 분이 진행한 실습 결과를 가져와봤어위의
private int cacheSize
가public synchronized void setCacheSize(int size)
덕분에 동적 설정이 가능해
내용은 맞는거같은데, 저 이해한 실습 자체를 복붙하는건 안좋은거같에
말로 풀어서 상대방이 이해하기 쉽게 말해주면 더 좋을거같에.
if(article != null) | ||
return article; | ||
else | ||
return null; |
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.
그러면 ResponseEntity 로 변경해야겠네~
|
||
private final ArticleMapper articleMapper; | ||
|
||
@Autowired |
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.
음 이자료는 훌륭한거같에
ArticleDTO.java
주요 필드의 목적
(MySQL에서 각각 CURRENT_TIMESTAMP, CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 적용)
title, content 필드에 대한 유효성 검사
ArticleMapper.xml