게시판 만들기 - 실습

Feb 02, 2024
게시판 만들기 - 실습

1. 로그인을 안하면 안 보여주기

  • owner 세션이 있으면 로그인이 된 것
게시물 아이디랑 내 세션 정보의 아이디가 동일해야 함
notion image
 
  • 세션에 접근하는 방법 session.getAttribute();
 
  • 로그인 안되도 들어갈 수 있는 것도 잡아야 함
권한 체크하기
package shop.mtcoding.blog.board; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import shop.mtcoding.blog._core.PagingUtil; import shop.mtcoding.blog.user.User; import java.util.List; @RequiredArgsConstructor @Controller public class BoardController { private final HttpSession session; private final BoardRepository boardRepository; // http://localhost:8080?page=0 @GetMapping({"/", "/board"}) public String index(HttpServletRequest request, @RequestParam(defaultValue = "0") int page) { //System.out.println("페이지: "+page); List<Board> boardList = boardRepository.findAll(page); request.setAttribute("boardList", boardList); // 가방에 담음 int currentPage = page; int nextPage = currentPage + 1; int prevPage = currentPage - 1; request.setAttribute("nextPage", nextPage); request.setAttribute("prevPage", prevPage); // 이것만 담으면 disable 못함 // 현재 페이지가 퍼스트인지 라스트인지 만든다. boolean first = PagingUtil.isFirst(currentPage); boolean last = PagingUtil.isLast(currentPage, 4); request.setAttribute("first", first); request.setAttribute("last", last); return "index"; } @GetMapping("/board/saveForm") public String saveForm() { return "board/saveForm"; } // 상세보기시 호출 @GetMapping("/board/{id}") // 1이 프라이머리키 -> 뭐든 넣어도 실행시키려면 변수화시켜서 {} public String detail(@PathVariable int id, HttpServletRequest request) { // 파싱하게 치환해서 알려줌 BoardResponse.DetailDTO reponseDTO = boardRepository.findById(id); request.setAttribute("board", reponseDTO); // 키를 통해 값을 찾음 // 1. 해당 페이지의 주인 여부 boolean owner = false; // 목적 // 2. 작성자 userId 확인하기 int boardUserId = reponseDTO.getUserId(); // 3. 로그인 여부 체크 User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser != null) { // 로그인 했고 if (boardUserId == sessionUser.getId()) { owner = true; } } request.setAttribute("owner", owner); return "board/detail"; } }
notion image
 

2. 보드 상세보기할 때

  • 보드만 조회 하는게 아니라 유저를 같이 조회해야 되서 데이터를 못 담았음
항아리가 아이디는 담을 수 있으나 유저를 못 담아서 새로운 항아리 DTO 만들기
 

3. ORM(Object-Relational Mapping)

  • 객체와 관계형 데이터베이스 간의 매핑을 자동화하는 기술이나 프레임워크
  • 원래 설계 순서 : 테이블 설계 > 자바코드 작성
  • DB랑 JAVA의 데이터 타입이 다름
테이블 : 모든 타입을 스칼라만 담을 수 있음
스칼라는 하나의 데이터
→ 테이블이 여러 데이터를 담고 싶으면 테이블을 만들어야 함
자바 : 하나의 클래스에 여러가지의 타입을 담을 수 있음
⇒ OOP 세상과 테이블 세상은 데이터 타입의 구조조차 다름 불일치
  • 불일치 해결
DB의 테이블 + 자바의 데이터 → 모델링해서 테이블이랑 타입을 똑같이 만들었음
package shop.mtcoding.blog.board; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.CreationTimestamp; import shop.mtcoding.blog.user.User; import java.time.LocalDateTime; @Data @Entity @Table(name = "board_tb") public class Board { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; private String title; private String content; @ManyToOne // 매니는 보드 원은 유저 : 1대 N -> 오류 사라짐 private User user; // 컬럼에 테이블로 만들어지지는 않음 @CreationTimestamp private LocalDateTime createdAt; }
  • 나는 객체를 적었는데 테이블을 만들 때는 외래키로 만들어줌
버전 1과 방식은 다르지만 똑같은 테이블이 생성됨
 
  • JPA: Hibernate와 orm이 담겨있음
Hibernate: 데이터베이스 타입의 데이터를 자바 Object로 변환해 줌=파싱해 줌
ORM : 자바는 자바의 타입으로 DB의는 데이터베이스 타입으로 만들어 줌
→ 불일치를 해결해주는 것
 
@ManyToOne // 매니는 보드 원은 유저 : 1대 N -> 오류 사라짐 private User user; // 컬럼에 테이블로 만ㄴ들어지지는 않음
  • 나중에 조회할 때 보드를 남으면 USER가 남김
  • 조인해서 담을 항아리가 있기 때문에 DTO를 만들 필요가 없음
 

4. Select

Query String = JPQL(Java Persistence Query Language)
: 쿼리를 객체지향 쿼리로 작성할 수 있게 해주는 문법
→ 쿼리를 작성하면 실제 쿼리는 아니나 실제 쿼리로 발동됨 / 객체가 뜸
select b from Board b select * from board_tb
  • 보드가 유저를 들고 있기에 DTO 필요 없음
notion image
 
@DataJpaTest // 스프링의 메인이 실행되지 않고 EntityManager랑 DB에 관련된 애들을 메모리에 다 띄워줌
package shop.mtcoding.blog.Board; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; import shop.mtcoding.blog.board.BoardRepository; @Import(BoardRepository.class) // 내가 만든 파일 임포트 @DataJpaTest // 엔티티 메니저랑 데이터베이스에 관련된 애들을 메모리에 다 띄워줌 public class BoardRepositoryTest { @Autowired private BoardRepository boardRepository; @Test public void findById(){ } }
 
@ResponseBody // 데이터를 리턴 // HTTP 응답의 본문(body)을 직접 생성하여 반환 // 소드의 반환 값을 HTTP 응답으로 직접 전송
 
  • Board가 user 객체를 들고 있음
notion image
 
  • 서로 매칭 됨 : 서로 일치해야 함
notion image
 
  • 객체 리턴 시 스프링이 JSON으로 변환
package shop.mtcoding.blog.board; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @RequiredArgsConstructor @Repository public class BoardRepository { private final EntityManager em; public Board findById(int id) { Query query = em.createQuery("select b from Board b join fetch b.user u where b.id = :id", Board.class); query.setParameter("id", id); Board board = (Board) query.getSingleResult(); return board; } }
insert into user_tb(username, password, email, created_at) values('ssar', '1234', 'ssar@nate.com', now()); insert into user_tb(username, password, email, created_at) values('cos', '1234', 'cos@nate.com', now()); insert into board_tb(title, content, user_id, created_at) values('제목1', '내용1', 1, now()); insert into board_tb(title, content, user_id, created_at) values('제목2', '내용2', 1, now()); insert into board_tb(title, content, user_id, created_at) values('제목3', '내용3', 1, now()); insert into board_tb(title, content, user_id, created_at) values('제목4', '내용4', 2, now());
package shop.mtcoding.blog.board; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; @RequiredArgsConstructor @Controller public class BoardController { private final HttpSession session; private final BoardRepository boardRepository; // 데이터 리턴 시 (RestController or ResponseBody) - 객체를 리턴하면 스프링이 JSON으로 변환 @GetMapping("/api/board/{id}") public @ResponseBody Board apiBoard(@PathVariable int id) { // @ResponseBody : 데이터를 리턴 return boardRepository.findById(id); } // http://localhost:8080?page=0 @GetMapping({"/", "/board"}) public String index() { return "index"; } @GetMapping("/board/saveForm") public String saveForm() { return "board/saveForm"; } // 상세보기시 호출 @GetMapping("/board/{id}") // 1이 프라이머리키 -> 뭐든 넣어도 실행시키려면 변수화시켜서 {} public String detail(@PathVariable int id, HttpServletRequest request) { // 파싱하게 치환해서 알려줌 return "board/detail"; } }
notion image
 

오늘의 정리

  • 레파지토리를 만들어서 함수를 만들어서 JPQL를 만들었음
예전처럼 네이티브 쿼리를 저적어도 됨
핵심은 ORM 보드로 받을 수 있음
제일 중요한건 테이블을 만들어낼때 원래는 INT USERID라고 적어야되는데 객체로 적었음
디비에서는 USER_ID로 만들어줌
USER라고 적으면 PK에 ?를 찾아서 만들어줌
  • 내가 이름을 만들고 싶을때
@JOINCUZJAFOTJ → NAME__ID하면 됨
객체로 적어서 테이블의 컬럼(INT)을 만들어냄
이렇게 만든 목적이 바로 ORM임
내가 조인해서 들고오면 JPA가 객체에USER에 담아줌
못 담아주면 DTO를 만들어서 담아야함
  • DTA DTO 만들때 비교를 하면
?같은 라인에 있으니까?
jpql 안 쓰고 내가 Object로 하나씩 담으면 담을 수 있음 → 그냥 orm이 해주는 것
 
플랫하고 일자로 해주는 것(Flat and Straight)
: 주로 객체와 DB간에 일관성 있고 간단한 매핑
굴곡있게 나와오는 것(Complex and Nesting)
: 객체의 계층 구조가 복잡하거나 중첩된 경우
→ 보기도 좋고 일할 때 좋음 데이터 찾기도 좋음
 
Share article
RSSPowered by inblog