-
Notifications
You must be signed in to change notification settings - Fork 1
기사 등록 기능 추가 #20
기사 등록 기능 추가 #20
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.hwangwongyu.news.article; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.validation.BindingResult; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
import javax.validation.Valid; | ||
|
||
@RestController | ||
public class ArticleController { | ||
|
||
private final ArticleService articleService; | ||
|
||
public ArticleController(ArticleService articleService) { | ||
this.articleService = articleService; | ||
} | ||
|
||
@Value("${spring.webservice.newsHome}") | ||
private String newsHomeURL; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이런 config성 설정값들은 Controller 클래스보단 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. BindingResult 파라미터 바로 앞에 오는 Valid 어노테이션이 적용된 클래스의 필드들을 테스트
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 피드백 회신은 지금까지 작성한것중에서 가장 명확하고 정확하고 깔끔하네! |
||
} else { | ||
articleService.addArticle(article); | ||
return "redirect:" + newsHomeURL; | ||
} | ||
} | ||
|
||
@GetMapping("/articles/{id}") | ||
public ArticleDTO findArticleById(@PathVariable long id) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. 자세한 설명을 위해서 디버깅 스택 트레이스를 참고했고 중심 코드라고 판단한 부분만 가져와봤어
결국 조사해보니 3. 에서 언급한
이해가 잘 안되는 부분이 있다면 회신해줘 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HTTP 통신의 첫 처리를 DispatcherServlet.doService()에서 한다고 했는데 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
위 코멘트의 설명 첫부분의 이전 단계까지 추가적으로 보완해서 과정을 설명해볼게 우선 서버로 온 URL 요청(Request)으로 서블릿 컨테이너(Spring Boot니까 Embedded Tomcat)에 의해 메모리에 Servlet 인스턴스가 있는지 먼저 확인하고 이제 Request는 웹 컨테이너의 스레드 풀로부터 하나의 스레드를 할당받아서 테스트
그러면
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
HandlerMethodArgumentResolver 동작원리 핵심 아래 args 정보는 내부의
|
||
ArticleDTO article = articleService.findArticleById(id); | ||
if(article != null) | ||
return article; | ||
else | ||
return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. 연동된 서비스에 맞게 Model에다가 View에 사용할 데이터를 전달하고, 그래야 어떤 에러가 있는지, 정상동작을 한건지 알고 처리할 수 있어
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그러면 ResponseEntity 로 변경해야겠네~ |
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package com.hwangwongyu.news.article; | ||
|
||
import lombok.AccessLevel; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import org.hibernate.validator.constraints.Length; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.format.annotation.DateTimeFormat; | ||
|
||
import javax.validation.constraints.*; | ||
import java.time.LocalDateTime; | ||
|
||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PRIVATE) | ||
public class ArticleDTO { | ||
|
||
private Long id; | ||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") | ||
private LocalDateTime createTime; | ||
@Length(min = 4, max = 100, message = "기사 제목은 {min} ~ {max}자로 작성해야 합니다.") | ||
@NotBlank(message = "기사 제목을 입력해주세요.") | ||
private String title; | ||
@Length(min = 10, max = 10000, message = "기사 내용은 {min} ~ {max}자로 작성해야 합니다.") | ||
@NotBlank(message = "기사 내용을 입력해주세요.") | ||
private String content; | ||
private Integer sectionId; | ||
private Integer companyId; | ||
private Long editorId; | ||
private Long viewCount; | ||
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") | ||
private LocalDateTime modificateTime; | ||
|
||
@Override | ||
public String toString() { | ||
return "ArticleDTO{" + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. 후에 기사 정보에 대한 파일 출력, 디버깅에 사용하려면 toString()을 Override하지 않으면 '클래스명@16진수 해시코드' 형식으로 표시되는데, |
||
"id=" + id + | ||
", createTime=" + createTime + | ||
", title='" + title + '\'' + | ||
|
||
", sectionId=" + sectionId + | ||
", companyId=" + companyId + | ||
", editorId=" + editorId + | ||
", viewCount=" + viewCount + | ||
", modificateTime=" + modificateTime + | ||
'}'; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.hwangwongyu.news.article; | ||
|
||
import org.apache.ibatis.annotations.Mapper; | ||
|
||
@Mapper | ||
public interface ArticleMapper { | ||
|
||
Integer addArticle(ArticleDTO article); | ||
|
||
ArticleDTO findArticleById(long id); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.hwangwongyu.news.article; | ||
|
||
public interface ArticleService { | ||
|
||
Integer addArticle(ArticleDTO article); | ||
|
||
ArticleDTO findArticleById(long id); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.hwangwongyu.news.article; | ||
|
||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public class ArticleServiceImpl implements ArticleService { | ||
|
||
private final ArticleMapper articleMapper; | ||
|
||
@Autowired | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. 자세한 설명을 위해서 디버깅 스택 트레이스를 참고했고 중심 코드라고 판단한 부분만 가져와봤어
그리고 이게 가능했던 이유 중 가장 핵심이 되는 인터페이스인 구글링하면 사람들이
결국 2. 에서 언급한 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more.
😰 우선 Autowiring 과정 설명 전에 내가 이전 코멘트에서
왜냐하면 인터페이스 계층구조를 보니 결국엔
다시
그래서 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 음 이자료는 훌륭한거같에 |
||
ArticleServiceImpl(ArticleMapper articleMapper) { | ||
this.articleMapper = articleMapper; | ||
} | ||
|
||
@Override | ||
public Integer addArticle(ArticleDTO article) { | ||
return articleMapper.addArticle(article); | ||
} | ||
|
||
@Override | ||
public ArticleDTO findArticleById(long id) { | ||
return articleMapper.findArticleById(id); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | ||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | ||
|
||
<mapper namespace="com.hwangwongyu.news.article.ArticleMapper"> | ||
|
||
<insert id="addArticle" parameterType="ArticleDTO"> | ||
INSERT INTO article (title, content, sectionId, companyId, editorId) | ||
VALUES (#{title}, #{content}, #{sectionId}, #{companyId}, #{editorId}) | ||
</insert> | ||
|
||
<select id="findArticleById" parameterType="long" resultType="ArticleDTO"> | ||
SELECT id, createTime, title, content, sectionId, companyId, editorId, viewCount, modificateTime | ||
FROM article | ||
WHERE id = #{id} | ||
</select> | ||
|
||
</mapper> |
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.
필드 주입 방식과 setter 주입 방식인 경우 final 키워드로 만들면 안돼
선택적 빈 주입의 경우(공식문서는
optional dependencies
라고 표현)final 키워드가 없으니 null 체크를 해주면 사용할 수 있겠네
실사용 예시는
애플리케이션 모니터링 API인 JMX에서 설정을 관리하는 리소스인 Mbean(Managed Bean)이 있어
설정 변경이 가능하게 getter, setter를 두고 Setter Injection을 하는거야
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.
immutable 하지 않아서 null로 변경 가능하게 되고
비즈니스 로직상에서 NPE가 발생할 상황이 생기게 돼
위 코드를 예시로
setService(Service service)
로 아직 주입을 하지 않은 상태에서아래처럼
Service
메서드인doSomething
의 호출이 가능하게 되고 이때 NPE가 발생하게 돼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.
내용은 맞는거같은데, 저 이해한 실습 자체를 복붙하는건 안좋은거같에
말로 풀어서 상대방이 이해하기 쉽게 말해주면 더 좋을거같에.