포스트

WeCode 2차 프로젝트 회고 - Soomgo 웹사이트 클론

React Hooks, Styled-Components, Kakao API를 활용한 두 번째 팀 프로젝트. 1차 프로젝트에서의 학습을 바탕으로 더 발전된 기술 스택과 협업 경험을 쌓았습니다.

WeCode 2차 프로젝트 회고 - Soomgo 웹사이트 클론

WeCode 부트캠프 2차 프로젝트에서 1차 프로젝트의 경험을 바탕으로 더 발전된 기술 스택을 활용하여 진행한 프로젝트 회고입니다.

프로젝트 소개

‘숨은 고수’를 찾아 일반인과 이어주는 생활서비스 전문가 매칭 플랫폼 Soomgo(숨고) 웹사이트를 클론한 프로젝트입니다.

프로젝트 개요

  • 프로젝트 기간: 2020년 7월 6일 ~ 7월 17일 (총 2주)
  • 팀 구성: Front-End 3명, Back-End 3명
  • 프로젝트 저장소

프로젝트 데모

프로젝트 시연 영상 보기

기술 스택

Front-End

카테고리기술/도구
CoreHTML, React.js, JavaScript (ES6)
React 패턴Class 컴포넌트, Functional 컴포넌트 (Hooks)
StylingSASS, Styled-Components
HTTP ClientAxios
External APIsKakao Map API, Kakao Login API
CollaborationGit/GitHub (git rebase), Slack, Trello

1차 프로젝트와의 차이점: React Hooks, Styled-Components, Axios, 외부 API 연동 추가

Back-End

카테고리기술/도구
LanguagePython
FrameworkDjango
DatabaseMySQL
SecurityCORS 헤더 설정
DevOpsAWS, Docker
API DesignRESTful API
Version ControlGit, GitHub, Git Rebase

담당 페이지 및 구현 기능

내가 구현한 기능 (Footer/고수 찾기/고수 디테일)

  • 함수형 컴포넌트 + Styled-Components
    • 1차 프로젝트에서 사용하지 못한 기술 스택 적용
    • CSS-in-JS 패턴으로 스타일 관리

2. 고수 찾기 페이지

  • Class 컴포넌트 + SASS
    • 전문가 목록 표시
    • Fetch API를 통한 Back-End 통신
    • Map 함수를 활용한 데이터 렌더링
    • Modal 창 구현 (필터 옵션 선택)
  • Mock Data 활용
    • Back-End에서 제공하지 못한 일부 데이터를 Mock Data로 대체
    • 프론트엔드 개발 독립성 확보

3. 고수 디테일 페이지

  • Class 컴포넌트 + SASS
    • 전문가 상세 정보 표시
    • 리뷰 데이터 렌더링
    • 서버 통신과 Mock Data 혼합 사용

팀원들이 구현한 기능

로그인/회원가입/고수 프로필

인증 및 소셜 로그인

  • 정규식을 활용한 이메일 형식 검증
  • 이메일 및 비밀번호 중복 확인
  • Kakao 소셜 로그인 구현
  • JWT 토큰을 localStorage에 저장
  • React Hooks와 Styled-Components 사용

고수 프로필 관리

  • 로컬에서 프로필 이미지 업로드
  • 고수 상세 정보 입력 및 저장
  • Daum Postcode API로 우편번호 검색
  • Kakao Map API 연동
    • Geocoder로 주소→좌표 변환
    • Circle로 활동 지역 표시
    • useEffect Hook으로 지도 초기화

메인 카테고리/설문 페이지

카테고리 페이지

  • Query Parameters를 활용한 카테고리별 페이지네이션
  • 점수 기반 별점 표시 함수
  • Map 함수로 인기 서비스/전체 서비스 렌더링
  • Class 컴포넌트와 Fetch API 사용

설문 페이지

  • Switch-case문으로 질문별 설문 Form 구현
  • React DatePicker로 날짜 선택
  • React Hooks와 Styled-Components
  • Axios로 Back-End API 통신

1차 프로젝트 대비 성장한 부분

기술적 성장

1. 새로운 기술 스택 도입

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1차: Class 컴포넌트만 사용
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  // ...
}

// 2차: Hooks를 활용한 함수형 컴포넌트
import { useState, useEffect } from 'react';

const MyComponent = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    // 사이드 이펙트 처리
  }, []);
  // ...
};

2. Styled-Components 도입

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1차: 별도 SASS 파일
import './Footer.scss';

const Footer = () => <footer className="footer">{/* 내용 */}</footer>;

// 2차: CSS-in-JS 패턴
import styled from 'styled-components';

const FooterContainer = styled.footer`
  background-color: #f8f9fa;
  padding: 2rem;

  @media (max-width: 768px) {
    padding: 1rem;
  }
`;

const Footer = () => <FooterContainer>{/* 내용 */}</FooterContainer>;

장점:

  • 컴포넌트와 스타일의 응집도 향상
  • Props 기반 동적 스타일링
  • CSS 클래스명 충돌 방지
  • TypeScript 지원 우수

3. Axios vs Fetch API 비교

1
2
3
4
5
6
7
8
9
10
11
12
// Fetch API (1차 프로젝트)
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => setData(data))
  .catch(error => console.error(error));

// Axios (2차 프로젝트)
import axios from 'axios';

axios.get('https://api.example.com/data')
  .then(response => setData(response.data))
  .catch(error => console.error(error));

Axios의 장점:

  • 자동 JSON 변환
  • 요청/응답 인터셉터
  • 취소 가능한 요청
  • 더 나은 에러 핸들링

협업 능력 향상

팀원 지원

1차 프로젝트에서는 내 작업만으로도 벅찼지만, 2차에서는 여유를 가지고 팀원을 도울 수 있었습니다:

  • 로그인/회원가입 구현 팀원에게 정규식 표현 도움
  • 경고 문구 UI/UX 개선 제안
  • Modal 창 구현 가이드 제공

Git Rebase 경험

1
2
3
4
5
6
# Merge vs Rebase의 차이 학습
# Merge: 병합 커밋 생성
git merge feature-branch

# Rebase: 커밋 히스토리를 선형으로 정리
git rebase main

💡 학습 노트: Git Rebase를 사용했지만 완전히 이해하지 못한 채 사용했습니다. 추후 Git 전반을 다시 학습할 필요성을 느꼈습니다.

기억하고 싶은 코드

1. Mock Data 생성 (처음 시도)

Back-End에서 시간 내에 제공하지 못한 리뷰 데이터를 Mock Data로 생성했습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// reviewMockData.js
export const REVIEW_MOCK_DATA = [
  {
    id: 1,
    user_name: "김철수",
    rating: 5,
    content: "정말 친절하고 꼼꼼하게 작업해주셨습니다.",
    created_at: "2020-07-10",
    service_images: [
      "https://example.com/review1.jpg"
    ]
  },
  {
    id: 2,
    user_name: "이영희",
    rating: 4,
    content: "가격 대비 만족스러운 서비스였어요.",
    created_at: "2020-07-08",
    service_images: []
  },
  // ... 더 많은 리뷰 데이터
];

배운 점:

  • Front-End 개발이 Back-End API에 의존하지 않도록 Mock Data 활용
  • 실제 데이터 구조를 미리 설계하여 통합 시간 단축
  • 프론트엔드와 백엔드의 병렬 개발 가능

2. Map 함수를 활용한 데이터 렌더링

서버에서 받은 데이터를 Map 함수로 효율적으로 렌더링했습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// ProList.js (고수 찾기 페이지)
class ProList extends Component {
  state = {
    proList: [],
    isModalOpen: false
  };

  componentDidMount() {
    fetch('https://api.example.com/professionals')
      .then(res => res.json())
      .then(data => {
        this.setState({ proList: data.results });
      });
  }

  toggleModal = () => {
    this.setState(prevState => ({
      isModalOpen: !prevState.isModalOpen
    }));
  };

  render() {
    const { proList, isModalOpen } = this.state;

    return (
      <div className="pro-list">
        <button onClick={this.toggleModal}>
          필터
        </button>

        {/* Modal 컴포넌트 */}
        {isModalOpen && (
          <FilterModal onClose={this.toggleModal} />
        )}

        {/* 전문가 목록 렌더링 */}
        <div className="pro-grid">
          {proList.map(pro => (
            <ProCard
              key={pro.id}
              name={pro.name}
              rating={pro.rating}
              reviewCount={pro.review_count}
              image={pro.profile_image}
              onClick={() => this.handleProClick(pro.id)}
            />
          ))}
        </div>
      </div>
    );
  }
}

신세계였던 경험:

“Back-End와 통신하며 데이터를 Map으로 저 코드 하나만으로 엄청난 양의 데이터를 페이지에 반복해서 구현된다는 것이 신세계였다.”

처음 Map 함수의 강력함을 체감한 순간이었고, 지금도 사용할 때마다 그 편리함에 감탄합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Footer.js
import React from 'react';
import styled from 'styled-components';

const FooterContainer = styled.footer`
  background-color: #f8f9fa;
  padding: 3rem 1rem;
  margin-top: 4rem;
`;

const FooterContent = styled.div`
  max-width: 1200px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2rem;

  @media (max-width: 768px) {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }
`;

const FooterSection = styled.div`
  h4 {
    font-size: 1rem;
    font-weight: 600;
    margin-bottom: 1rem;
    color: #333;
  }

  ul {
    list-style: none;
    padding: 0;
  }

  li {
    margin-bottom: 0.5rem;
    color: #666;
    font-size: 0.9rem;
    cursor: pointer;

    &:hover {
      color: #00c471;
    }
  }
`;

const Footer = () => {
  return (
    <FooterContainer>
      <FooterContent>
        <FooterSection>
          <h4>숨고 소개</h4>
          <ul>
            <li>회사 소개</li>
            <li>채용 안내</li>
            <li>팀 블로그</li>
          </ul>
        </FooterSection>

        <FooterSection>
          <h4>고객 안내</h4>
          <ul>
            <li>이용 안내</li>
            <li>안전 정책</li>
            <li>예상 비용</li>
          </ul>
        </FooterSection>

        <FooterSection>
          <h4>고수 안내</h4>
          <ul>
            <li>고수 가이드</li>
            <li>고수 가입</li>
          </ul>
        </FooterSection>

        <FooterSection>
          <h4>고객 지원</h4>
          <ul>
            <li>공지사항</li>
            <li>자주 묻는 질문</li>
          </ul>
        </FooterSection>
      </FooterContent>
    </FooterContainer>
  );
};

export default Footer;

프로젝트를 통해 배운 점

긍정적인 경험

1. 새로운 기술 도전

1차 프로젝트에서 사용하지 못한 기술들을 적극적으로 도입했습니다:

  • React Hooks (useState, useEffect)
  • Styled-Components
  • Axios
  • Kakao Map API / Kakao Login API

2. 팀원 배려

프로젝트 완성도도 중요하지만, 학습을 우선순위로 두자는 팀원들의 합의가 있었습니다. 덕분에 부담 없이 새로운 기술을 시도할 수 있었습니다.

3. 협업 능력 향상

1차 때는 내 작업에만 집중했다면, 2차에서는 여유를 갖고 팀원들을 도울 수 있었습니다.

4. Mock Data 경험

Back-End가 제공하지 못한 데이터를 Mock Data로 대체하여 독립적으로 작업할 수 있었습니다.

5. Map 함수의 강력함

서버 데이터를 Map 함수 하나로 효율적으로 렌더링하는 경험이 인상적이었습니다.

개선이 필요했던 부분

1. 프로젝트 관리

  • 트렐로 관리 미흡: 후반부에 작업 현황 업데이트가 소홀해짐

2. 코드 컨벤션

  • 컨벤션을 정했지만 네스팅을 고려하지 않아 코드 통합 시 어려움
  • 전체적으로 코드가 깔끔하지 못했음
1
2
3
4
5
6
7
8
9
10
11
// 문제가 있었던 구조
<div className="container">
  <div className="wrapper">
    <div className="content">
      <div className="item">{/* 내용 */}</div>
    </div>
  </div>
</div>

// 개선 필요: 과도한 네스팅으로 CSS 선택자 복잡도 증가
// .container .wrapper .content .item { 스타일 }

3. 기술 이해 부족

  • Git Rebase: 사용했지만 정확한 동작 원리를 이해하지 못함
  • Filter 함수: 시간 부족으로 적용하지 못함

4. 발표 준비

  • Front-End 발표자였지만 긴장으로 팀원 기능을 제대로 설명하지 못함
  • 팀원에게 미안한 감정이 남음

5. 시간 관리

  • Class형 컴포넌트 구현에 시간을 많이 써서
  • Styled-Components를 충분히 활용하지 못함

다음 프로젝트를 위한 개선 방안

1. 코드 품질

1
2
3
4
5
6
7
8
✅ 네스팅을 고려한 코드 컨벤션 수립
  - 최대 3단계 이하 네스팅
  - BEM 방법론 또는 CSS Modules 고려
  - Styled-Components에서 과도한 중첩 지양

✅ 코드 리뷰 문화 정착
  - PR 단위로 코드 리뷰 진행
  - 서로의 코드에서 배우기

2. 기술 학습

1
2
3
4
5
6
7
8
✅ Git 심화 학습
  - Rebase, Cherry-pick, Reset 등 완전히 이해
  - Conflict 해결 패턴 학습
  - Git Flow 전략 익히기

✅ JavaScript 배열 메서드 마스터
  - Map, Filter, Reduce 조합
  - 성능 최적화 고려

3. 협업 개선

1
2
3
4
5
6
7
✅ 트렐로 꾸준한 업데이트
  - 매일 작업 시작/종료 시 업데이트
  - 블로커 즉시 공유

✅ 발표 준비
  - 하루 전 거울 보며 연습
  - 팀원 기능 깊이 이해하기

4. 일정 관리

1
2
3
4
5
6
7
✅ 우선순위 기반 작업
  - 핵심 기능 먼저 구현
  - 여유 시간 확보 후 도전적 기능 시도

✅ 데드라인 준수
  - 계획한 날짜에 맞춰 구현 완료
  - 필요시 야근도 감수

5. 팀워크

1
2
3
✅ 리팩토링 시간 확보
  - 프로젝트 종료 후 팀원과 함께 코드 개선
  - 베스트 프랙티스 공유

마치며

1차 프로젝트에 이어 2차 프로젝트에서도 훌륭한 팀원들과 함께할 수 있었습니다. 프로젝트 완성도보다 학습을 우선시하자는 팀의 합의 덕분에 조급함 없이 새로운 기술들을 시도해볼 수 있었습니다.

핵심 성과

  • ✅ React Hooks, Styled-Components 등 새로운 기술 습득
  • ✅ Kakao Map/Login API 연동 경험
  • ✅ Mock Data로 독립적인 프론트엔드 개발
  • ✅ 1차 대비 향상된 협업 능력

다짐

“항상 쓴 것을 계속 쓰는 개발자가 아닌, 새로운 기술에 겁내지 않고 도전하는 개발자가 되자.”

이 프로젝트에서 배운 도전 정신협업의 가치를 잊지 않고, 계속해서 성장하는 개발자가 되겠습니다.

팀원들과 꼭 시간을 맞춰 리팩토링을 진행하고 싶습니다!

참고 자료

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.