🎨다양한 페이지들과 내 마이페이지 연동하기
로그인을 한 경우 생기는 마이페이지에서 나의 활동 로그들을 한 눈에 볼 수 있도록
하기 위한 테이블 조인 작업!
처음 해보는 테이블 조인으로 3일 전부터 무섭고 계속 퍼뜩 생각이 나가지고
빨리 해치우고 싶었으나, 제대로 해치워야지
정리하면서 하면 하나하나 기억에 오래 저장할 수 있으니 한 번 기록해보겠어.
1. DB에 있는 공공데이터들 불러오기
2. 내가 저장한 장소 저장 목록에 불러오기
3. 내가 쓴 게시글 목록 불러오기 <오늘 할 작업>
아래는 내가 그린 마이페이지 프로토타입
내 여행기 부분에 내가 쓴 게시글을 불러오기 위해서 조인 작업을 할 예정이다.
어제 공공API를 통해서 추천 핫플레이스를 DB에 저장해서 랜덤으로 불러오는 것과
핫플페이지 내 저장을 누르면 로그인한 사람의 저장목록을 불러올 수 있도록 만들었다.
조건 1. 로그인된 사용자의 m_idx와 M_Mypage테이블의 m_idx와 같아야 함
조건 2. M_Mypage 테이블의 m_idx 와 post 테이블의 m_idx를 매칭시킴.
조건 3. 위 두 조건이 충족된 상태에서, post 테이블의 post_writer, post_content , post_date , like_count, comment_count 컬럼의 데이터를 출력해야 함!
로그인 한 m_idx가 102번인 사람이 글을 쓰면 포스트 테이블에 값이 들어온다.
SELECT
p.post_writer AS postWriter, -- VO 필드와 매핑
p.post_content AS postContent, -- VO 필드와 매핑
p.post_date AS postDate, -- VO 필드와 매핑
p.like_count AS likeCount, -- VO 필드와 매핑
p.comment_count AS commentCount -- VO 필드와 매핑
FROM m_mypage m
JOIN post p
ON m.m_idx = p.m_idx -- m_mypage와 post 테이블의 m_idx로 조인
WHERE m.m_idx = #{m_idx}; -- 로그인된 사용자의 m_idx로 필터링
조인한 뷰 테이블
불러와진 게시글!!!!!!!!!!!!
🪁MypageVO와 PostVO
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MypageVO {
private String addr1; // 주소
private String firstimage; // 첫 번째 이미지 URL
private String title; // 장소 제목
private int likes; // 좋아요 수
// 조인된 post 테이블의 컬럼 추가
private String postWriter; // 게시글 작성자
private String postContent; // 게시글 내용
private LocalDateTime postDate; // 게시글 작성 날짜
private int likeCount; // 게시글 좋아요 수
private int commentCount; // 게시글 댓글 수
// 추가된 필드
private int m_idx;
}
public class PostVO {
private int m_idx; //예슬 추가
private int id; // 게시글 ID
private String writer; // 작성자
private String content; // 내용
private LocalDateTime postDate; // 작성 날짜
private int commentCount;
private int likeCount;
private boolean isLiked;
조인된 테이블의 컬럼을 VO에 추가해줬다.
여기 보면 VO들의 이름이 다 다르다.
테이블 생성할 때도 이름이 다 다르다.
여기서 2시간을 헤매고
컨트롤러 중복으로 인한 페이지 오류로 1시간을 헤맸다.
초보자의 설움인가?
정리를 해보겠다.
잘 안됐던 이유는 "별칭 문제"
MyBatis와 같은 ORM 도구에서는 SQL 쿼리에서 반환되는 컬럼 이름과 VO 클래스 필드 이름이 정확히 일치해야 데이터를 올바르게 매핑할 수 있습니다.
이 때문에 별칭을 사용하지 않으면, 테이블의 컬럼 이름과 VO 클래스의 필드 이름이 일치하지 않아 null 값이 반환되는 상황이 발생할 수 있습니다. - GPT선생님
null값이 이렇게 무서운 적은 처음입니다만..
아무튼
post 테이블의 컬럼 이름: post_writer, post_content, post_date, like_count, comment_count
PostVO 필드 이름: writer, content, postDate, likeCount, commentCount
MypageVO postWriter, postContent, postDate, likeCount, commentCount;
이처럼 테이블 컬럼과 VO 클래스 필드 이름이 일치하지 않으면, MyBatis는 데이터를 매핑할 수 없다.
SELECT
post_writer AS postWriter, -- 컬럼 이름을 VO 필드에 맞춰 별칭 설정
post_content AS postContent, -- 컬럼 이름을 VO 필드에 맞춰 별칭 설정
post_date AS postDate, -- 명확한 매칭을 위해 별칭 사용
like_count AS likeCount, -- 일치시키기 위해 별칭 사용
comment_count AS commentCount-- 일치시키기 위해 별칭 사용
FROM post;
그러니까.. post 테이블 컬럼명을 나의 mypage 테이블에 불러와야하니끼ㅏ!
mypage테이블 컬럼명과 일치시키려고 별칭을 mypagevo로 맞춘거다!!
CREATE TABLE post (
m_idx INT NOT NULL, -- 회원 식별자 (m_idx)
post_id INT AUTO_INCREMENT PRIMARY KEY, -- 게시글 ID (자동 증가)
post_writer VARCHAR(50) NOT NULL, -- 작성자
post_content TEXT NOT NULL, -- 게시글 내용
post_date DATETIME DEFAULT NOW(), -- 게시글 작성 날짜
like_count INT DEFAULT 0, -- 좋아요 수
comment_count INT DEFAULT 0 -- 댓글 수
);
CREATE TABLE m_mypage (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, -- 기본 키로 auto_increment 설정
m_idx INT NOT NULL, -- 회원 식별자 (m_idx)
contentid VARCHAR(100) -- contentid 컬럼, VARCHAR 타입으로 설정
);
🪁MypageController
//게시글 불러오기
@RequestMapping("/m_myJourneys") // "/m_myJourneys"라는 URL 경로로 클라이언트가 요청을 보낼 때 이 메서드가 실행됨
public String getSavedPostList(HttpSession session, Model model) {
// 세션에서 M_MemberVO 객체 가져오기
// 로그인 시 세션에 저장된 회원 정보를 가져옴. "member"는 로그인 시 세션에 저장된 M_MemberVO 객체의 키 값임
M_MemberVO member = (M_MemberVO) session.getAttribute("member"); // 로그인할 때 저장된 vo를 session에서 가져오기
// 만약 세션에 "member" 객체가 없다면, 즉 사용자가 로그인하지 않았을 경우
if (member == null) {
System.out.println("세션에서 member를 찾을 수 없습니다.");
// 로그인 페이지로 리다이렉트 함. 이는 로그인되지 않은 사용자가 접근할 수 없도록 하기 위함
return "redirect:/Member/login"; // 로그인 정보가 없으면 로그인 페이지로 리다이렉트
}
// 세션에서 가져온 M_MemberVO 객체에서 회원의 고유 식별자 m_idx를 가져옴
// m_idx는 데이터베이스에서 회원을 식별하는 중요한 키 값임
int m_idx = member.getM_idx(); // 로그인된 사용자의 m_idx 값을 가져옴
System.out.println("로그인된 사용자 m_idx: " + m_idx); // 콘솔에 m_idx 값을 출력하여 디버깅에 사용
// mypageService를 통해 저장된 목록과 post 테이블과 조인된 데이터를 가져옴
// mypageService.getSavedPostList(m_idx)는 m_idx에 해당하는 사용자의 저장된 목록과 게시글을 불러오는 로직임
List<MypageVO> savedPostList = mypageService.getSavedPostList(m_idx); // m_idx로 조인된 데이터를 가져오는 메서드 호출
// model 객체에 "savedList"라는 이름으로 가져온 데이터를 추가함
// 이 데이터는 View(JSP)에서 사용되어 화면에 표시됨
model.addAttribute("savedPostList", savedPostList); // 모델에 저장된 목록 데이터를 추가
// MyPage 폴더 내에 있는 m_myJourneys.jsp 파일을 반환하여, 클라이언트에게 해당 페이지를 보여줌
// 이 페이지에서 model에 저장된 savedList 데이터를 활용해 UI에 표시할 수 있음
return "MyPage/m_myJourneys"; // MyPage 폴더 내의 m_myJourneys.jsp로 이동
}
여기서 생긴 문제는
해당 컨트롤러 위에
/*
// 내 여행기 페이지로 이동
@GetMapping("/m_myJourneys") public String myJourneys() { return
"MyPage/m_myJourneys"; }
*/
이렇게 getMapping을 해놨기 때문에 되지 않았다. 중복이 되면 잘 돌아가게 만든 백 로직도
안돌아가더라,,MVC에서 가장 답답한건 404오류의 범위가 너무 넓고 포괄적으로 오류를 나타내줘서
어디가 문제인지 알아가는 과정이 초보자 입장에선 까마득하닷.. ㅎ..ㅎ.ㅎ..
너 덕분에 살빠졌다 스프링아!
🪁MypageService, MypageServiceImpl, MypageDAO, MypageMapper
// MypageService
List<MypageVO> getSavedPostList(int m_idx);
// MypageServiceImpl
//게시글 불러오기
@Override
public List<MypageVO> getSavedPostList(int m_idx) {
System.out.println("서비스에서 전달된 m_idx: " + m_idx); // m_idx 값 로그 확인
List<MypageVO> savedPostList = mypageDao.getSavedPostList(m_idx);
System.out.println("서비스에서 가져온 저장 데이터 목록: " + savedPostList); // 가져온 데이터 출력
return savedPostList;
}
}
//MypageDAO
//게시글 불러오기
public List<MypageVO> getSavedPostList(int m_idx) {
System.out.println("DAO에서 전달받은 m_idx: " + m_idx); // m_idx 값을 확인
List<MypageVO> savedPostList = sqlSession.selectList(MAPPER + ".getSavedPostList", m_idx);
System.out.println("DAO에서 가져온 데이터 출력하기: " + savedPostList); // 가져온 데이터 확인
return savedPostList;
}
//MypageMapper
<select id="getSavedPostList" resultType="com.human.web.vo.MypageVO">
SELECT
p.post_writer AS postWriter, -- VO 필드와 매핑
p.post_content AS postContent, -- VO 필드와 매핑
p.post_date AS postDate, -- VO 필드와 매핑
p.like_count AS likeCount, -- VO 필드와 매핑
p.comment_count AS commentCount -- VO 필드와 매핑
FROM
m_mypage m
JOIN
post p ON m.m_idx = p.m_idx -- m_mypage와 post 테이블의 m_idx로 조인
WHERE
m.m_idx = #{m_idx}; -- 로그인된 사용자의 m_idx로 필터링
</select>
<!-- MypageMapper.xml 파일에 정의된 쿼리 -->
<insert id="insertMypage" parameterType="com.human.web.vo.MypageVO">
INSERT INTO m_mypage (m_idx)
VALUES (#{m_idx});
</insert>
디버깅의 흔적이 보이시나요?
무지하게 답답했습니다.
차라리 SNS로그인 하는 게 나을 정도로 답답했습니다.
그런데 한편으로는 VO랑 컬럼명이 다른게 내내 신경이 쓰였는데
정확히 알지 못하고 마음이 조급하니까 알면서도 넘어가게 된 것 같다! (너 그러지마!🤔)
촉은 정확했다. 이것도 경험치가 쌓이면서 느껴지는 촉인가보닷 해헷
<!-- MypageMapper.xml 파일에 정의된 쿼리 -->
<insert id="insertMypage" parameterType="cohttp://m.human.web.vo.MypageVO">
INSERT INTO m_mypage (m_idx)
VALUES (#{m_idx});
</insert>
자 요코드는 Post(게시글 생성)파일들에서 온 친구입니다.
🍖PostController
PostController
// 예슬 추가: 게시글 작성 처리 (JSON 응답)
@PostMapping("/create")
public ResponseEntity<String> createPost(HttpSession session, @RequestBody PostVO post) {
// 세션에서 로그인한 사용자 정보 가져오기
M_MemberVO member = (M_MemberVO) session.getAttribute("member");
// 세션에 저장된 m_idx 값을 PostVO 객체에 설정
if (member != null) {
int m_idx = member.getM_idx(); // 세션에서 m_idx 가져오기
post.setM_idx(m_idx); // PostVO에 m_idx 설정
} else {
// 세션에 member 정보가 없으면 에러 처리
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("로그인 된 유저가 없음");
}
// 서비스 계층에 게시글 생성 요청
int result = postService.createPost(post);
// 성공 시 201 응답, 실패 시 500 응답 반환
if (result == 1) {
return ResponseEntity.status(HttpStatus.CREATED).body("SUCCESS"); // 성공 시 201 응답
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("ERROR"); // 실패 시 500 응답
}
}
/create로 요청이 들어가면 세션에서 로그인한 정보를 가져오고 서비스 계층에 생성을 요청한다.
여기서 createPost로 컨트롤러를 시작하고, 서비스 계층의 메서드를 호출한다.
🥟PostServiceImpl
PostServiceImpl
//예슬 추가: 게시글 생성 및 m_mypage 테이블에 데이터 삽입
@Override
public int createPost(PostVO post) {
return postDAO.createPostAndMypage(post);
}
서비스 계층에서 createPost를 호출한 후,
바로 m_mypage 테이블에 데이터를 넣기 위해 createPostAndMypage를 호출 하는 것이다.
createPost는 게시글 삽입만 처리
반면, DAO의 createPostAndMypage는 두 개의 테이블을 모두 처리하는 복합적인 작업
🍪PostDAO
// 예슬: m_mypage 테이블에 데이터 삽입
public int createPostAndMypage(PostVO post) {
// 1. post 테이블에 게시글 추가
// SQL 쿼리를 실행하여 post 테이블에 새로운 게시글을 삽입하고, 성공 여부를 result에 저장
int result = sqlSession.insert(POST_NAMESPACE + ".createPost", post);
// 2. post 테이블에 게시글 삽입이 성공했을 경우
if (result > 0) {
// 3. 새로운 MypageVO 객체 생성
MypageVO mypage = new MypageVO();
// 4. MypageVO 객체에 현재 게시글을 작성한 사용자의 m_idx 값을 설정
mypage.setM_idx(post.getM_idx());
// 5. m_mypage 테이블에 새로 생성된 게시글의 정보를 삽입
// 즉, 사용자의 m_idx 값을 포함하여 m_mypage 테이블에 데이터를 추가
sqlSession.insert(MYPAGE_NAMESPACE + ".insertMypage", mypage);
}
// 6. post 테이블에 게시글이 성공적으로 생성되었는지 여부(result)를 반환
return result;
}
createPostAndMypage이 메서드는 게시글을 Post에
삽입하는 역할과 관련 정보를 m_mypage에 삽입하는 역할을 하기 때문에 DAO에서 호출한다.
🍱PostMapper
<!-- 게시글 생성 --> <!--예슬 추가: #{m_idx} -->
<insert id="createPost" parameterType="com.human.web.vo.PostVO">
INSERT INTO post (post_writer, post_content, m_idx)
VALUES (#{writer}, #{content}, #{m_idx});
</insert>
매일 헷갈리는 GetMapping, PostMapping 이젠 헷갈리지 말자!
오늘은 아침에 영준이 깃으로 합치다가 날릴 뻔해서 압축을 세 번이나 했다.
ㅋㅋㅋㅋ그러다 서버 오류가 터졌다.
거의 아침 2시간은 그래서 둥가둥가 날리고 점심시간부터 4시간만에 성공.
다음 일정 불러올 때는 일트에 가져오고 싶다!
나 그래도 정리하면서 한 번 정돈하니까 시간은 오래 걸려도 뿌듯해..
모든 오류에는 배움이 있다.
이제 모레 시험 보는 정청산기 실기 공부하러 총 슝 ☕
'Project > javachip' 카테고리의 다른 글
[2nd Project] 아이디, 비밀번호 찾기/재설정(이메일 인증, JSP, STS) (4) | 2024.10.22 |
---|---|
[2nd Project] 프로필 이미지 수정(JSP, STS) (8) | 2024.10.22 |
[2nd Project] 구글 API 구현하기 (7) | 2024.10.04 |
[2nd Project] 스프링, JSP, Javascript로 네이버 API 로그인 구현하기 (5) | 2024.10.04 |
[2nd Project] ⁴ Servlet, JSP, AJAX 이메일 인증 기능 구현 및 오류 해결 과정 (3) | 2024.09.25 |