파일 구조
src/main/java
- /com/example/springbasicjsp
아래 파일 MvcPostFormServlet, MvcPostListServlet, MvcPstSaveServlet, Post, PostRepository, ServletInitializer, SpringBasicjspApplication(메인 실행)
src/webapp/WEB-INF/views
아래 파일
- post-form.jsp : 블고그 포스트 작성 페이지, 제목, 내용, 저장 버튼
- posts.jsp
- save-result.jsp
블로그 작성 폼 서블릿
localhost:8080/mvc/posts 로 들어가게 되면 post-form.jsp 의 view가 보임
MvcPostFormServlet 자바 파일로 들어가게 되면 import 구문 다음으로 아래 문구가 보임
@WebServlet : 서블릿 클래스를 선언하고 구성하는데 사용됨. (description, initParams, loadOnStartup 등의 속성도 있음, 여기서는 name과 urlPatterns만 사용함)
name 속성은 서블릿의 이름을 지정
urlPatterns 는 이 서블릿이 응답할 URL 패턴을 저장함 , 보면 ' localhost:8080/mvc/posts' 이부분 포트 뒤의 경로와 똑같은 걸 볼 수있었음
@WebServlet(
name = "mvcPostFormServlet",
urlPatterns = "/mvc/posts"
)
그 다음 줄인데 HttpServlet을 상속받아 작성한 MvcPostFormServlet 클래스임.
service 메소드를 오버라이딩 했는데 보면 Http 요청과 응답을 매개변수로 받고 ServletException이나 IOException의 예외가 발생할 시 밖으로 던진다고 되어 있음.
service 메소드 안쪽을 보면 path를 지정해주고 있음
여기서 [webapp/WEB-INF/views -> WEB-INF 내부의 파일은 외부에서 호출할 수 없다.] 이 주석이 정확히 이해할 수 없어서 찾아봄
보안 강화때문에 WEB-INF 디렉토리 안에 있는건 클라이언트에서 바로 접근이 불가능함.
원래는 보통 localhost:8080/post-form.jsp 으로 접근이 가능할 텐데 안됨.
보통 여기안에는 서블릿 클래스 파일, 설정 파일(web.xml), 라이브러리 JAR 파일 등을 넣어놓음.
이 내부의 파일을 이용하려면 'RequestDispatcher'를 이용해서 포워딩해야하는데 여기서는 선언했던 path를 넣어주고 포워드 하고 있는 걸 확인할 수 있음
public class MvcPostFormServlet extends HttpServlet {
@Override
protected void service(
HttpServletRequest request,
HttpServletResponse response
) throws ServletException, IOException {
// webapp/WEB-INF/views -> WEB-INF 내부의 파일은 외부에서 호출할 수 없다.
String path = "/WEB-INF/views/post-form.jsp";
// 이때 Controller에서 View로 이동
RequestDispatcher dispatcher = request.getRequestDispatcher(path);
// forward : Servlet에서 다른 Servlet이나 jsp 호출
dispatcher.forward(request, response);
}
}
블로그 글 저장 서블릿
MvcPostSaveServlet 파일 / Controller 역할
이 부분에서 PostRepository가 나오게 되는데 PostRepository클래스 안에는 private static final PostRepository instance = new PostRepository(); 으로 클래스 로드 시 한 번만 초기화되는 정적 인스턴스를 생성함. 그리고 getInstance() 메소드는 이 정적 인스턴스를 반환해서 항상 동일한 PostRepository가 되고 새로운 인스턴스를 생성하지 못하도록 함.
// + 이부분 이해가 안되서 튜터님께 가서 여쭤봤는데 보통 빈에 선언되는 변수들은 final static으로 하고, 보니까 intellij에서도 final을 지우니까 final쓰라고 권고해줌 !
private PostRepository postRepository = PostRepository.getInstance();
위에 했던다른 서블릿과 동일하게 name과 urlPattern을 정의해주고 있음
@WebServlet(
name = "mvcPostSaveServlet",
urlPatterns = "/mvc/posts/save"
)
service를 오버라이딩하고 매개변수 받는거랑 예외처리하는 부분은 위랑 똑같고.
request 객체로부터 "title"이름을 가진 파라미터와 "content" 이름을 가진 파라미터를 얻어와서 지역 변수에 저장함
여기서 request는 매개변수로 받았던 HttpServletRequest의 객체임
String title = request.getParameter("title");
String content = request.getParameter("content");
포스트 객체를 만들고 postRepository에 저장해줌
Post post = new Post(title, content);
postRepository.save(post);
save() 메소드에대해서 보자면 Post 클래스에는 HashMap이 'database'라는 이름으로 선언되어 잇음
전위 연산자라 incrementId는 1부터 시작해서 저장됨
Hashmap에 put을 이용해서 ket:post.getId()를 저장하고 값에 post 객체를 저장함
Q. HashMap이 선언되어 있는데 Key값이 Long이야, 근데 따로 equals가 재정의되지 않았는데 key값이 Long으로 되어있어서야? 근데 만약 멀티스레드 환경이면 key값이 겹치는 불상사가 생기지 않을까?
A. Long 타입을 사용한 이유가 Long 타입이 기본적으로 equals하고 hasCode 메소드를 재정의하고 있어서 관행적으로 키값으로 사용하는데, 멀티스레드 동시성 문제는 생길 수 있음(아직 이건 못배웟으니까 다음의 내가 다시 돌아와서 말할 것!)
public Post save(Post post) {
post.setId(++incrementId);
database.put(post.getId(), post);
return post;
}
해당 서블릿에서 생성된 이제 post라는 데이터를 request 객체의 속성으로 저장하는데 여기서 "post"로 들어간건 속성의 이름이고, post는 실제로 저장되는 데이터 객체임. 요롷게 저장하면 view에서 접근이 가능함 보니까 save-result.jsp 파일에서 ${post.getId()} 나 ${post.getTitle()} 등으로 사용하는걸 볼 수 있음
request.setAttribute("post", post);
아까랑 똑같이 jsp 파일 위치 path로 저장하고 포워드로 request랑 response 객체를 전달
String path = "/WEB-INF/views/save-result.jsp";
RequestDispatcher disPatcher = request.getRequestDispatcher(path);
disPatcher.forward(request, response);
위 내용들을 토대로 localhost:8080/mvc/posts에서 제목칸에 "제목입니다", 내용에 "내용" 입력
/mvc/posts/save 페이지로 이동하면서 글 아이디, 제목, 내용 출력 되는 것을 확인
블로그 글 조회 서블릿
이번 서블릿은 MvcPostListServlet 자바 파일
@WebServlet(
name = "mvcPostListServlet",
urlPatterns = "/mvc/posts/list"
)
여기서 선언되는 postRepository 객체는 위에서 사용한 MvcPostSaveServlet에서 사용한 객체와 같은 객체임(static final로 설정되어 있기 때문에 Application이 작동하는 동안에 바뀌지 않음)
private PostRepository postRepository = PostRepository.getInstance();
동일하게 서비스 메소드 오버라이딩, request, response 매개변수로 받고 예외 던지고.
List 선언. Post 전체 조회 부분
List<Post> posts = postRepository.findAll();
이건 PostRepository에 선언되어있는 findAll() 메소드의 구현 부인데, 여기서 database는 해시맵으로 구현되어 있는데 ArrayList로 반환할거니까 database를 컬렉션 타입으로 반환(values()메소드) 하면 Post 객체들의 컬렉션이 반환됨
// + 해시맵의 값들만 필요해서 키값은 반환값에 포함되지 않음, 키까지 같도싶으면 entrySet() 메소드를 사용해야함.
// Post 전체 조회
public List<Post> findAll() {
return new ArrayList<>(database.values());
}
jsp파일에서 posts를 사용할 수 있도록 request 객체에 저장함
request.setAttribute("posts", posts);
그리고 하던대로 요청, 응답 객체를 뷰에 넘김
String path = "/WEB-INF/views/posts.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(path);
dispatcher.forward(request, response);
MVC 모델 적용
뷰는 JSP파일
컨트롤러는 각 서블릿
모델은 여기서 Post와 PostRepository 클래스.
'개발 일기' 카테고리의 다른 글
2024-12-17 / Controller와 RestController 차이, Cookie,Session 메소드 재정리 (0) | 2024.12.17 |
---|---|
2024-11-14 / 최소공배수-최대공약수, 메소드 체이닝 (0) | 2024.11.14 |
2024-11-07 / PJ - 헤맸던 부분, Spirng - Item 정의, JAVA-Mysql 연결 및 쿼리 (0) | 2024.11.07 |