2주 프로젝트 회고 - moviebara

project moviebara

Moviebara 👉 https://movie.capybara25.com

깃헙 레포 바로가기

대표적인 VOD 콘텐츠 플랫폼인 넷ㅇㅇㅇ, 왓ㅇ 를 모두 이용중인 유저로서, 늘 불편했던 점이 있었다. 왓ㅇ 에는 최신 콘텐츠가 적었고, 넷ㅇㅇㅇ 에는 옛날 콘텐츠가 적은 동시에 다른 유저들의 별점과 리뷰를 볼 수가 없었다는 것! 때문에 보고싶은게 있을 땐 넷ㅇㅇㅇ 로 가야했고, 뭘 볼까 탐색하고 싶을 땐 왓ㅇ로 가야했다. 덕분에 두 플랫폼 사이에서 왔다갔다 하는 까마귀 유저가 될 수밖에 없었다. 월 결제도 2배!

사실 지금까지 본 영화가 별로 없는데, 또 보고 싶은 욕구는 하늘을 찔러서 보고 싶어요나 찜 목록에 영화들이 쌓여만 가고 있다. 그리고 두 플랫폼에 없는 영화는 메모장에 기록해두거나 그대로 기억 저편으로 잊혀지고 만다. 그렇게 잃어버린 영화가 모르긴 몰라도 적진 않을 것이다.

그렇다면 어떤 영화가 보고싶어지는가? 보통은 다른 사람의 탁월한 스토리텔링과 감상평이 내가 전혀 알지 못했던 영화에 대한 호기심을 불러일으키지 않는가? 그에 더해 좋은 감상평은 영화를 본 후에는 또 색다른 시각을 보여주기도 한다. 여기서 감상평의 역할, 그 중요성에 집중했다. 프로젝트 아이디어는 감상평 그 자체에서 시작해서, 일기를 쓰듯 내가 본 영화를 짧게 기록해두고, 다른 사람의 기록을 저장해둘 수 있는 하나의 블로그 공간을 만드는 것으로 방향성이 정해졌다.


삽질

N:M 관계

  • 유저는 여러 개의 포스트를 스크랩 할 수 있고, 포스트는 여러 번 스크랩될 수 있다.
  • 따라서 유저와 포스트는 스크랩을 연결 테이블로 두고 N:M의 관계를 가지고 있다.
  • 스크랩 테이블에는 유저 아이디와 포스트 아이디 칼럼을 두었다.
  • 포스트의 정보를 불러올 때, 조인된 다른 테이블의 정보를 포함해, 스크랩된 수를 같이 보내야 했다.
// models/index.js
// super many-to-many가 많은 유연성을 제공하기 때문에
// 사용하는 것이 최선일듯 함
user.hasMany(scrap);
scrap.belongsTo(user, {
  foreignKey: "userId",
});

post.hasMany(scrap);
scrap.belongsTo(post, {
  foreignKey: "postId",
});

post.belongsToMany(user, { through: scrap });
user.belongsToMany(post, { through: scrap });
  • 데이터베이스의 count 함수를 사용하기 위해 sequelize.fn을 사용해준다. 첫번째 인자로는 사용할 함수가 들어가고, 두번째 인자에서 .col 메소드를 통해 참조할 열을 설정해준다. 마지막으로 count 함수 결과에 대한 별칭을 부여해줘야 모델에서 해당 결과에 접근할 수 있다.
  • 관계 설정된 외부 테이블의 열을 카운팅하는 것이기 때문에, 먼저 include를 통해 조인하고, 해당 테이블의 id를 참조하도록 했다.
  • 이렇게만 하면 grouping이 설정되지 않았다는 에러가 뜬다. 집계 함수를 사용하기 위해서는 특정 키로 먼저 그룹핑하여 결과를 카운팅해야하는 것 같다.
const postByUser = await post.findAll({
  attributes: [[sequelize.fn("COUNT", sequelize.col("scraps.id")), "scrap"]],
  include: [
    { model: scrap, attributes: [] },
  ],
  group: ["id"],
  where: { userId: query.user_id },
});


Sliding-session

Access Token & Refresh Token 인증

  • 토큰 사용에 대한 전략으로 처음에는 로그인 시 엑세스 토큰과, 조금 더 만료기간이 긴 리프레쉬 토큰을 발급하는 것을 생각했으나, 이 방법을 사용하는 경우 엑세스 토큰은 서버에 따로 저장할 필요 없이 정의 된 비밀 키를 이용해 인증하면 되지만 리프레쉬 토큰은 서버에 따로 저장해두고 검증해야 한다.
  • 엑세스 토큰이 만료되었을 경우, 서버는 엑세스 토큰이 조작되지 않았는지 검증한 후, 저장소의 리프레쉬 토큰과 데이터베이스의 리프레쉬 토큰을 비교하여 동일한 동시에 유효할 경우 새 엑세스 토큰을 발급해주는 것이다.
  • 이 전략은 엑세스 토큰만 사용하는 경우보다 안전하고 사용자가 로그인을 자주 할 필요가 없지만, 서버에 별도의 저장소를 만들어야 하고, 구현이 복잡하며, 엑세스 토큰이 만료될 때마다 검증하고 새로 발급하는 과정에서 HTTP 요청 횟수가 많아진다. 이는 서버의 자원이 낭비됨을 뜻한다. 쉽게 말해 보안성은 높지만 편의성은 떨어진다.


Sliding-session

  • 이 전략은 사이트를 지속적으로 이용하는 유저의 토큰에 대해 자동으로 만료 기한을 늘려주는 것이다.
  • 즉, 유저의 액션이 있을 때, 이 유저가 유효한 엑세스 토큰을 가지고 있을 경우 새로 액세스 토큰을 발급해주는 것이다.
  • 매 요청마다 엑세스 토큰을 인증한 후 새로 발급해주기 위해 미들웨어를 만들어 필요한 요청에 달아주었다. 발급한 토큰은 쿠키로 생성해 주고 열어서 확인하는 식으로 구현했다.
  • 슬라이딩 세션 전략은 액션이 있을 때마다 새로 토큰이 발급되기 때문에, 글을 작성하거나 결제를 하는 와중에 토큰이 만료되는 문제를 방지할 수 있다.
  • 매 요청마다 토큰을 발급해주는 것보다는 글 작성을 시작할 때, 장바구니에 물건을 담았을 때에 토큰을 발급해주는 것이 훨씬 더 안전한 것 같지만, 이에 대해서는 더 생각해봐야 할 것 같다.
// middleware/token.js
const jwt = require("jsonwebtoken");
const generateToken = (payload) => {
  return new Promise((resolve, reject) => {
    jwt.sign(
      payload,
      process.env.ACCESS_SECRET,
      { expiresIn: "1d" },
      (error, token) => {
        if (error) reject(error);
        else resolve(token);
      }
    );
  });
};
const tokenChecker = (req, res, next) => {
  try {
    const token = req.cookies.accessToken;
    if (token) {
      jwt.verify(
        req.cookies.accessToken,
        process.env.ACCESS_SECRET,
        async (err, result) => {
          if (err) {
            res.status(400).json({ message: "auth error" });
          } else {
            delete result.iat;
            delete result.exp;
            const newToken = await generateToken(result);
            res.cookie("accessToken", newToken, {
              path: "/",
              httpOnly: true,
              secure: true, 
              sameSite: "none",
              maxAge: 1000 * 60 * 60 * 24,
              overwrite: true,
            });
            next();
          }
        }
      );
    } else {
      res.status(400).json({ message: "auth error" })
    }
  } catch (err) {
    console.error(err);
  }
};
module.exports = tokenChecker;


후기

첫 프로젝트를 마치고 나니 감격스럽다. 기획부터 차근차근 올라가며 다같이 마무리로 css를 조정할 땐 정말 재밌었다. 아무래도 설날 명절까지 겹친 2주 간의 짧은 프로젝트이다 보니 못한 것도 너무 많고 하고 싶지만 어드밴스드로 뺀 것도 굉장히 많았다. 하지만 기간에 비해 많은 것을 한 프로젝트라고 생각한다. 무엇보다 초반에 정말 어리버리했던 나를 생각하면 나름의 성과가 있었다고 생각한다. 미들웨어를 만들 줄 몰라서 중복 코드가 덕지덕지 묻어있는 걸 보고 팀장님이 많이 놀라셨다. 처음엔 좀 창피했지만 그렇다면 내가 미들웨어를 만들겠다 선언하고 여러 군데의 예시 코드들을 조합해가면서 뚝딱대며 만들었는데 제대로 돌아가는 걸 보니 참 뿌듯했다. 막상 해보니 별 거 아니잖아? 라고 생각하곤 건방짐에 놀랐다. 몰랐던게 당연해지는 게 기분이 좋다. 앞으로도 새로운걸 많이 배우고 익숙해지고 싶다.

Categories:

Project

Load Comments