Project/javachip

[2nd Project] SQL 조인해서 테이블 연동하기, 마이페이지에 불러오기!

ParkYeseul 2024. 10. 18. 18:13

🎨다양한 페이지들과 내 마이페이지 연동하기

 

로그인을 한 경우 생기는 마이페이지에서 나의 활동 로그들을 한 눈에 볼 수 있도록

하기 위한 테이블 조인 작업!

 

처음 해보는 테이블 조인으로 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시간만에 성공.

 

다음 일정 불러올 때는 일트에 가져오고 싶다!


나 그래도 정리하면서 한 번 정돈하니까 시간은 오래 걸려도 뿌듯해.. 
모든 오류에는 배움이 있다.

이제 모레 시험 보는 정청산기 실기 공부하러 총 슝 ☕