1차 프로젝트 회고록 | thisisnevercode

김태규·2021년 10월 19일
1
post-thumbnail

프로젝트 소개

이번 1차 프로젝트는 지금까지 배운 프론트엔드와 백엔드 지식들을 바탕으로 선정된 사이트를 클론 코딩해보는 방식으로 진행되었다. 팀원은 나 자신을 포함하여 5명으로 구성되었고, 5명 모두 프론트엔드와 백엔드에서 업무를 분담하여 1주는 프론트엔드, 다른 1주는 백엔드를 개발하는 방식으로 프로젝트가 진행되었다. 팀명은 클론 코딩할 사이트의 이름에서 따와서 thisisnevercode 로 정했다.

thisisneverthat 사이트
thisisnevercode 프론트엔드 GitHub
thisisnevercode 백엔드 GitHub
thisisnevercode 배포


사용된 기술

프론트엔드

  • React
  • React Router
  • Sass
  • Restful API
  • Git & GitHub

백엔드

  • Node.js
  • Express
  • MySQL
  • JWT
  • Bcrypt
  • Git & GitHub

팀의 역할 분배

프론트엔드

메인 페이지(김태규)

  • 캐러셀
  • 반응형에 따른 컨텐츠 재배치
  • 스플래쉬 스크린

리스트 페이지(김민재)

  • 영역 별 호버 효과
  • 색상 별 이미지 전환
  • 무한 스크롤

Nav & Footer(김휘민)

  • 드롭다운 메뉴
  • 브레드 크럼 구현
  • SORT 및 VIEW 모달창 구현

상세 페이지(김동권)

  • 캐러셀
  • 반응형

회원가입 & 로그인(윤창현)

  • validation 기능 구현
  • 약관 동의

백엔드

상세 페이지 API (김태규)

  • 상품 ID에 따른 상품 데이터 전달

리스트 페이지 API (김동권)

  • 무한스크롤 구현을 위한 상품 데이터 전달

정렬 API (김휘민)

  • 가격별 주문량에 따라 ordering된 상품 데이터 전달

카테코리 API (윤창현)

  • 카테고리와 서브 카테고리 데이터 전달

회원가입 및 로그인 API (김민재)

  • access token를 활용한 회원가입, 로그인과 로그아웃 기능(API)

모델링


thisisnevercode 시연 영상


협업의 경험

지금까지 동기 분들과 함께 공부하면서도 항상 팀이 존재했고, 함께 미션을 수행하면서 의견을 나누고 서로의 코드도 리뷰해주면서 진행해왔지만, 프로젝트를 5명의 협업으로 진행하는 것은 다른 차원의 문제였다.

일단 5명의 업무가 정확하게 분배되어 있기 때문에 본인이 맡은 부분을 잘 구현해야겠다는 책임감을 많이 느꼈고, 지속적으로 팀원들과 소통을 하기 위해서는 본인의 코드뿐 아니라 다른 팀원들의 코드와 프로젝트의 전체적인 흐름을 파악하는 것이 중요하다고 느꼈다. 같은 이유로 본인의 코드는 팀원들에게 읽히기 때문에 이해하기 쉽도록 작성해야 하고, 꾸준하게 리팩토링하는 것이 중요하다고 생각했다.

각자의 결과물을 깃헙을 통해서 합치고, 프론트엔드와 백엔드의 결과물을 합칠 때에는 많은 시행착오를 겪었다. 하지만 그런 과정을 거치면서 개발자로서 성장하는 것을 느꼈고, 코딩 실력을 기르는 것도 물론 중요하지만 동료들에게 함께 일하고 싶은 개발자가 되야겠다고 다짐했다.


아쉬운 점

시간이 촉박한 편이었고, 5명 모두 프로젝트 경험이 처음이었기 때문에 처음에 프로젝트를 계획할 때, 너무 이것저것 욕심부리기보다는 기본적인 것에 충실하자고 의견을 모았다. 그래서 구현하지 못한 기능이 있는데, 조금 더 미리 계획을 잘 세웠다면 욕심내서 추가적으로 구현해볼 수 있는 부분이 있었을 것이라고 생각했다. 2차 프로젝트를 진행할 때는 1차 때를 교훈 삼아서 많은 부분을 구현해보고 싶다.


기록하고 싶은 코드

이미지 슬라이드

메인 페이지에 이미지 슬라이드를 반응형으로 구현했는데 브라우저 창의 크기가 클 때에는 기본적으로 슬라이드에 4개의 이미지가 보여지는데 버튼을 눌렀을 때 4개의 이미지 중에서 2개씩 넘어가게 구현을 했고, 창의 크기가 작을 때에는 컨텐츠가 재배치 되어서 슬라이드에 2개의 이미지가 보여지고 버튼을 눌렀을 때 1개씩 넘어가도록 구현했다.

반응형을 고려하여 창의 크기에 따라 경우를 나눴고 각 경우마다 수학적인 로직이 들어갔기 때문에 재미있게 코딩할 수 있었다.

class ImageSlide extends Component {
  constructor() {
    super();
    this.state = {
      imagePosition: 0,
    };
  }

  changeImageToLeft = position => {
    const slideLength = this.props.images.length;
    let newPosition = position - 1;
    let maxPosition;

    if (window.innerWidth > 1140) {
      maxPosition = Math.ceil((slideLength - 4) / 2); // 큰 화면용
    } else {
      maxPosition = Math.ceil(slideLength - 2); // 작은 화면용
    }

    if (newPosition < 0) newPosition = maxPosition; // 오른쪽 최대치 넘어가면 제일 왼쪽으로 되돌림

    this.setState({
      imagePosition: newPosition,
    });
  };

  changeImageToRight = position => {
    const slideLength = this.props.images.length;
    let newPosition = position + 1;
    let maxPosition;

    if (window.innerWidth > 1140) {
      maxPosition = Math.ceil((slideLength - 4) / 2); // 큰 화면용
    } else {
      maxPosition = Math.ceil(slideLength - 2); // 작은 화면용
    }

    if (maxPosition < newPosition) newPosition = 0; // 왼쪽 최대치 넘어가면 제일 오른쪽으로 되돌림

    this.setState({
      imagePosition: newPosition,
    });
  };

  render() {
    const { images } = this.props;
    const { imagePosition } = this.state;

    return (
      <div className='ImageSlide'>
        <div className='slideBox'>
          <button
            className='slideBtn btnPrev'
            onClick={() => this.changeImageToLeft(imagePosition)}
          >
            <FontAwesomeIcon icon={faChevronLeft} size='2x' />
          </button>
          <button
            className='slideBtn btnNext'
            onClick={() => this.changeImageToRight(imagePosition)}
          >
            <FontAwesomeIcon icon={faChevronRight} size='2x' />
          </button>
          <div
            className='slideList'
            style={{
              transform: `translateX(
                ${imagePosition * -50}vw`,
            }}
          >
            {images &&
              images.map(image => (
                <Link
                  className='slideContent'
                  to='/products/shoes'
                  key={image.id}
                >
                  <img
                    className='slideImage'
                    alt={image.name}
                    src={image.imgUrl}
                  />
                </Link>
              ))}
          </div>
        </div>
      </div>
    );
  }
}

export default ImageSlide;

상세페이지 API

thisisnevercode 상세페이지 API

프론트엔드 상세페이지에서 원하는 형태로 데이터를 가공하여 보내는 것을 목표로 코드를 짰다. 우선 데이터의 id 값에 해당하는 데이터를 1개씩 뽑아야했고, 객체 데이터 안에 value 값으로 detailImages 와 subImages 가 배열형태로 들어와야 했기 때문에 쿼리문으로 원하는 데이터를 뽑은 후, 마지막에 자바스크립트 문법으로 데이터 형태를 완성하였다.

// productDao.js

const getProductById = async (id) => {
  const detailImages = await prisma.$queryRaw`
    SELECT
      key_number as keyNumber,
      di.detail_image_url as detailImg
    FROM
      products P
    LEFT JOIN
      detail_images di
    ON
      di.product_id = p.id
    WHERE
      p.id = ${id}
  `;

  const subImages = await prisma.$queryRaw`
    SELECT
      key_number as keyNumber,
      si.sub_image_url as subImg
    FROM
      products P
    LEFT JOIN
      sub_images si
    ON
      si.product_id = p.id
    WHERE
      p.id = ${id}
  `;

  const [product] = await prisma.$queryRaw`
    SELECT 
      p.id,
      p.name,
      p.price,
      p.description,
      p.textile_information as textileInfo,
      p.main_image_url as mainImg
    FROM 
      products p
    WHERE
      p.id = ${id}
  `;

  if (product) {
    product.detailImg = detailImages;
    product.subImg = subImages;
  }

  return product;
};

2개의 댓글

comment-user-thumbnail
2021년 10월 21일

태규님의 코드처럼 읽기 쉬운 글,, 잘봤습니다 고생많으셨습니다 디네코..😀 다음주에 디네댓 후드티 입고 오겠습니다,,

답글 달기
comment-user-thumbnail
2021년 10월 23일

태규님 너무 멋진 회고록 잘 읽었습니다 함께해서 즐거웠습니다^^

답글 달기