Hush 프로젝트 회고록

정다영·2022년 10월 2일
3
post-thumbnail

일주일 동안 기본적인 api 공부를 마치고 2주간 러쉬 홈페이지를 기반으로 한 프로젝트를 진행했다. 프로젝트가 끝난 기념으로 회고록을 작성하려고 한다.


프로젝트 간단 소개

개발 기간: 2022.09.19 ~ 2022.09.30
개발 내용: 러쉬 사이트를 클론한 디저트 판매 사이트
개발 상세 내용

  • 회원가입 및 로그인
  • 상품 리스트(필터 및 정렬 기능 포함)
  • 상품 상세 페이지
  • 장바구니
  • 찜하기

팀 구성

프론트엔드 : 김대호(조장), 강루비, 양석문, 주혜린
백엔드 : 김정훈, 정다영, 정승렬

기술스택

javascropt, node.js, express mysql8.0, Postman, JWT


프로젝트 시작

러쉬 사이트를 배정받으며 가장 우려했던것은 저작권이었다. 이전에 러쉬측에서 이미지 사용을 허락받지 못한 사정이 있기에 우리 팀 또한 러쉬를 클론코딩 하되 디저트를 판매하는 사이트로 변경하였다.

일정관리


trello를 활용해 프론트엔드와 일정을 공유하면서 체크리스트를 관리했다.

  • backlog : 총 전체 할당량
  • This sprint : 일주일 내로 해야할 작업
  • In Progress : 현재 진행중인 작업
  • In Review : 리뷰중인 작업
  • Done : 끝난 작업
  • 회의록 : 매일 아침 10시 30분에 진행된 회의기록

DB모델링

가장 먼저해야할 것은 db 모델링이었다. db는 프로젝트 중간에 수정하면 전체코드의 변경이 올 수 있는 참사가 일어나기에 팀원들과 가장 오랫동안 시간을 투자했다.

멘토님의 조언을 바탕으로 최종 완성된 ERD는 다음과 같다.


나의 역할

  1. 회원가입 구현
    • 가입할 이메일 중복 검사 구현
    • 가입 성공시 비밀번호 BCRYPT 암호화 후 DB에 저장 구현
  2. 로그인 구현
    • 로그인 성공시 토큰 발행 구현
  3. 찜목록 CRD 구현
    • 상품 상세페이지에서 제품의 찜하기 구현
    • 마이페이지 내 찜목록 리스트 보여주기 구현
    • 체크박스에 표시된 상품들의 삭제 구현
  4. 제품 구매 C 구현
    • 구매 생성 구현
  5. 검색 구현
    • 제품 검색시 검색어와 일치한 제품 리스트 제공 구현
  6. 리펙토링
    • 가독성을 위한 코드 수정
    • 변수 통일 및 엔드포인트 변경


기억에 남는 코드

1. 구매하기 api 구현 中 TRANSACTION

const createOrder = async (userId, productId, total, reqMessage, address) => {

    await queryRunner.connect();
    await queryRunner.startTransaction();

    try{

        userPoint = await orderDao.checkPoint(queryRunner,userId);

        if(userPoint < total){
            const error = new Error("LACK_OF_POINT")
            error.statusCode = 400;
            throw error;
        }

        await orderDao.deductPoint(queryRunner,userId, total);


        const items = await orderDao.getCart(queryRunner, userId, productId);

        const orderId = await orderDao.createOrder(queryRunner,userId, reqMessage, address);
        await orderDao.createOrderItems(queryRunner,orderId, items);
        const checkStock = await orderDao.checkStock(queryRunner,productId);
        if(checkStock.length != 0 ){
            const error = new Error(`${JSON.stringify(checkStock)}_IS_LACK`);
            error.statusCode = 400;
            throw error;
        }
        await orderDao.deleteProductStock(queryRunner,productId);
        await orderDao.deleteCart(queryRunner, userId, productId);

        await queryRunner.commitTransaction();


    } catch (err) {
        await queryRunner.rollbackTransaction();
        const error = new Error('ROLLBACK' +' : ' + err.message);
		error.statusCode = 400;
		throw error

    }
    return;

}

트랜잭션이란 간단히 말해 데이터베이스의 상태를 변화시키기는 작업의 단위를 말한다. DB의 상태를 변화시킨다는 것은 sql를 이용하여 데이터베이스에 접근하는 것을 의미한다.

트랜잭션의 과정을 간단히 설정하자면 트랜잭션으로 묶은 단위는 한번에 모두 반영되어야 한다. 만약 한 곳에서 오류가 났다면 이미 처리된 트랜잭션을 다시 rollback함으로서 원상복귀시킨다.

나는 queryrunner을 활용해서 service층 내에 트랜잭션을 구현했다. service층에서 queryrunner 선언한 후 model층 안에 있는 메소드 안에 매개변수로 queryrunner를 전달해준다. 그렇게 queryrunner를 전달받은 모든 메소드들은 하나의 queryrunner를 사용해게 되고 트랜잭션이 이루어진다.

query문이 존재하는 model층에 queryrunner을 사용하지 않고 service층에서 선언한 이유는 무엇일까? 이유는 다음과 같다.

  • queryrunner를 사용하기 위해서는 하나의 메소드안에 여러 query문을 선언해야 한다.그렇다면 그 query문은 재사용하기 어려워진다. 자바스크립트에서는 하나의 query를 사용하기 위해 여러개가 담긴 메소드를 호출해야 할 것이다.

  • 메소드를 사용하고 싶지만 동시에 queryrunner도 선언하고 싶다면 여러 queryrunner를 선언해아 한다. 하지만 그렇게 되면 한 queryrunner에서 동작하지 않으므로 트랜잭션이 이루어지기 어렵다.


2. 카트의 제품 추가 中 UPSERT

const addCart = async (product_id, quantity, userId) => {
	return await dataSource.query(`
	INSERT INTO carts(
		user_id,
		product_id,
		quantity
	) VALUES (
		?,
		?,
		?
	) ON DUPLICATE KEY UPDATE 
		quantity = quantity + ?
	`, [userId, product_id, quantity, quantity]
	)

이번 코드는 다른 팀원의 코드를 리팩토링 하는 과정에서 작성했다. 카트에 제품이 1개 담겨있는 상황에서 내가 또 제품을 담으려면 update문을 통해 제품의 quantity만 변경해야 한다. 즉 데이터를 insert할 때 이미 있는 데이터라면 지정된 컬럼 데이터를 update하는 작업이다.

팀원은 카트에 있는 제품 리스트를 검사하는 메소드를 만들었다. 그래서 만약 리스트내에 제품이 있다면 update 없다면 insert를 하도록 코드를 짰다. 하지만 리팩토링을 통해 upsert를 통해 하나의 쿼리문으로 두 가지의 작업을 동시에 처리하도록 변경하였다.


3. 제품 리스트 가져오기 中 JSON_ARRAYAGG


const getProduct = async (productId) => {
  const [result] = await dataSource.query(`
    SELECT
      p.id,
      p.name,
      p.price,
      p.stock,
      c.name as category_name,
      p.thumbnail_image_url,
      JSON_ARRAYAGG(i.image_url) AS image_url
    FROM 
      products p
    JOIN 
      categories c
    ON  
      c.id = p.category_id
    JOIN
      product_images i
    ON
      p.id = i.product_id
    WHERE 
      p.id = ?`, [productId]
  )

  if(typeof result.image_url == "string"){
    result.image_url = result.image_url.replace("[",'');
    result.image_url = result.image_url.replace("]",'');
    result.image_url = result.image_url.replace(/"/g,'');
    result.image_url = result.image_url.replace(/ /g,'');
    result.image_url = result.image_url.split(",");
  }

  return result

마지막 코드 또한 다른 팀원의 코드를 리팩토링 하는 과정에서 작성했다.
JSON_ARRAYAGG를 사용하면 group by 결과를 배열로 받을 수 있다.
예를 들어 한 제품이 2개의 이미지를 갖는다고 가정하자. JSON_ARRAYAGG를 사용하지 않는다면 하나의 속성에서 이미지 url로 구성된 2개의 데이터가 나오고, 나머지는 중복된 데이터들이 나온다. 우리는 중복된 데이터를 지우기 위해 JSON_ARRAYAGG를 사용해 이미지라는 속성에 url를 배열타입으로 묶어준다.

중간의 if문은 mysql의 오류를 처리하기 위한 코드이다. JSON_ARRAYAGG로 묶은 데이터의 데이터타입은 mac OS에서 배열로 처리되지만 우분투는 string으로 작성된다. 이는 mysql의 오류이므로 이를 해결하기 위해 string타입인 경우 배열로 처리하는 로직을 작성하게 되었다.


결과물

1. 회원가입

2. 로그인

3. 찜목록

4. 제품 구매

5. 검색


좋았던 점📣

  1. 팀 협업
  • 좋은 팀원들을 만나서 팀프로젝트 내내 즐거움이 끊이질 않았다. 서로 자유롭게 의견과 지식을 공유하는 분위기였고, 그 부분이 결과물에 잘 반영된거 같아 뿌듯하다.
  • 개인프로젝트와의 차이점 또한 결과물에서도 들어난다. 혼자 풀스택으로 모든 코드를 짰을 때는 내가 부족한 부분에 대해 채워줄 사람이 없었다. 하지만 이번 프로젝트의 름다운 홈페이지 디자인을 보며, 단연코 나혼자는 이런 결과물을 내놓을 수 없을 거라 생했다.
  1. 리팩토링 과정 속 깨달음
  • 리팩토링은 다른 사람이 작성한 코드 또한 내가 분석하며, 코드를 가독성 있으며 통일감이 있게 작성되어야 한다는 걸 뼈저리게 느꼈다. 특히 퀴리문의 겨우 가독성이 떨어지면 오류를 찾기 어려워진다. 그리고 장문의 코드들을 몇 줄로 줄일 때 즐거움도 상당했다.
  1. 프로젝트를 진행하기 위한 다양한 tool 사용
  • 팀 프로젝트에서 최대한의 퍼포먼스를 내기 위해 trello, notion, git등을 활용하여 일정을 관리하고 코드를 공유하였다. 프로젝트를 위한 git 사용이 이번이 처음이 어려움이 컸다. 하지만 2주차를 지나서는 git 사용이 익숙해진 거 같아 개인적으로 가장 뿌듯하다. 다음에는 일정관리를 위한 툴을 좀 더 공부해보고 싶다.
  1. 나의 실력 향상 및 깨달음
  • express를 공부하며 미들웨어의 목적이나 활용이 이해가 안 갔다. 이번 프로젝트를 통해 미들웨어가 동작하는 원리를 알게되었고, 에러핸들링을 위한 미들웨어를 작성해보며 기본 자바스크립트의 실력 또한 향상된 거 같아 마음이 뿌듯했다.
  • 이번 러쉬 클론 코딩 프로젝트를 진행하면서 내가 무엇을 공부해야 할지 알게되었다. 결국 기본기를 탄탄히 해야 에러 발생률을 줄일 수 있다는 것과 아주 복잡한 로직도 단 한줄의 함수 하나로 작성할 수 있는 상황을 경험하며, 자바스크립트와 sql문에 대해 공부를 많이 해야겠다고 다짐했다.

아쉬웠던 점😅

  1. 초기셋팅 부족
  • 팀원들과 초기셋팅시 엔드포인트 상의를 하지 않아 프로젝트 중간에 github에서 충돌이 많이 일어났다.
  • 변수명을 먼저 정하고 프로젝트를 진행하지 않아 프론트엔드와 통신시 변수명을 일치시키는 작업이 필요해서 불필요한 시간낭비가 있었다.
  1. 기능 부족
  • 우리의 목표는 가장 기본적인 쇼핑몰을 구현하되 정확한 로직을 배워가자는 것이었다. 그리고 시간이 남는다면 기능을 추가하자고 했지만 결국 기능을 추가하지 못했다. DB가 매우 간단했기에 api 추가 생성에는 어려움이 없었지만 기능추가는 독단적으로 진행할 수 없어 프론트와 조율이 필요했다. 하지만 시간 부족으로 결국 추가 기능 구현이 안되서 아쉬웠다.
  1. 일정 관리 부족
  • trello를 통해 일정을 공유했지만 기한을 짧게 잡지 않았고, 또 기한내로 작업이 진행되지 않은 경우가 많았다. 아침에 무리하지 않게 오늘의 task를 정했다면 저녁까지는 완성을 목표로 하고, 저녁에 이를 체크하는 시간을 가졌으면 좀 더 수월한 진행이 이루어졌을 거 같다.
  1. 백엔드 조장 부재
  • 조장이 있었다면 결정을 빠르게 내리고, 백엔드의 일정관리가 수월했을 거 같다.
    조장의 부재로 결정이 느려지고, 일정을 각자 세우다 보니 팀원간의 속도차이가 많이 났다.
  1. 에러 처리 부족
  • 에러는 프론트와 백엔드 모두 처리해야 안전하게 사이트가 돌아갈 수 있다. 프론트엔드에서는 사용자 편의를 위한 검증이 필요하고, 백엔드는 보안 또는 서버의 안정을 위한 검증이 필요하다. 이번 프로젝트에서는 프론트엔드 혹은 백엔드 한곳에서의 에러처리만 이루어져 불안정한 통신이 이루어질 때가 종종 있었다.


다음 프로젝트에서는 무엇을 해야 할까?🗒️

결국 1차프로젝트 때 아쉬웠던 점을 줄여보는 방향으로 2차 프로젝트를 진행해야 후회를 안 할 거 같다. 내가 2차 때 해야할 리스트를 작성해보겠다.

1. 초기 api 분기 작성

  • 초기에 api 분기를 작성하면 프론트와 백엔드 모두 정해진 변수명만 사용하여 통신시 오류가 날 일이 적어진다. 또 작업 중 불필요한 소통을 줄일 수 있어 작업 속도를 높일 수 있다.

2. 간트 차트 작성

  • 간트 차트를 작성하며 작은 작업 또한 기한을 설정할 수 있어서 좋을 거 같다. 모든 팀원이 간트 차트를 작성하고 공유한다면 기한내에 목표를 달성하고 추가기능까지 구현할 수 있을 거 같다.

3. 하루 체크리스트 작성

  • 아침 회의시간 때 작성하는 체크리스트는 각 팀원의 할당량과 목표를 확인하는 방법이지만, 완수했는지 시간을 갖지 않아 실제로 작업이 진행되지 않는 경우가 부지기수였다. 이번 프로젝트에는 저녁에 체크리스트를 완수했는지 확인하는 시간을 가지고 싶다.

4. 에러처리 고민

  • 다음 프로젝트에는 에러 처리에 시간을 투자해보고 싶다. 통신시 서버를 동작할 때는 다양한 변수가 생긴다. 이를 예방하는 방법으로 에러처리를 함으로써 통신의 오작동을 줄여보고 싶다.
profile
Hello World~

5개의 댓글

comment-user-thumbnail
2022년 10월 3일

역시,, 다영님 프로젝트 말해뭐해 ~~~!! 😆👍

답글 달기
comment-user-thumbnail
2022년 10월 3일

눈물 한방울 똑 떨어지는 감동적인 포스팅입니다.

답글 달기
comment-user-thumbnail
2022년 10월 3일

정말 좋은글 보고갑니다~ 한번 제가 활용해보도록 하겠습니다!!

답글 달기
comment-user-thumbnail
2022년 10월 3일

포스팅까지 완뵥한 다영님,,😲👍🏻 함께해 행복했습니닷🥰

답글 달기
comment-user-thumbnail
2022년 10월 6일

Thanks for the information, I will try to figure it out for more.
Taco Bell Survey

답글 달기