난감과 난관 : 11/17~9(금토일)

Yunhye Park·2023년 11월 19일
0

11/17(금)

한 것

  • 박스오피스 자동 슬라이드
  • 장르 DB 추가 어떻게 할지?
  • Search 결과 가져오기(우선 영화 타이틀)

typeof querySelectorAll

const boxOfficePosters = 
document.querySelectorAll('.box-office-poster img');

nodeList는 유사배열객체라서 배열 메서드X, 프로퍼티(length, index, forEach, item) 사용 가능

map은 결과를 하나의 배열로 반환하니까

let posterPath = []; // <--- 빈 배열 만들 필요가 없다

movieData.map(movie => {
  posterPath.push(movie.poster_path)});

결과값으로 새 배열을 반환하므로 그걸 바로 변수에 저장해도 된다.

const posterPath = movieData.map(movie => movie.poster_path;

데이터는 객체에 담아

axios get이든 Post든 프론트에서 result.data로 접근한 이후에 보낸 객체 타고 들어가야 함.

ex.

res.json({movie: movieInfo, searchInput: req.query.input});

이렇게 백에서 정보를 넘기면, 프론트는 그걸 가져다 써야 함. 이때 res.data에 결과물이 담긴다. 백에서 객체 형태로 보내줬으므로 각각 key로 접근해서 들어갈 수 있다.

axios({
// get요청은 params에 데이터 담기
}).then(res => {
	const movieData = res.data.movie; // movieInfo 객체
    const searchValue = res.data.searchInput; // 바로 값
})

프론트가 편하게 데이터 처리할 수 있도록 동일한 데이터 형태, 즉 객체에 담아 보내자.

검색 화면

하지만 좀 건들다보니 다시 망가져서 또 난리를.. 하나 건드렸다고 생각했는데 사실 2~3개 동시에 만지고나서 확인하는 바람에 무엇이 문제인지 삽질이 길어졌다고 한다.


11/18(토)

한 것

  • 장르 추가해서 디비 넣기
  • 태그별 검색 기능 (select)
  • 메인 API를 싹 갈아엎는 거... 시도.

sequelize와 mysql 테이블 컬럼명

create()의 내용과 mysql table 구조가 똑같아야 함. 에러 없이 잘 된다면 오탈자 주의!

ex.
mysql sequelize를 이용 중. 장르 번호(참조용도)와 장르명을 넣으려는데 장르명이 안 들어갔다.

genre : genres.join(', ')

genres라고 컬럼명을 멋대로 바꾸는 바람에 아무리 해도 안 되었던 것이다. 하하.

DB select

1. 'ㅇㅇㅇ'이 들어간 것(like, or, and)

sequelize 모듈의 Op 사용.

ex.

const { Op } = require('sequelize');

.findAll({
where: {
	[Op.like]: {title: '%눈%'}
	},
});

2. select를 랜덤하게

sequelize 메서드의 fn 사용.

ex.
외국 API를 갖다 쓰다보니 language를 바꿔도 워낙 다양한 언어가 담겨서 정체 불명의 title이 나오는지라 적절한 기준으로 커트하는 게 필요했다. 게다가 특정 키워드를 포함한 내용을 찾고자 하니, 다른 태그의 영화와 겹치는 경우가 생겨서 아예 랜덤하게 뽑아오는 게 낫겠다는 결론.

if(!rdDramaMovie) {
  const searchDrama = await Movie_info.findAll({
    order: Sequelize.fn('RAND'),
    where: {
      overview: {
        [Op.or]: [
          { [Op.like]: '%일상%' },
          { [Op.like]: '%눈부신%' },
          { [Op.like]: '%행복%' },
        ]
      }
    },
    attributes: ['title', 'poster_path'],
    limit: 5
  });

11/19(일)

한 것

  • 검색 초기화 - 유저 UX 고려하여 적용 확인
  • 이미지 없을 경우 로직 추가
  • main.ejs DB select (axios 안 쓰고)

if-else 로직

Promise로 result로 넘겨 받는 상황에서 true/false 로직을 만들고 싶다면, 단순히 값의 존재 유무가 아닌 특정할 수 있는 값(ex. length)을 기준으로 좀더 딥하게 가야 한다.

ex.
findAll 메서드 진행 후 then으로 받은 결과(result)라서 result가 있는 셈 되는 듯. 그래서 특정 값이 넘어왔음을 조건으로 명시해 주었다.

if (result && result.length > 0) { // <--- 여기
  movieInfo = result.map(movie => ({
    title: movie.title,
    poster: movie.poster_path,
    count: result.length
  }));
} else {
  movieInfo = [{ msg: '검색 결과가 없습니다.' }];
}

검색 초기화

검색은 사용자 UX를 많이 고려해야 하는 것 같다. 게시판 단순 검색만 있는 거라면 키워드를 잘 캐치해서 보여주는 것에 그치겠으나, 영화에 국한된 주제라면 영화 제목/장르/키워드 등 다양한 케이스를 고려해야 한다.

하면 할수록 더 할 수 있을 것 같다. 근데 애석하게도 팀원들과 화요일 수업 전까지 완성해 오기로 해서, 지금은 추가적인 시간을 쓸 수 없을 듯하다. 그래서 우선 영화 타이틀 검색만이라도 기깔나게 처리하도록 테스트하며 조건을 추가했다.

검색 결과엔 기본적으로 건수/타이틀/포스터가 뜬다. 이를 초기화하는 함수.

function onSearch() {
  searchInput.removeAttribute('placeholder');
  // 카운트 초기화
  searchCount.innerHTML = '';

  // 타이틀 초기화
  const resultTitleArr = Array.from(resultTitle);
  for (const title of resultTitleArr) {
    title.innerHTML = '';
  }

  // 포스터 초기화
  for (const poster of resultPoster) {
    poster.src = '';
  }}

우선 결과 검색을 보여주기 전에 초기화부터 한다. 그래야 이전 흔적 없이 지금 값과 관련된 부분만 보여준다.

function searchResult() {
  // 결과 초기화
  onSearch();

  // 결과창 show
  document.querySelector('.search-result__section').classList.remove('hidden');

  axios({
    method: 'get',
    url: '/search/result',
    params: {input: searchInput.value}
  }).then((res) => {
    const result = res.data;
    const failResult = document.querySelector('.search-result');

    for(let i=0; i<result.movie.length; i++) {
      if (result.movie.length > 0 && result.movie[i].count) {
        // 결과 초기화
        failResult.textContent = '';

        // 결과 개수
        const movie = result.movie[i];
        searchCount.innerHTML = 
          `<span style="color: var(--blue-point-color);">검색 결과</span>
${movie.count}<span style="color: var(--blue-point-color);">개</span>`;

        // 결과 title
        resultTitle[i].textContent = `${movie.title}`;

        // 결과 img(poster)
        const url = movie.poster;
        resultPoster[i].src = `${posterUrl}${url}`;

      } else {
        failResult.textContent = result.movie[i].msg;
      }
    }
  })
}

이것으로 아래 케이스 해결.

  • 검색 결과 없음 -> 검색 결과 있음
    : failResult 초기화 추가

  • 검색 결과 있음 -> 검색 결과 없음
    : if-else 로직이라서 언제나 둘 중 하나만 실행이라 영향 X

  • 검색 결과 없음 -> 태그 검색
    : 다른 function이라서 영향 X

  • 태그 간 검색
    : 개수 및 형태가 모두 동일하여 문제 없음

  • 태그 검색 -> 검색 결과 있음
    : 검색 결과, 태그 검색 시작할 때 onSearch()로 내용 초기화

그러다 이미지 없는 영화들이 너무 거슬렸다. 뭐라도 채워넣어야겠다는 마음에 정보 없다는 문구와 BGColor 변경하는 구문 추가.

하나 고치면 또 하나의 문제가 발생한다. 정말 신기.

  • 검색 결과 있음 -> 태그 검색
    : 이것이 문제였다.

검색 결과 개수 > 태그별 영화 개수

만약 유저가 검색한 결과로 7개의 영화 포스터가 떴다고 하자. 마지막 영화 포스터가 정보가 없어서 '정보 없음' 문구와 함께 BGColor가 회색으로 변경되었다. 그리고 유저가 태그별 영화 클릭 시, 7번째 자리에 여전히 회색으로 된 div가 존재한다.

태그별 영화에는 배경색까지 초기화(resetBgColor())해야 하는 것이다. 라고 적고 보니 검색 결과에도 해당되겠다. 검색 결과 개수 < 태그별 영화 개수라면 영화 포스터가 남아있을 테니까.
추가해야겠군..

// 태그별 영화
function tag2000() {
  handleTagSearch('/search/tag2000');
}

function tagComedy() {
  handleTagSearch('/search/tagcomedy');
}

function tagHorror() {
  handleTagSearch('/search/taghorror');
}

function tagDrama() {
  handleTagSearch('/search/tagdrama');
}

function tagThriller() {
  handleTagSearch('/search/tagthriller');
}

// 태그 공통 부분
function handleTagSearch(url) {
  // 결과 초기화
  onSearch();
  failResult.textContent = '';

  document.querySelector('.search-result__section').classList.remove('hidden');

  // 배경색 초기화
  resetBgColor();

  axios({
    method: 'get',
    url: url,
  }).then(result => {
    const res = result.data;

    if (res.data) {
      for (let i = 0; i < res.data.length; i++) {
        // 결과 title
        resultTitle[i].textContent = res.data[i].title;

        // 결과 img(poster)
        const url = res.data[i].poster_path
        const resultPoster = document.querySelectorAll('.search-result__poster img');
        resultPoster[i].src = `${posterUrl}${url}`;
      }
    }
  });
}

// 배경색 초기화 : 검색 결과 있음 -> 태그 클릭 영향 상쇄
function resetBgColor() {
  for (const poster of resultPosterBody) {
    poster.style.backgroundColor = '';
  }
}

하지만 동적 생성하면

초기화 패턴 하나하나 걸어둘 필요가 없다... 이때는 이걸 몰라 삽질을 길게 했구나 수고했어..

백엔드 데이터를 프론트로 받아올 때(ejs)

원래는 프론트에서 API 데이터를 직접 받아 바로 띄웠기 때문에 get 요청이 필요한지라 axios를 사용했다. 하지만 웹사이트 구조상 메인 페이지에서 영화 포스터 클릭 시 개별 상세페이지로 접속 가능해야 한다. 고로 DB에 있는 영화 정보 중 PK가 일치하는 데이터를 url로 적용하기 위해서는 메인 페이지도 데이터를 전달 받아야 한다.

axios로 데이터를 주고받아야 한다고 생각했는데 ejs 문법을 이용해서도 충분했다. 백에서 render할 때 데이터 보낸 걸 바인딩하는 방식 <%= %>은 잘 알았는데, <%- %>의 존재를 오늘에서야 아차 하고 깨달았다.

<%- %>

이스케이프되지 않는 ejs 문법. 이스케이프 되지 않는다는 말은 곧 문자열을 다른 형식으로 변환하지 않음을 의미한다. 주로 동적으로 생성된 데이터를 HTML에 삽입할 때 사용한다고 한다.

ex.

const data = {
  name: 'John',
  age: 30,
  city: 'New York'
};

객체 형태의 변수를

<script>
  const receivedData = <%- JSON.stringify(data) %>;
  console.log(receivedData);
</script>

ejs 파일에서 JSON 방식으로 변환해 코드를 그대로 읽으면

<script>
  const receivedData = {"name":"John","age":30,"city":"New York"};
  console.log(receivedData);
</script>

js 파일에서 작성한 그대로 ejs script에서 받게 된다.

그냥 <%- data %> 하면?

만약 data가 문자열이라면 정상적으로 동작한다. 말 그대로 문자열을 그냥 써넣은 거니까. 하지만 위처럼 data가 객체인 경우, [object Object]와 같은 문자열이 반환된다.
JSON 데이터를 클라이언트에게 전달할 때 <%- JSON.stringify(data) %>와 같이 사용하는 것이 일반적이다.

라는데 완전히 이해한 느낌은 아니다. 더 많이 써봐야 알듯.

힐링타임

조장이기도 하고 기록하고 정리하는 걸 좋아해서 자발적으로 시작한 팀 노션. 하지만 작성 비율(?)의 99%가 나일 줄은 몰랐다. 공부한 거 같이 나누면서 하면 좋을 거 같았는데.. 아무래도 다들 개인적으로 기록하는 공간이 있는가보다.


덧붙이는 말

  • 스트레스 받기 시작한다. 토요일부터 뭔가 상태가 좋지 않았는데.. 11/7에 프로젝트 시작한 이래로 가장 집중 안 됐다. 건너편에 앉은 사람들의 대화가 빠짐없이 귀에 박혀서 정말 괴로웠다. 우우..

  • 토요일 대사건 :
    메인페이지 기능 얼추 끝났다고 생각했는데 DB 데이터 select 해서 가져오는 걸로 바뀌면서 말짱 도루묵 됐다.
    오늘 쉴까.. 미룰까.. 하다가 쉬워보이는 검색부터 고쳐나갔고 얼추 끝내니까 자연스럽게 메인 페이지를 끼적댔다.

    • 메인에 최신 영화 10개는 이미지 있는 걸로 select해서 걸어놨다. 자동 슬라이드도 해놓고.. 근데 자동 슬라이드 딜레이가 좀 있는 것 같다.

    • 섹션 2, 3을 위한 더미 데이터도 만들었다. sql문이 아니라 컨트롤러에 새로 판 거라 로컬 상관 않고 편하게 쓸 수 있음! 호호.

    • comment랑 movie_info 테이블 join 시도했는데 자꾸 연결 안 된다고 오류 난다.

  • 로직을 꼼꼼히 봤는데 도무지 해결이 안 된다면 어딘가 오탈자 있을 확률이 높다. 정 안 되면 지우고 다시 쓰는 것도 방법..

  • 하면 할수록 더 잘하고 싶다는 생각이 든다. 그런 의미로 내일 하루도 최선을 다하자.

profile
일단 해보는 편

0개의 댓글