즐겨찾기 관리

keep_going·2023년 4월 1일
0

문제해결

목록 보기
8/36
  1. 회원 모델에서 즐겨찾기 항목을 만들고
  2. 특정 빵집의 즐겨찾기를 누르면 그 빵집 id를 저장하고
  3. 빵집 상세 페이지에 접속했을때 회원 정보를 조회해서 즐겨찾기가 되어있으면 ★ 로그아웃 상태이거나 즐겨찾기 안되어 있으면 ☆

로그인 했나 / 로그인 안했나
로그인 했으면 즐겨찾기 추가 했나 / 즐겨찾기 추가 안했나
나눠서 로그인 안했을 경우 클릭하면 로그인 화면으로 이동하도록하고
로그인 했는데 즐겨찾기 추가 안했을 경우 즐겨찾기 추가 되도록
로그인 했는데 즐겨찾기 추가 했을경우 즐겨찾기에서 빠지도록

  • 문제
    이렇게 구현하려면... userModel에서 즐겨찾기 누를때 객체로 빵집 id들을 저장하면 즐겨찾기에서 뺄때 그 빵집만 제외하기가 가능한가? 객체에서 특정 값을 찾아서 지우기... 가능하더라도 복잡할것 같고 즐겨찾기 추가했다 뺐다 할때마다 값을 넣었다 지웠다 하는게 비효율 적으로 느껴짐. boolean으로 주는게 좋을듯.

  • 아이디어

  1. bookmark를 새로운 모델로 만드는것이 낫것다...
  2. 여기에 userId와 bakeryId를 저장하도록 하고 isBookmarked의 default값을 false로 주고
  3. 특정 user가 즐겨찾기를 추가 했을때 userId와 bakeryId를 저장하고 isBookmark값을 true로 가지도록 한다.
  4. 특정 user가 특정 bakery 페이지에 접속했을때 bookmark 여부를 체크하려면 userId와 bakeryId 여부를 동시에 확인해야한다. -> 베이커리 페이지에 user와 bakery정보가 모두 있으니 async/await로 어찌저찌 잘 해보면 get으로 찾아올수 있을것
  5. 즐겨찾기 삭제를 누르면 isBookmarked 값을 false로 주고 여기까지는 문제 없는데
  6. 이걸 실시간으로 읽어와서 별 모양을 차있는별/빈별 로 바꿔줘야하는데.. watch를 쓰면 가능할까? 일단 해보자
  • 새로운 문제발생
    최초에 즐겨찾기 버튼 누를시에 post로 userId와 bakeryId를 DB에 저장하고 이때 DB에 isBookmarked값을 true로 바꿔준다.
    이후에 즐겨찾기 버튼을 다시 누를시 query로 userId와 bakeryId값을 줘서 DB를 찾은뒤 put으로 isBookmarked값을 false로 바꿔준다.
    이후에 즐겨찾기 버튼을 다시 누를시 같은 방식으로 isBookmarked값을 true로 바꿔주고 싶은데... 문제는 뭐냐!
    즐겨찾기 버튼 클릭 이벤트를 3가지로 나눴는데
    로그인 안했을때->로그인 화면으로 이동
    로그인 안했고 즐겨찾기 안했을때->post로 db에 등록
    로그인 안했고 즐겨찾기 했을때->put으로 isBookmarked값 false로 변경
    이러니 즐겨찾기 버튼을 계속 누르면 db에 값이 계속 저장됨...

  • 아이디어

  1. 만약에 모델에서 기본키 값을 없애면 같은값이 계속 들어가면 중복으로 저장되지 않고 같은값으로 인식할까? -> no 기본키는 필수니까 기본키임. 없앨 수 없고.. userId와 bakeryId를 엮어서 기본키로 만들수는 있지만 어쨌든 기본키를 중복으로 넣어줄수없다.
  2. 최후의 방법으로는 즐겨찾기 삭제시 그냥 db에서 값을 삭제해도됨. 이경우에는 나야 소규모니까 상관없지만 즐겨찾기 넣고 뺴고 할떄마다 디비에 넣고 뺴고하는데 서버에 무리갈듯.
  3. 로그인 했을때는 이벤트를 하나로 통일한다. v-if로 보여지는 것만 빈별/찬별로 나누고 클릭 이벤트는 userId와 bakeryId를 쿼리로 주고 isBookmarked 값을 get하는 함수를 만들어서 onMounted 시킬것. 값을 못찾으면 post하고 값을 찾으면 isBookmarked값을 변화시켜줌. 새로고침시 즐겨찾기 여부가 풀리는 문제도 해결됨

3번 방식으로 시도해보자

  • 해결?
<el-button class="button" text v-if="!state.isLogin || !state.isBookmarked" @click="handleBookmarkClick()">
  로그인 안했거나 즐찾안함☆
</el-button>
<el-button class="button" text v-if="state.isLogin && state.isBookmarked" @click="handleBookmarkClick()">
  로그인 했고 즐찾했음★
</el-button>

이렇게 빈별, 찬별로만 구분하기 위해 버튼을 통합했고

//express
// 즐겨찾기 조회
router.get('/select', async function(req, res, next) {
    try{
        const query = { bakeryId : Number(req.query.bakeryId), email : req.query.email };
        const result = await Bookmark.find(query)
        console.log("즐겨찾기 조회", result.length);
        if(result.length) { // result값이 존재
            return res.send({ status : 200, result });        
        }
        return res.send({ status : 0, result })
    }

    catch(e){
        console.error(e); 
        return res.send({ status : -1, result : e });
    }
});

// 즐겨찾기 최초 추가
router.post('/insert', async(req, res, next) => {
    const { bakeryId, bakeryName, email } = req.body;
    try{
        const bookmark = Bookmark({
            bakeryId,
            bakeryName,
            email, 
            isBookmarked: true,  
        });    
        const result = await bookmark.save();

        if(result !== null) {
            return res.send({ status : 200, isBookmarked : true });
        }
        return res.send({ status : 0 });
    } catch(e) {
        console.error(e);
        return res.send({ status : -1, result : e });
    }
});

// 즐겨찾기 여부 변경 -> isBookmarked 값을 변경시킴
router.put('/update', async function(req, res, next) {
    try {
        const query = { bakeryId : Number(req.query.bakeryId), email : req.query.email };
        const obj   = await Bookmark.findOne(query);
		// front에서 true, false 여부를 받음
        obj.isBookmarked = req.body.isBookmarked;

        const result = await obj.save();
        console.log("즐찾여부 변경", result);
        if(result !== null) {
            return res.send({status : 200});
        }
        return res.send({status : 0});
    }
    catch(e) {
        console.error(e);
        return res.send({status : -1, result : e });
    }
});

이렇게 back을 짰고

// vue
// 즐겨찾기 조회 -> onMounted에서 if 걸어서 로그인 되어 있을 경우 실행
const handleBookmarkCheck = async() => {
  const url = `/api/bookmark/select?bakeryId=${state.bakery}&email=${state.email}`;
  const headers = { 'Content-Type': 'application/json' };
  const { data } = await axios.get(url, { headers });

  console.log('즐겨찾기 조회', data);

  // 데이터 값이 있으면 checkBookmark 값을 true를 주고 isBookmarked값을 받아온다
  if (data.status === 200) {
    state.isBookmarked = data.result[0].isBookmarked;
    state.checkBookmark = true;
  } else {
    state.checkBookmark = false;
  }
};

// 즐겨찾기 최초 추가
const handleBookmarkInsert = async () => {
  const url = `api/bookmark/insert`;
  const headers = { 'Content-type': 'application/json' };
  const body = {
    bakeryId: state.bakery,
    bakeryName: state.row.name,
    email: state.email
  };

  const { data } = await axios.post(url, body, { headers });
  console.log('즐겨찾기 추가', data);

  if (data.status === 200) {
    state.isBookmarked = data.isBookmarked;
    // 최초로 즐겨찾기 값을 넣어준 이후에는 항상 checkBookmark 값은 true이므로 수동으로 넣어줌
    state.checkBookmark = true;
  }
};

// 즐겨찾기 변경
const handleBookmarkUpdate = async () => {
  const url = `/api/bookmark/update?bakeryId=${state.bakery}&email=${state.email}`;
  const headers = { 'Content-Type': 'application/json' };
  // 클릭할때마다 현재 isBookmarked값과 반대되는 값을 넣어준다
  const body = { isBookmarked: !state.isBookmarked }
	
  const { data } = await axios.put(url, body, { headers });
  console.log('즐겨찾기 변동', data);

  if (data.status === 200) {
    // 즐겨찾기 값 수정 후 변경된 값 읽어오기
    handleBookmarkCheck();
  }
};

// 즐겨찾기 버튼 클릭
const handleBookmarkClick = () => {
  // 로그인 안됐으면 로그인
  if (!state.isLogin) {
    alert('로그인이 필요합니다.');
    router.push({ path: '/login' });
  } else {
    // 로그인 됐으면 
    if (!state.checkBookmark) {
      // checkBookmark값 false면 insert함수 호출
      handleBookmarkInsert();
    } else {
      // checkBookmark값 true면 update 함수 호출
      handleBookmarkUpdate();
    }
  }
};

그런데 이렇게 짜고 보니까 드는생각은 결국 클릭시 마다 db 조회해서 isBookemarked 값 변경해주고 변경된 값 다시 받아오고 할꺼면.. 아예 db값을 삭제하고 다시 입력하고 하는거랑 과부하 되는 정도는 똑같지 않았을까...?

gpt의 의견으로는...
첫 번째 방법에서는 즐겨찾기를 추가할 때마다 새로운 데이터를 입력하고, 제외할 때마다 해당 데이터를 삭제합니다. 그러나 이 방법은 데이터베이스에 대한 I/O 작업이 많아질 수 있으며, 즐겨찾기에 대한 추가 및 삭제 작업이 빈번하게 발생하는 경우에는 데이터베이스 성능에 영향을 줄 수 있습니다.

두 번째 방법에서는 즐겨찾기를 추가할 때마다 새로운 데이터를 입력하지 않고, 해당 데이터의 isBookmarked 값을 변경합니다. 이 방법은 데이터베이스에 대한 I/O 작업을 줄일 수 있습니다. 그러나 즐겨찾기에 대한 추가 및 삭제 작업이 자주 발생하는 경우, 데이터베이스에 대한 업데이트 작업이 빈번하게 발생하므로 이러한 작업이 데이터베이스 성능에 영향을 줄 수 있습니다.

따라서, 애플리케이션의 사용자 수, 즐겨찾기 작업의 빈도 및 데이터베이스 크기 등에 따라 어떤 방법이 더 효율적인지 결정됩니다. 보다 효율적인 방법을 선택하려면, 애플리케이션의 요구 사항을 분석하고 테스트를 수행하여 데이터베이스 성능에 대한 영향을 평가해야합니다.

결국 장단점이 있다는 얘기. 데이터 입력하고 삭제하고 반복하는건 여러번 해봤으니.. 안해본걸 해봤다는데 의의를 두자.


위에 작성한 즐겨찾기 데이터 조회는 회원별 즐겨찾기 데이터였고
상점별 즐겨찾기 데이터를 가져오고 싶은데
이 경우에 express에 이미 작성한 get문을 활용할 수 있을까?
여기서 쿼리를 bakeryId와 email 두개를 받도록 했는데
특정 bakeryId값만 일치하면 email값과는 상관없이 데이터를 전부 받아오도록 하고 싶다.

  • 문제
    쿼리를 2개 읽도록 짜놓은 코드에 쿼리를 1개만 읽고 나머지 1개는 전체 값을 읽도록 할 수 있을까?
  • 해결
const query = { bakeryId : Number(req.query.bakeryId) };
	if (req.query.email) { // 특정 회원의 즐겨찾기 조회
  		query.email = req.query.email;	
	}

front에서 email값을 쿼리로 줬을때만 back의 query 변수에 넣어주도록 함!

  • 문제
    빵집별로 즐겨찾기 수를 카운팅 해서 빵집 상세 페이지에 띄우려고 하니.. 회원별 즐겨찾기 수 조회에서는 최초에 한번 저장 후 isBookmarked 값을 변환시키는 것이기 때문에 result의 값이 0아니면 1이었기 때문에 길이가 있냐 없냐로만 판단했는데...
    빵집의 즐겨찾기 수를 counting 하려면 result 배열 내에 존재하는 객체들 중 isBookmarked 값이 true인 객체의 수가 필요하다..! 배열에 객체 값에 접근해서 거기에 해당하는 객체의 갯수를 받아올 수 있나?

  • 해결
    Array.prototype.filter() 메서드 사용

router.get('/select', async function(req, res, next) {
    try{
        const query = { bakeryId : Number(req.query.bakeryId) };
        if (req.query.email) { // 특정 회원의 즐겨찾기 조회
            query.email = req.query.email;
        }
        const result = await Bookmark.find(query)
        console.log("최초 즐겨찾기 했었나", result.length);

        const bakeryResult = result.filter(obj => obj.isBookmarked === true);
        const bakeryCount = bakeryResult.length;
        console.log("빵집별 즐겨찾기 수", bakeryCount)

        if(result.length) { // 빵집 입장에서도 어떤 회원도 즐겨찾기 안했다면 값을 받아올 필요가 없음
            return res.send({ status : 200, bakeryCount, result });        
        }
        return res.send({ status : 0, result })
    }

    catch(e){
        console.error(e); 
        return res.send({ status : -1, result : e });
    }

});

filter를 통해서 result 객체중에서 isBookmarked의 값이 true인 객체를 obj에 담고 이걸 bakeryResult 변수에 저장
여기서 필요한건 객체들의 정보가 아닌 객체의 수이므로 length값만 bakeryCount에 넣어서 반환해줌

근데 이경우 빵집별 즐겨찾기 조회, 회원별 즐겨찾기 조회를 동시에 수행하도록 했기 때문에 빵집별 조회에서는 필요없는 result값이 반환되고 회원별 조회에서는 필요없는 bakeryCount값이 반환되는 문제가 있다.

if를 걸어서 쿼리에 email이 있을때는 result값만 반환하고 아닐때는 bakeryCount값만 반환하도록 하는것도 생각해봤는데 이경우 이중 if를 돌아야 하므로 (result.lenth값이 일단 있을때) 처리속도가 좀더 걸릴꺼라 생각해서 반환값이 좀 지저분해보여도 이게 더 나은 방법 아닌가 싶어서 그대로둠

profile
keep going

0개의 댓글