remagine
혼자서 웹 서비스 만들어보기 - 6 본문
혼자서 웹 서비스 만들어보기 - 6
https://github.com/remagine/webNovel/tree/develop
사실 매일 조금씩 업데이트는 해왔는데 글로 정리를 못해 죄송하게 생각합니다.
오늘은 Story > Chapter 의 등록과 수정을 만들어 볼까 합니다.
의도한 Flow는 이렇습니다.
1. 글쓰기 버튼을 누른다.
2. 로그인 인터셉터로 session을 체크해서 로그인 페이지로 redirect 혹은 story list 페이지를 반환한다.
3. 사용자가 작성한 Story List가 나옵니다. 여기서 Register를 누릅니다.
4. 이제 Story 작성을 하고 등록을 누릅니다.
5. 등록 결과 메시지나 나오면서 수정페이지로 넘어갑니다. 오른쪽에 Chapter List를 볼 수 있는 navgation이 보여집니다.
6. add chapter를 눌러 chapter 등록/수정 페이지로 들어갑니다.
7. 등록에 성공하면 챕터 수정페이지로 들어서고, 오른쪽 Nav에 챕터 리스트가 늘어난 것을 확인 할 수 있습니다.
오늘은 이 Flow대로 적용할 수 있도록 코드를 작성해 보겠습니다.
1. Story Entity
1 2 3 4 5 | @OneToMany @OrderBy(clause = "id asc") @JoinColumn(name = "story") private List<Chapter> chapterList; | cs |
@OneToMany 필드를 추가합니다.
이렇게 설정한 후
FetchType.EAGER 혹은 Hibernate.initialize 를 사용하면 연관객체들을 담을 수 있습니다.
코드로 살펴보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @Transactional public Story get(int storyId, Member loginUser) { Story result = storyDao.get(storyId, loginUser); Hibernate.initialize(result.getChapterList()); return result; } @Transactional public List<Story> list(Member loginUser) { List<Story> storyList = storyDao.list(loginUser); for(Story story : storyList){ Hibernate.initialize(story.getChapterList()); } return storyList; } | cs |
기존 코드에서 Hibernate.initialize 라는 구문이 추가 되었습니다.
정확하게 설명드릴 수 없어 아쉽지만, @OneToMany로 연관되어 있는 다른 객체를 한 Transaction에서 담을 수 있는 방법입니다. 자주 쓰게 됩니다.
2. Chapter Entity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | @Entity @Table(name = "chapter") public class Chapter { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_chapter") @SequenceGenerator(name = "seq_chapter", sequenceName = "chapter_id_seq", allocationSize = 1) private Integer id; @Column(name = "title") private String title; @Column(name = "body") private String body; @ManyToOne @JoinColumn(name = "story") private Story story; @Enumerated(EnumType.STRING) @Column(name = "state") private State state; @Temporal(TemporalType.TIMESTAMP) @Column(name = "create_at", insertable = false, updatable = false) private Date createAt; @Column(name = "views") private Integer views; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public Story getStory() { return story; } public void setStory(Story story) { this.story = story; } public State getState() { return state; } public void setState(State state) { this.state = state; } public Date getCreateAt() { return createAt; } public void setCreateAt(Date createAt) { this.createAt = createAt; } public Integer getViews() { return views; } public void setViews(Integer views) { this.views = views; } } | cs |
Chapter 엔티티를 추가합니다. 특이사항이 있다면 State라는 EnumType을 연관시키기 위해서@Enumerated(EnumType.STRING)을 사용했습니다.
setState를 하고 싶을때 이제 String이 아닌 Enum의 필드값을 지정해줍니다.
3. Chapter Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @MemberRole @RequestMapping(value = "/create/{storyId}", method = RequestMethod.GET) public String create(@PathVariable("storyId") int storyId, Model model , HttpSession session, RedirectAttributes attrs){ Member loginUser = Logics.memberFromSession(session); Story story = storyService.get(storyId, loginUser); model.addAttribute("story", story); if(story != null){ return create(model); } else { ViewMessage.error().message("서버 오류로 저장에 실패했습니다.").register(attrs); return "redirect:/story/create"; } } public String create(Model model){
return "/story/chapter/edit";
} | cs |
add chapter의 url은 /story/create/{storyid} 형식입니다. get 메소드만을 받는 상황입니다. 링크로 접근하면 이 메소드로 맵핑이 됩니다.
@MemberRole은 이 메소드는 로그인 유저만 접근할 수 있도록 한 조치입니다.
그리고 반드시 session의 로그인된 유저 = Story 작성 유저 여야 합니다.
그러므로 현재 로그인된 유저와 story Id를 가지고 story를 가져오도록 합니다.
가져온 값이 null인지 아닌지로 분기처리를 하였습니다.
4. Chapter Controller - 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //chapter 등록 @MemberRole @RequestMapping(value = "/create/{storyId}", method = RequestMethod.POST) public String registChapter(@PathVariable("storyId") int storyId, Chapter chapter,Model model , HttpSession session, RedirectAttributes attrs){ Member loginUser = Logics.memberFromSession(session); Story story = storyService.get(storyId, loginUser); if(story != null){ chapter.setStory(story); chapter.setState(State.on); chapter.setViews(0); Integer id = chapterService.insert(chapter); return "redirect:/story/chapter/edit/"+story.getId() +"/"+id; } else { ViewMessage.error().message("서버 오류로 저장에 실패했습니다.").register(attrs); return "redirect:/story/create"; } } | cs |
이번엔 POST메소드를 받는 Controller입니다. 위 컨트롤러와 동일한 url이지만 get, post 메소드 구분을 통해 다른 메소드를 탈 수 있게 할 수 있습니다.
역시 session의 로그인된 유저 = Story 작성 유저 를 확인한 후
저장을 시키도록 합니다.
클라이언트의 Form 정보에는 story id, state, view정보는 없습니다. id 값은 insert할 때 자동으로 들어가지만, state와 view는 지정해 줘야 합니다.
저장이 잘 되었으면 chapter 수정화면으로 redirect 시켜줍니다.
5. Chapter Controller - 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @MemberRole @RequestMapping(value = "/edit/{storyId}/{chapterId}", method = RequestMethod.GET) public String view(@PathVariable("storyId") int storyId, @PathVariable("chapterId") int chapterId, Model model, HttpSession session, RedirectAttributes attrs){ Member loginUser = Logics.memberFromSession(session); Story story = storyService.get(storyId, loginUser); if(null != story){ Chapter chapter = chapterService.get(story, chapterId); model.addAttribute("story", story); model.addAttribute("chapter", chapter); return "/story/chapter/view"; } else { ViewMessage.error().message("유효하지 않은 접근입니다.").register(attrs); return "redirect:/"; } } | cs |
이제 등록된 Chapter를 수정할 수 있는 Controller 메소드를 만들어줍니다. 역시 Get메소드로 맵핑합니다. create와 차이점이 있다면
model.addAttribute()입니다.
model은 정확하게 설명할 수 없지만, client에 던져질 response 중 하나라고 보시면 됩니다. 이 model에 담을 story와 chapter 객체를 addAttribute 메소드를 통해 담을 수 있습니다.
6. Chapter Controller - 4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @MemberRole @RequestMapping(value = "/update/{storyId}/{chapterId}", method = RequestMethod.POST) public String updateChapter(@PathVariable("storyId") int storyId, @PathVariable("chapterId") int chapterId, Chapter chapter, Model model, HttpSession session, RedirectAttributes attrs){ Member loginUser = Logics.memberFromSession(session); Story story = storyService.get(storyId, loginUser); model.addAttribute("story", story); if(story != null){ Chapter origin = chapterService.get(story, chapterId); chapter.setStory(story); chapter.setId(origin.getId()); chapter.setViews(origin.getViews()); chapter.setState(origin.getState()); chapterService.update(chapter); model.addAttribute("chapter", chapter); ViewMessage.success().message("저장되었습니다.").register(attrs); return "redirect:/story/chapter/edit/"+story.getId() +"/"+chapter.getId(); } else { ViewMessage.error().message("서버 오류로 저장에 실패했습니다.").register(attrs); return "redirect:/story/create"; } } | cs |
다음은 수정페이지에서 수정내역을 저장할 메소드입니다. url을 이해를 쉽게 하기 위해 edit로 안하고 update로 해봤습니다. edit로 해도 됩니다.
Form 에 담긴 정보를 담을 Chapter 객체가 메소드의 인자로 들어가 있습니다.
이 정보를 origin Chapter객체에 덮어씌어주는 개념입니다.
그러므로 origin 객체를 불러온 후
적절하게 Form의 정보로 덮어준 후 update를 실행하면 됩니다.
7. Chapter Controller 전체
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | @Controller @RequestMapping("/story/chapter") public class ChapterContorller { @Autowired private StoryService storyService; @Autowired private ChapterService chapterService; @MemberRole @RequestMapping(value = "/create/{storyId}", method = RequestMethod.GET) public String create(@PathVariable("storyId") int storyId, Model model , HttpSession session, RedirectAttributes attrs){ Member loginUser = Logics.memberFromSession(session); Story story = storyService.get(storyId, loginUser); model.addAttribute("story", story); if(story != null){ return create(model); } else { ViewMessage.error().message("서버 오류로 저장에 실패했습니다.").register(attrs); return "redirect:/story/create"; } } //chapter 등록 @MemberRole @RequestMapping(value = "/create/{storyId}", method = RequestMethod.POST) public String registChapter(@PathVariable("storyId") int storyId, Chapter chapter,Model model , HttpSession session, RedirectAttributes attrs){ Member loginUser = Logics.memberFromSession(session); Story story = storyService.get(storyId, loginUser); if(story != null){ chapter.setStory(story); chapter.setState(State.on); chapter.setViews(0); Integer id = chapterService.insert(chapter); return "redirect:/story/chapter/edit/"+story.getId() +"/"+id; } else { ViewMessage.error().message("서버 오류로 저장에 실패했습니다.").register(attrs); return "redirect:/story/create"; } } @MemberRole @RequestMapping(value = "/update/{storyId}/{chapterId}", method = RequestMethod.POST) public String updateChapter(@PathVariable("storyId") int storyId, @PathVariable("chapterId") int chapterId, Chapter chapter, Model model, HttpSession session, RedirectAttributes attrs){ Member loginUser = Logics.memberFromSession(session); Story story = storyService.get(storyId, loginUser); model.addAttribute("story", story); if(story != null){ Chapter origin = chapterService.get(story, chapterId); chapter.setStory(story); chapter.setId(origin.getId()); chapter.setViews(origin.getViews()); chapter.setState(origin.getState()); chapterService.update(chapter); model.addAttribute("chapter", chapter); ViewMessage.success().message("저장되었습니다.").register(attrs); return "redirect:/story/chapter/edit/"+story.getId() +"/"+chapter.getId(); } else { ViewMessage.error().message("서버 오류로 저장에 실패했습니다.").register(attrs); return "redirect:/story/create"; } } @MemberRole @RequestMapping(value = "/edit/{storyId}/{chapterId}", method = RequestMethod.GET) public String view(@PathVariable("storyId") int storyId, @PathVariable("chapterId") int chapterId, Model model, HttpSession session, RedirectAttributes attrs){ Member loginUser = Logics.memberFromSession(session); Story story = storyService.get(storyId, loginUser); if(null != story){ Chapter chapter = chapterService.get(story, chapterId); model.addAttribute("story", story); model.addAttribute("chapter", chapter); return "/story/chapter/view"; } else { ViewMessage.error().message("유효하지 않은 접근입니다.").register(attrs); return "redirect:/"; } } public String create(Model model){ return "/story/chapter/edit"; } } | cs |
'개인프로젝트 - 웹소설 사이트' 카테고리의 다른 글
혼자서 웹 서비스 만들어보기 - 7 (0) | 2017.06.30 |
---|---|
혼자서 웹 서비스 만들어보기 - 5 (0) | 2017.06.26 |
혼자서 웹 서비스 만들어보기 - 4 (0) | 2017.06.23 |
혼자서 웹 서비스 만들어보기 - 3 (0) | 2017.06.15 |
혼자서 웹 서비스 만들어보기 - 2 (0) | 2017.06.13 |