2차 프로젝트 회고록(Megabox)

Jetom·2021년 11월 4일
1

잡담

목록 보기
9/10
post-thumbnail

2차 프로젝트에 대한 짧은(?) 회고록을 쓰려한다.. 점점 회고록을 쓸 일이 많아지면 좋겠당!! 그래야 취업하지...🤣


메가박스... 🥴 저희 메가폭스도 잘만들었습니다!(만들면서 멘붕이 너무 와서 문제..)

👉 멘붕의 시작인 메가폭스 깃허브

🎬 팀소개

👩🏻‍💻 프로젝트 기간

  • 2021.10.18 ~ 2021.10.28

🔎   팀명

  • Megafox
    뭐하지...? 열심히 고민하다 다빈님의 아이디어로 탄생했습니다 ^^7

🐥   팀원 소개 및 구현 기능

FrontEnd

  • 신혜리(Me! 🙇‍♀️)
    공용 탭 컴포넌트 구현
    전체 영화 페이지 구현
    영화 상세 페이지 구현
    전체 영화 페이지네이션 구현
    전체 영화 이미지 hover시 API로 받아오는 줄거리 내용이 나타나도록 구현
    동적 라우팅을 이용해 각 영화 상세 페이지로 넘어가게 구현
    관람 등급에 따라 아이콘을 이미지가 아닌 스타일로 구현

  • 강단
    Header영역 navigation구현
    sitemap 구현
    영화 명에 마우스 호버 시 해당 영화 포스터 출력되는 기능 구현
    로그인/아웃 상태에 따른 유저 정보 출력 구현
    메인페이지 : api를 사용한 관객 수 top 4 영화리스트 출력
    영화 포스터에 마우스 호버 시 해당 영화 정보 출력 기능 구현

  • 정찬영
    공용 style variables, font 추가
    소셜 로그인 (카카오) 구현
    전체 극장 페이지 구현
    스크롤 이벤트 (상단 이동 버튼) 구현
    예매 페이지 구현
    날짜 슬라이드 구현
    상영 유무에 따른 블러처리 구현
    다중 선택 버튼 구현, 선택된 타겟에 따른 API호출 구현

BackEnd

  • 이다빈
    카카오 소셜 로그인 API를 활용한 로그인 API(GET)
    유저 정보 호출 API (GET)
    유저 권한 확인용 로그인 데코레이터 구현
    리뷰 생성, 삭제, 수정 API (POST, GET, DELETE, PATCH)
    빠른예매, 예매내역 API(POST, GET)

  • 김도훈
    영화 리스트/상세페이지 API (GET)
    영화관 리스트 API (GET)
    영화, 영화관, 댓글 좋아요/삭제 API (POST)
    빠른예매, 예매내역 API (POST, GET)

🛠   적용기술

  • FrontEnd :React, React Hooks, React Route ,Styled-Components

  • BackEnd :Python@3.8.11, Django@3.2.8, MySQL@8.0.27

  • 배포 :AWS(EC2, RDS, LB)


열심히 쓴 코드입니다 예쁘게 봐주세요 👼

기능 구현 및 코드 공유

🎹 Tab Component(공통)

전체 영화 페이지 및 상세 페이지에 tab이 똑같은 색으로 들어가기에 공통 컴포넌트로 만들게 됐다. 😊

코드 공유

import React from 'react';
import styled from 'styled-components/macro';
import { flexAround } from 'styles/mixin';

//다른곳에서 쓰이는 컴포넌트이기 때문에 props로 내려받아야 할 속성들을 지정한다.
function Tab({ tabList, selectedTab, toggleTab }) {
  return (
    <TabMenuContainer>
      <TabMenu>
    	//받아온 data를 map으로 구현합니다.
        {tabList.map(tab => {
          return (
            <TabMenuList
            //선택된 tab의 id가 selectedTab과 일치하면 TabMenuList 컴포넌트로 인해 border의 컬러가 바뀌게된다.
              isSelected={selectedTab === tab.id}
              key={tab.id}
              onClick={() => {
                toggleTab(tab.id);
              }}
            >
              {tab.menu}
            </TabMenuList>
          );
        })}
      </TabMenu>
    </TabMenuContainer>
  );
}


//styled-components
const TabMenuContainer = styled.div`
  max-width: 1100px;
  margin: 0 auto;
  padding-top: 40px;
`;

const TabMenu = styled.ul`
  ${flexAround}
`;

const TabMenuList = styled.li`
  width: 20%;
  padding: 10px;
  text-align: center;
  color: ${({ isSelected, theme }) => (isSelected ? theme.purple : '#222')};
  border: 1px solid
    ${({ isSelected, theme }) => (isSelected ? theme.purple : '#ebebeb')};
  border-bottom: 1px solid
    ${({ isSelected, theme }) => (isSelected ? 'transparent' : theme.purple)};

  cursor: pointer;
`;

export default Tab;

🎫 전체영화 페이지

전체영화 페이지에서 구현한 내용이 많은데, 구현한 기능을 한번 더 설명하고 코멘트로 자세히 설명하겠다. 😁

  • 전체 영화 이미지 hover시 API로 받아오는 줄거리 내용이 나타나도록 구현
  • 동적 라우팅을 이용해 각 영화 상세 페이지로 넘어가게 구현
  • 관람 등급에 따라 아이콘을 이미지가 아닌 스타일로 구현

코드 공유

import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import Tab from 'components/Tab/Tab.js';
import styled from 'styled-components/macro';
import { flexCenter } from 'styles/mixin';

//LIMIT은 상수 데이터이므로 function 밖에서 선언해주었다.
const LIMIT = 4;

function Movies() {
  const [moviePosterList, setMoviePosterList] = useState([]);

  // offset을 제외한 tab, tabCurrent, toggleTab은 tab 컴포넌트에서 내려 받는것들!
  const [tab, setTab] = useState([]);
  const [tabCurrent, setTabCurrent] = useState(1);

  const [offset, setOffset] = useState(0);

  const toggleTab = idx => {
    setTabCurrent(idx);
  };

  // custom hook으로 빼주려다가 시간이 촉박해서 주소를 넣었다 😅 리팩토링 할 예정...
  useEffect(() => {
    fetch(
      `배포용주소/movie?limit=${LIMIT}&offset=${offset * LIMIT}`
    )
      .then(res => res.json())
    
      //...(Spread Operator)를 사용하지 않으면 값 공유가 되어 페이지네이션이 많이 아파한다...
      .then(res => setMoviePosterList(prev => [...prev, ...res]));
  }, [offset]);

  useEffect(() => {
    fetch('data/TabData.json')
      .then(res => res.json())
      .then(res => setTab(res));
  }, []);

  const findMovie = () => {
    setOffset(offset + 1);
  };

  //조건에 따라 색이 바뀌게 구현됨(리팩토링 필요한 코드!)
  const ageLimit = age => {
    if (age === '12세이상관람가') {
      return '#188ef7';
    }
    if (age === '15세이상관람가') {
      return '#eba010';
    }
    if (age === '전체관람가') {
      return '#3fa701';
    }
    if (age === '청소년관람불가') {
      return '#e81002';
    }
  };

  return (
    <MoviesContainer>
      <MoviesLayout>
        <MoviesTitle>전체영화</MoviesTitle>
    
    	//공통 컴포넌트에 필요한 props를 받아왔다.
        <Tab tabList={tab} selectedTab={tabCurrent} toggleTab={toggleTab} />
        <MoviesSearchContents>
          <MoviesSearchNewRelease>
            <NewReleaseBtn>
              <span />
            </NewReleaseBtn>
            <p>개봉작만</p>
            <p>10개의 영화가 검색되었습니다.</p>
          </MoviesSearchNewRelease>

          <MoviesSearch>
            <input placeholder="영화명 검색" />
            <button>
              <i className="fas fa-search" />
            </button>
          </MoviesSearch>
        </MoviesSearchContents>
        <MoviesListContainer>
           //map 함수를 이용해 movie list를 구현
          {moviePosterList?.map(info => {
            return (
              <MoviesList key={info.movie_id}>
                <MoviesSummaryContainer>
                  <Link to={`/movie-info/${info.movie_id}`}>
                    <MoviesPoster
                      src={info.image[0].main_image_url}
                      alt={info.ko_name}
                    />
                    <MoviesSummary>
                      <p>{info.description}</p>
                    </MoviesSummary>
                  </Link>
                </MoviesSummaryContainer>

                <MoviesListTitle>

                  <MoviesAge age={ageLimit(info.age_rate)}>
		  //12, 15는 숫자만 나오게하고 그외는 정해진 글자로 나오도록 설정(리팩토링이 필요한것같다.)
                    {(() => {
                      if (parseInt(info.age_rate, 10)) {
                        return parseInt(info.age_rate, 10);
                      } else {
                        if (info.age_rate === '청소년관람불가') {
                          return '청불';
                        }
                        if (info.age_rate === '전체관람가') {
                          return '전체';
                        }
                      }
                    })()}
                  </MoviesAge>
                  <h3>{info.ko_name}</h3>
                </MoviesListTitle>
                <MoviesInfo>
                  <p>예매율 13.6%</p>
                  <span />
                  <p>{info.release_date}</p>
                </MoviesInfo>
                <MoviesBtn>
                  <LikeBtn>
                    <i className="far fa-heart" />
                    <p>{info.like}</p>
                  </LikeBtn>

                  <BookingLink to="/booking">
                    <BookingBtn>
                      <p>예매</p>
                    </BookingBtn>
                  </BookingLink>
                </MoviesBtn>
              </MoviesList>
            );
          })}
        </MoviesListContainer>

	//클릭시 movie list들이 4개씩 나온다.
        <SeeMore onClick={findMovie}>
          더보기
          <i className="fas fa-chevron-down" />
        </SeeMore>
      </MoviesLayout>
    </MoviesContainer>
  );
}

//styled-components
const MoviesContainer = styled.div`
  padding-top: 40px;
`;

const MoviesLayout = styled.div`
  max-width: 1100px;
  margin: 0 auto;
`;

const MoviesTitle = styled.h2`
  padding: 0 0 26px 0;
  font-size: 28px;
  color: ${props => props.theme.gray};
`;

const MoviesSearchContents = styled.div`
  display: flex;
  justify-content: space-between;
  margin-top: 40px;
  margin-bottom: 15px;
`;

const MoviesSearchNewRelease = styled.div`
  display: flex;
  align-items: center;

  p {
    margin-left: 10px;
    font-size: 15px;
    color: #ccc;

    &:last-child {
      margin-left: 20px;
      font-size: 16px;
      color: #222;
    }
  }
`;

const NewReleaseBtn = styled.button`
  position: relative;
  width: 26px;
  height: 14px;
  border-radius: 32%;
  border: transparent;
  background: #ccc;

  span {
    position: absolute;
    display: inline-block;
    width: 11px;
    height: 11px;
    top: 1.46px;
    left: 2px;
    border-radius: 50%;
    background: white;
  }
`;

const MoviesSearch = styled.div`
  position: relative;
  display: flex;
  border: 1px solid #d8d9db;

  input {
    width: 80%;
    height: 34px;
    padding-left: 10px;
    border: transparent;

    &:focus {
      outline: none;
    }
  }

  button {
    width: 20%;
    border: transparent;
    background: transparent;

    i {
      position: absolute;
      top: 8px;
      right: 9px;
      font-size: 16px;
      color: #929292;
    }

    &:focus {
      outline: none;
    }
  }
`;

const MoviesListContainer = styled.div`
  display: flex;
  flex-wrap: wrap;
`;

const MoviesList = styled.div`
  display: flex;
  flex-direction: column;
  min-width: 20%;
  max-width: 22%;
  margin-right: 30px;
  margin-bottom: 60px;
`;

const MoviesPoster = styled.img`
  width: 100%;
`;

const MoviesSummaryContainer = styled.div`
  position: relative;
  width: 100%;
  height: 100%;
`;

const MoviesSummary = styled.div`
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 99%;
  padding: 20px;
  color: white;
  line-height: 1.4;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;

  p {
    height: 150px;
    font-size: 15px;
    overflow: hidden;
  }

  &:hover {
    opacity: 1;
  }
`;

const MoviesListTitle = styled.div`
  display: flex;
  align-items: center;
  margin-top: 15px;

  h3 {
    width: 70%;
    margin-left: 5px;
    font-size: 20px;
    font-weight: 400;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
`;

const MoviesAge = styled.span`
  display: inline-block;
  width: 23px;
  height: 23px;
  line-height: 2.2;
  border-radius: 50%;
  text-align: center;
  font-size: 11px;
  color: white;
  background: ${({ age }) => age};
`;

const MoviesInfo = styled.div`
  display: flex;
  margin-top: 10px;
  font-size: 16px;

  span {
    margin: 0 5px;
    border-left: 1px solid #d8d9db;
  }
`;

const MoviesBtn = styled.div`
  display: flex;
  margin-top: 10px;
`;

const LikeBtn = styled.button`
  ${flexCenter}
  width: 25%;
  padding: 5px;
  margin-right: 5px;
  border: 1px solid #ebebeb;
  border-radius: 3px;
  color: #aaa;
  background: transparent;

  i {
    font-size: 17px;
    margin-right: 2px;
  }

  p {
    margin-left: 3px;
    color: ${props => props.theme.purple};
  }

  &:hover {
    background: #f2f4f8;
  }
`;

const BookingLink = styled.a`
  width: 77%;
  text-decoration: none;
`;

const BookingBtn = styled.button`
  width: 100%;
  padding: 5px;
  color: white;
  border: transparent;
  border-radius: 3px;
  background: ${props => props.theme.purple};

  &:hover {
    background: #351f67;
  }
`;

const SeeMore = styled.button`
  ${flexCenter};
  width: 100%;
  padding: 10px 15px;
  margin-top: 40px;
  border: 1px solid #eaeaea;
  color: #666;
  background: transparent;

  i {
    margin-left: 6px;
  }

  &:hover {
    border: 1px solid #666;
  }
`;

export default Movies;

🍿 상세 페이지

상세 페이지? 영화 정보 페이지?... 어떻게 불러야할지 고민하다 상세 페이지라고 정했다... 무튼 여기선 id 값으로 판별해 동적 라우팅을 적용했다. 그래서 라우터를 movie-info/:id로 작성해주었다 :D

👇 routes.js 일부

코드 공유

import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import { flexBetween, flexCenter } from 'styles/mixin';

//match는 구조분해할당으로 넣어주었다!
function MoviesInfo({ match }) {
  const [movie, setMovie] = useState({});

  useEffect(() => {
  // id값을 매칭해서 결괏값을 동적 라우팅을 통해 나타내준다.
    fetch(`http://3.36.66.16:8000/movie/${match.params.id}`)
      .then(res => res.json())
      .then(res => {
        setMovie(res[0]);
      });
  }, [match.params.id, setMovie]);

  return (
    <>
      <MoviesInfoContainer>
        <MoviesPosterInfo>
          <MoviesBackground img={movie.images?.[0].main_image_url} />
          <MoviesInfoHeader>
            <MoviesInfoHeaderContainer>
              <MoviesHashtag>#돌비시네마 #빵원티켓+ #굿즈패키지</MoviesHashtag>
              <h3>{movie.ko_name}</h3>
              <h4>{movie.eng_name}</h4>

              <HeaderBtn>
                <HeaderLike>
                  <i className="far fa-heart" />
                  <p>{movie.like}</p>
                </HeaderLike>

                <HeaderSharedBtn>
                  <i className="fas fa-share-alt" />
                  <p>공유하기</p>
                </HeaderSharedBtn>
              </HeaderBtn>
            </MoviesInfoHeaderContainer>

            <MoviesContentInfo>
              <MoviesImg
                src={movie.images?.[0].main_image_url}
                alt={movie.ko_name}
              />
              <Link to="/booking">
                <button>예매</button>
              </Link>
            </MoviesContentInfo>
          </MoviesInfoHeader>

          <MoviesInfoContents>
            <MoviesScore>
              <p>실관람 평점</p>
              <MoviesCount>
                <i className="fas fa-trophy" />
                <p>8.7</p>
              </MoviesCount>
            </MoviesScore>

            <MoviesScore>
              <p>누적관객수</p>
              <MoviesCount>
                <i className="fas fa-users" />
                <p>66,038</p>
              </MoviesCount>
            </MoviesScore>
          </MoviesInfoContents>
        </MoviesPosterInfo>
      </MoviesInfoContainer>
      <MoviesReview>
        <p>
          {movie.ko_name}에 대한 <span>1,543</span>개의 이야기가 있어요!
        </p>
        <ReviewContents>
          <MyReview>
            <span>F</span>
            <p>MEGAFOX</p>
          </MyReview>
          <MyStory>
            <p>
              <span>{movie.ko_name}</span> 재미있게 보셨나요? 영화의 어떤 점이
              좋았는지 이야기해주세요.
            </p>

            <MyStoryWrite>
              <i className="far fa-edit" />
              <p>관람평쓰기</p>
            </MyStoryWrite>
          </MyStory>
        </ReviewContents>

        <AnotherUser>
          <AnotherUserId>
            <i className="fas fa-user" />
            <p>Jet**</p>
          </AnotherUserId>

          <AnotherUserComment>
            <p>관람평</p>
            <span>10</span>
            <p>연출</p>

            <AnotherUserLine />

            <AnotherUserWrite>
              <p>웅장한 스케일과 스토리에 잼나게 봤어요</p>
              <AnotherUserMenu>
                <AnotherUserLike>
                  <i className="far fa-thumbs-up" />
                  <p>0</p>
                </AnotherUserLike>
                <i className="fas fa-ellipsis-v" />
              </AnotherUserMenu>
            </AnotherUserWrite>
          </AnotherUserComment>
        </AnotherUser>
      </MoviesReview>
    </>
  );
}

//styled-components
const MoviesInfoContainer = styled.div`
  background: black;
`;

const MoviesPosterInfo = styled.div`
  position: relative;
  max-width: 1100px;
  height: 520px;
  margin: 0 auto;
`;

const MoviesBackground = styled.div`
  position: absolute;
  width: 100%;
  height: 100%;
  margin: 0 auto;
  background: url(${({ img }) => img}) no-repeat;
  background-position: top center;
  background-size: 80% auto;
  filter: blur(5px);
  opacity: 0.4;
`;

const MoviesInfoHeaderContainer = styled.div`
  display: flex;
  flex-direction: column;
`;

const MoviesInfoHeader = styled.div`
  position: relative;
  display: flex;
  justify-content: space-between;
  padding: 55px 45px 0 45px;
  color: white;

  h3 {
    margin: 15px 0 10px 0;
    font-size: 46px;
  }
`;

const MoviesHashtag = styled.span`
  color: #ccc;
  font-size: 15px;
`;

const HeaderBtn = styled.div`
  display: flex;
  margin-top: 15px;
`;

const HeaderLike = styled.button`
  display: flex;
  align-items: center;
  padding: 6px 15px;
  margin-right: 6px;
  border: 1px solid #706f72;
  border-radius: 4px;
  color: white;
  background: transparent;

  i {
    margin-right: 4px;
  }
`;

const HeaderSharedBtn = styled.button`
  display: flex;
  align-items: center;
  border: 1px solid #706f72;
  border-radius: 4px;
  color: white;
  background: transparent;

  i {
    margin-right: 4px;
  }
`;

const MoviesInfoContents = styled.div`
  display: flex;
  padding: 0 45px;
`;

const MoviesScore = styled.div`
  position: relative;
  display: flex;
  flex-direction: column;
  margin-left: 35px;

  p {
    color: #ccc;
    font-size: 14px;
    padding-bottom: 15px;
  }

  &:first-child {
    margin-left: 0;
  }

  i {
    color: white;
  }
`;

const MoviesCount = styled.div`
  display: flex;
  i {
    margin-right: 6px;
    font-size: 20px;
  }

  p {
    font-size: 32px;
    color: white;
  }
`;

const MoviesContentInfo = styled.div`
  display: flex;
  flex-direction: column;
  width: 260px;
  height: 374px;

  button {
    width: 100%;
    margin-top: 8px;
    padding: 10px;
    font-size: 18px;
    border-radius: 5px;
    border: transparent;
    color: white;
    font-weight: bold;
    background: #329eb1;
  }
`;

const MoviesImg = styled.img`
  border-radius: 10px;
`;

const MoviesReview = styled.div`
  max-width: 1100px;
  margin: 0 auto;
  padding-top: 30px;

  p {
    font-size: 22px;
    color: ${props => props.theme.purple};

    span {
      color: #01738b;
    }
  }
`;

const ReviewContents = styled.div`
  display: flex;
  margin-top: 15px;
`;

const MyReview = styled.div`
  display: flex;
  flex-direction: column;
  width: 6%;

  span {
    ${flexCenter}
    width: 50px;
    height: 50px;
    margin: 0 auto 10px auto;
    line-height: 3;
    border-radius: 50%;
    text-align: center;
    font-weight: bold;
    color: white;
    background: rgb(107, 74, 190);
    background: linear-gradient(
      127deg,
      rgba(107, 74, 190, 1) 0%,
      rgba(80, 51, 150, 1) 100%
    );
  }

  p {
    font-size: 14px;
    color: #444;
  }
`;

const MyStory = styled.div`
  ${flexBetween}
  width: 93%;
  padding: 25px;
  margin-left: 25px;
  border: 1px solid #eaeaea;
  border-radius: 0 10px 10px 10px;

  p {
    color: #666;
    font-size: 15px;
  }
`;

const MyStoryWrite = styled.div`
  display: flex;
  color: #666;

  i {
    margin-right: 5px;
    color: #acabb0;
  }
`;

const AnotherUser = styled.div`
  display: flex;
  margin-top: 20px;
`;

const AnotherUserId = styled.div`
  display: flex;
  justify-content: center;
  flex-direction: column;
  width: 6%;

  i {
    margin-bottom: 5px;
    font-size: 30px;
    text-align: center;
    color: #b1b1b1;
  }

  p {
    font-size: 14px;
    color: #444;
    text-align: center;
  }
`;

const AnotherUserComment = styled.div`
  ${flexBetween};
  width: 95%;
  margin-left: 23px;
  padding: 25px;
  background: #f8f8fa;

  p {
    font-size: 15px;
  }

  span {
    font-size: 36px;
    color: ${props => props.theme.purple};
  }
`;

const AnotherUserLine = styled.span`
  width: 1px;
  height: 50px;
  border-left: 1px solid #d3d3d3;
`;

const AnotherUserWrite = styled.div`
  ${flexBetween};
  width: 80%;

  p {
    color: #666;
  }
`;

const AnotherUserMenu = styled.div`
  display: flex;
  align-items: center;
  color: #444;
`;

const AnotherUserLike = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-right: 25px;
  color: #aeaeae;

  i {
    font-size: 18px;
    margin-bottom: 5px;
  }
  p {
    color: #666;
  }
`;

export default MoviesInfo;

아쉬운 점

첫 번째로는, 1차때와 같이 트렐로의 활용이다..ㅜㅜ 아무래도 시간이 촉박해져서 점점 트렐로를 활용을 못했다는 변명아닌 변명과 기능 구현에 열심히 해야하기 때문이란 변명이다...🙄

두 번째로는, 만들어주신 API 활용을 못했다는게 아쉽습니다 ㅠㅜ,, API는 준비를 다 해주셨는데 많이 활용을 못했다는 미안함+아쉬움이 있다..😥


좋았던 점

역시나 우리 팀원들이다 !!! 다시 한번 같이한 빈쓰~ 영찬쓰~ 단쓰~ 도련님~ 너무 너무 좋았다 😍 서로 모르는건 도와주고! 도움받고...(찬영,단님이 일방적으로 도와주신거지만..😅ㅎ) 만약 2주 프로젝트가 아닌 한달이였어도 끝까지 서로 도와주고 화목하게 끝났을것같은 느낌이 들 정도로 너무 팀원분들이 좋았다 :)


마지막으로..

  • 열정적으로 강의해주신 연~욱!님 다시 한번 감사드립니다..흐흐 (명강의를 제가 바보처럼 못 알아들는것도 아쉬운 점이라 할 수 있겠네요..😢)
  • 어떤 방법으로 접근을 해서 문제를 풀어가야하는지 알려주신 종택님 감사합니다 🥳
  • css가 싫다고 하시면서 어떻게든 같이 해결하면서 가르쳐주시려고 노력하는 승현님 넘 감사해요 😊
  • 짜증 제로 웃음 100%였던 우리 팀원 너무 감사합니다 저때문에 힘드셨을텐데... 너무 감사해요 😭

2차 프로젝트에는 못해본 기능들을 추가하느냐고 많이 멘붕이였고, 처음 써보는 function component와 styled-components로 멘붕이였지만,, 그래도 너무 재밌고 프로젝트가 끝났다는것에 대해 많이 아쉬운 감정, 이렇게 내가 좋은 사람들로 구성된 프로젝트를 다시 할 수 있을까? 하는 불안함도 있지만 다양한 경험을 통해 다양한 시야를 가질 수 있을거란 기대감으로 다시 열심히 공부해야겠다 생각한다. 또한 나 자신 잘할거라 믿고, 그동안도 고생했다라는 스스로를 위하는 말을 해야겠다고도 다짐했다.😌

profile
사람이 좋은 인간 리트리버 신혜리입니다🐶

0개의 댓글