본문 바로가기
🎨 Front-end/React.js

React.js 쇼핑몰 만들기 | #1. 프로젝트 세팅, Styled Component 활용해보기

by Jay Anderson 2025. 7. 27.

 

한동안 Vue.js나 Nuxt.js만 써 오다가, 최근 들어 리액트 프로젝트에 투입되면서

다시금 초심으로 돌아가 React.js를 공부해보자 라는 생각이 들었다. (첫 프론트엔드 입문을 React.js로 했었음.)

그래서, 몇 년 전에 해본거긴 한데 기억을 더듬어가며 개인연습 겸 겸사겸사 포트폴리오 겸 쇼핑몰 만들기를 해보려고 함.

이왕 하는김에 지금 진행중인 프로젝트와 비슷한 레이아웃 구성으로 가보려고 한다. (창의성이 오래전에 고갈되었는지... 독창적인게 생각 안난다 ㅠ)

 

🛠️ 사용 기술 스택 정리

💻 프론트엔드

- React : 컴포넌트 기반 UI 개발

- Vite : 빠른 빌드 및 핫 리로딩을 제공

- TypeScript : 정적 타입 기반 개발로 안정성 향상

 

🎨 스타일링

- Emotion (@emotion/react, @emotion/styled) : CSS-in-JS 방식의 스타일링 라이브러리. 컴포넌트 스타일링하기 편함.

 

🔐 백엔드 (예정 - Firebase / 진행중 변동될 수도 있음)

- Firebase Authentication : 이메일/비밀번호 로그인, 구글/깃허브 등 소셜 로그인 지원

- Firebase Firestore : NoSQL방식의 실시간 데이터베이스 (유저 정보 저장에 적합)

- Firebase Security Rules : 인증된 사용자만 데이터 접근 가능하도록 보안규칙 설정

- Firebase Hosting : 정적파일 배포

- Firebase SDK : 클라이언트에서 Firebase 서비스 호출 (로그인/데이터 저장 등)

 

예전엔 Firebase로 안했던것같은데 이번엔 한번 써보려고 함.

 

✨ Firebase를 쓰면 좋은 이유

- 직접 서버를 만들 필요 없어 개발이 빨라짐.

- 인증, DB, 호스팅, 파일 업로드까지 다 지원됨.

- 무료 플랜 사용가능

- 프론트 중심 개발자 입장에선 사용하기 아무래도 편함.

 

복잡하게 썼지만, 쉽게 말해 React + TS + Emotion 으로 쇼핑몰을 만들어보겠다는 뜻임.

 

### 대충 구상해 본 디렉토리 구성

shopmall/
├── public/               # 정적 파일 (import필요없이 사용할 favicon, robots.txt이나 mp4영상파일 등)
├── src/
│   ├── assets/           # 이미지, 폰트 등 번들링 및 import해서 사용할 리소스들
│   ├── components/       # 재사용 가능한 UI 컴포넌트들 (버튼, 카드, 모달 등)
│   ├── features/         # 도메인 별 컴포넌트 묶음 (상품, 장바구니, 회원 등)
│   │    ├── product/
│   │    │     ├── ProductCard.tsx
│   │    │     ├── ProductList.tsx
│   │    │     └── productSlice.ts (상태관리 시)
│   │    ├── cart/
│   │    └── user/
│   ├── hooks/            # 커스텀 훅들
│   ├── layouts/          # 페이지 레이아웃 컴포넌트 (헤더, 푸터 등 포함)
│   ├── pages/            # 라우팅용 페이지 컴포넌트 (메인, 상세, 로그인 등)
│   ├── styles/           # Emotion 글로벌 스타일, 테마 등
│   ├── utils/            # 유틸 함수, API 함수, 상수 등
│   ├── App.tsx
│   ├── main.tsx
├── package.json
├── tsconfig.json
├── vite.config.ts
└── README.md

 

간단히 스케치해 본 디렉토리는 다음과 같음. 스케치일 뿐이라 빼먹은것도 있을수도 있고 작업하면서 달라질 수도 있음.

styles폴더 안에 스타일 tsx들을 따로 모아두는게 더 좋을지, 각 페이지 tsx와 같은 루트에 나란히 두는게 나을지는 모르겠는데,

일단 프로젝트 기초 세팅 하면서 손 잡히는대로 해 보겠음.

이 포스팅을 마칠 즈음에 디렉토리 구성을 캡쳐해 올리겠음.

React 오랜만에 하려니 막막하다... ㅅㅂ 어떻게 하더라 ㅋㅋ

 

 

(일단 Node.js정도는 다 설치했다고 생각하고 시작함. 모르겠으면 터미널에 node -v 쳐봐라.)

 

 

(1) React 프로젝트 생성하기

Vite도 같이 사용해보겠다고 했으니까, create-react-app 말고 다른걸 입력해보겠다.

npm create vite@latest shopmall -- --template react

shopmall은 내가 스스로 정한 내 프로젝트 이름임. 저기에 shopmall말고 my-shop, myprj 등 뭐 다양하게 맘대로 입력해도 됨.

 

 

 

엥? SWC는 또 뭐임

진짜 프론트 단은 자고 일어나면 뭐가 바뀌어 있다 ㅠㅠ 맨날 공부해야 함

 

 

⚙️ TypeScript  vs  TypeScript + SWC

항목 설명
TypeScript 일반적인 TypeScript 설정 (Vite + TS)
TypeScript + SWC TypeScript를 SWC(빠른 컴파일러) 로 변환

 

검색해 보니 이런 차이가 있더라

 

🧠 SWC란?

- Speedy Web Compiler의 약자. Rust로 만든 초고속 트랜스파일러로, 기존 Babel보다 훨씬 빠름

- Facebook의 Babel vs SWC = JavaScript로 된 것 vs Rust로 된 것.

- 성능이 최대 20배 빠르다는 말도 있고...?

 

뭐 일단 SWC란 녀석은 Vite와 궁합이 매우 좋아보인다. 낯설지만, Pick 안 할 이유는 없다고 판단했다. TypeScript + SWC로 ㄱㄱ

 

 

프로젝트 생성했으니, 초심자처럼 바로 npm i 때리지 말고 cd로 경로 바꿔주는거 잊지말자.

cd shopmall   # 생성한 프로젝트 폴더로 이동
npm install   # npm 설치

 

 

몇초 안걸려서 인스톨은 다 끝남. run dev로 돌려보자.

npm run dev

 

 

localhost정보 뜨는거 복사해 크롬에 붙여넣어 들어가 보면


성공적으로 기본 React 템플릿 페이지가 뜨고 있음.

 

 

※ 참고로, TailwindCSS 설치할거냐고 뜨는데 난 안설치함. 어차피 Emotion으로 할거기도하고, 무엇보다 언제부턴가 Tailwind 설치하면 자꾸 오류 뜨는데 왜 이러는지 모르겠음. 설치는 됐는데 node_modules 안에도 존재하지 않아서 못불러오고... 암튼 Tailwind는 패스하겠음.

 

 

이제 Emotion을 설치.

npm install @emotion/react @emotion/styled

 

 

TS환경이니까 타입패키지도 같이 설치하겠다.

npm install -D @types/react

 

 

 

이제 쇼핑몰의 구성을 위해, 카테고리나 컴포넌트들의 구조를 조금 디테일하게 스케치 해 보겠음.

src/
├── components/
│    ├── KeyVisual.tsx            # 메인 키비쥬얼 (영상 혹은 슬라이더)
│    ├── QuickMenu.tsx            # 퀵버튼 메뉴 (아이콘 + 텍스트)
│    ├── BannerSection.tsx        # 프로모션 배너 영역
│    ├── ProductList.tsx          # 상품 리스트 공통 컴포넌트
│    ├── ProductCard.tsx          # 상품 카드 UI
│    └── SpecialPromotion.tsx     # 기획전 영역
├── pages/
│
└── main.tsx                 # 메인 페이지, 위 컴포넌트 조합

 

 

 

난 main.tsx를 진짜 메인페이지처럼 사용하고 싶다.

그렇다면 React 앱 진입점 포인트는 어디에 만들 것인가? ReactDOM.render 담당을 어디에 하면 좋겠느냔 말이다.

App.tsx에다가 하는건 좀 아닌거같다...

 

(2) React 쇼핑몰 구성을 위한 기초 작업 하기

 

main.tsx 옆에 index.tsx를 하나 만들자.

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
)

 

 

App.tsx는 이렇게 바꿔주자.

import Main from './main'
import GlobalStyles from '@/styles/GlobalStyles';

function App() {
  return (
    <>
      <GlobalStyles />
      <Main />
    </>
  )
}

export default App

 

 

src/styles 폴더 안에 GlobalStyles.tsx도 만들어주자.

/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react';

const GlobalStyles = () => (
  <Global
    styles={css`
      html, body {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
        font-family: 'Pretendard', sans-serif; /* 원하는 폰트로 */
        background-color: #fff;
        color: #111;
      }

      *, *::before, *::after {
        box-sizing: inherit;
      }

      body {
        overflow-x: hidden;
      }
    `}
  />
);

export default GlobalStyles;

 

 

최상위 루트에 index.html이 있다. 뷰포트, 메타태그 등 설정 가능한 곳임.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Shopmall</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/index.tsx"></script>
  </body>
</html>

이렇게 수정해주자.

 

 

다음으로 해야 할 일은, 이거 정말 중요한건데, 절대경로 alias설정이다.

import를 하거나 어떤 경로를 지정할 때, @ 을 사용해서 절대경로로 쓰면 편하고 이식성도 좋아지고 개발협업시 매우 좋아 필수임.

 

tsconfig.app.json 을 찾아 열어주고, 거기에 alias설정을 추가하겠음.

"compilerOptions" 항목을 찾아보면 뭐라고 길게 적혀있는데, 맨 아래에다가 다음과 같이 두개 항목을 추가.

{
  "compilerOptions": {
    "baseUrl": "src",
    "paths": {
      "@/*": ["*"]
    }
  },
}

이렇게 위의 부분을 넣어주면 됨. "compilerOptions" 내부 baseUrl과 paths를 맞게 넣었나 다시한번 잘 확인할 것.

baseUrl이 "src"인지 확인하고, paths에 "@/*": ["*"] 라고 돼있으면 성공적.

 

 

아, 그리고 스크롤 이벤트인 AOS도 세팅하려고 한다.

먼저 AOS를 설치해주자.

npm install aos

 

AOS의 사용 예시는 다음과 같다.

import React, { useEffect } from 'react';
import AOS from 'aos';

const Main = () => {
  useEffect(() => {
    AOS.init({
      duration: 800,  // 애니메이션 시간 (ms)
      easing: 'ease-in-out',
      once: true,     // 스크롤시 한 번만 애니메이션 실행
    });
  }, []);

  return (
    <div>
      <section data-aos="fade-up">
        {/* 신상품 영역 */}
      </section>
      <section data-aos="fade-left">
        {/* 인기상품 영역 */}
      </section>
      {/* 기타 섹션 */}
    </div>
  );
};

export default Main;

저렇게 data-aos라는걸 넣으면 되는데, 현재 진행중인 프로젝트에서는 이렇게 안쓰고 있더라.

AOS관련 props만 받아서 쉽게 재사용할 수 있도록 만들어져 있어서 나도 그렇게 해보려고 함.

 

일단 AOS 기본세팅 (main.tsx에 먼저 넣자.)

useEffect(() => {
  AOS.init({
    duration: 800,
    easing: 'ease-in-out',
    once: true,
  })
}, [])

 

/src/ 안에 utils 라는 폴더를 만들고, 그 안에 aos.ts 를 만들었음.

/src/utils/aos.ts

이곳에선 aos 타입 정의 및 props 자동 할당을 위한 헬퍼함수를 만들것임.

// AOS 애니메이션용 공통 props 타입
export type AOSProps = {
  'data-aos'?: string
  'data-aos-duration'?: number
  'data-aos-easing'?: string
  'data-aos-once'?: boolean
}

// fade-up 고정값을 기본으로 하는 props 객체
export const aos: AOSProps = {
  'data-aos': 'fade-up',
  'data-aos-duration': 800,
  'data-aos-easing': 'ease-in-out',
  'data-aos-once': true,
}

 

 

참고로, AOS설치 후 오류가 뜨더라.

"message": "Could not find a declaration file for module 'aos'.

TypeScript가 aos모듈에 대한 타입선언파일 (.d.ts)를 못찾아서 생기는 문제임.

해결방법은 아래와 같음.

npm i --save-dev @types/aos

이렇게 설치해주면 오류는 끝남.

 

 

이제 AOS적용도 시켜볼 겸, 빠르게 메인 레이아웃을 잡아보자.

어디까지나 임시이고, 향후 서서히 바꿔나갈 것임.

메인을 잡으면서 키비쥬얼 영역은 바로 동시에 컴포넌트로 분리해뒀는데 소스 보여주겠음.

/** @jsxImportSource @emotion/react */
import { useEffect } from 'react'

import KeyVisual from '@/components/KV/KeyVisual'

import AOS from 'aos'
import 'aos/dist/aos.css'

import * as S from './main.style'
import { aos } from '@/utils/aos'
import { SampleProducts } from '@/data/SampleProducts'


// --- 메인 컴포넌트 ---
const Main = () => {
  useEffect(() => {
    AOS.init({
      offset: 100,
      duration: 400,
      easing: 'ease-in-out',
      once: true,
    })
  }, [])


  return (
    <S.MainContainer>
      <S.KV>
        <KeyVisual />
      </S.KV>

      <S.QuickMenuWrapper>
        <S.QuickMenuButton>신상품</S.QuickMenuButton>
        <S.QuickMenuButton>인기상품</S.QuickMenuButton>
        <S.QuickMenuButton>특가상품</S.QuickMenuButton>
        <S.QuickMenuButton>기획전</S.QuickMenuButton>
      </S.QuickMenuWrapper>

      <S.BannerArea {...aos}>
        프로모션 배너 영역
      </S.BannerArea>

      <S.ProductSection {...aos}>
        <S.SectionTitle>신상품</S.SectionTitle>
        <S.ProductList>
          {SampleProducts.map((product) => (
            <S.ProductCard key={product.id}>
              <h3>{product.name}</h3>
              <p>{product.price}</p>
            </S.ProductCard>
          ))}
        </S.ProductList>
      </S.ProductSection>

      <S.ProductSection {...aos}>
        <S.SectionTitle>인기상품</S.SectionTitle>
        <S.ProductList>
          {SampleProducts.map((product) => (
            <S.ProductCard key={product.id + 100}>
              <h3>{product.name}</h3>
              <p>{product.price}</p>
            </S.ProductCard>
          ))}
        </S.ProductList>
      </S.ProductSection>

      <S.ProductSection {...aos}>
        <S.SectionTitle>특가상품</S.SectionTitle>
        <S.ProductList>
          {SampleProducts.map((product) => (
            <S.ProductCard key={product.id + 200}>
              <h3>{product.name}</h3>
              <p>{product.price}</p>
            </S.ProductCard>
          ))}
        </S.ProductList>
      </S.ProductSection>

      <S.ProductSection {...aos}>
        <S.SectionTitle>기획전</S.SectionTitle>
        <S.BannerArea>
          {/* 기획전 내용 또는 슬라이더 들어갈 자리 */}
          기획전 배너 영역
        </S.BannerArea>
      </S.ProductSection>
    </S.MainContainer>
  )
}

export default Main

 

 

메인 키비쥬얼을 넣어보자.

영상을 넣을 생각인데, 영상 소스는 그냥 pixabay에서 아무 무료 샘플영상 짤막한거로 받아옴.

 

/src/components/KV/KeyVisual.tsx

/** @jsxImportSource @emotion/react */
import * as S from './KeyVisual.style'

const KeyVisual = () => {
  return (
    <S.KVContainer>
      <video
        autoPlay
        muted
        loop
        playsInline
        preload="auto"
      >
        <source src="/kv/kv_sample_01.mp4" type="video/mp4" />
      </video>
      <S.OverlayText>메인 KV영상</S.OverlayText>
    </S.KVContainer>
  )
}

export default KeyVisual

 

 

KeyVisual.style.tsx

import styled from '@emotion/styled'

export const KVContainer = styled.div`
  width: 100%;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;

  video {
    filter: brightness(0.5);
  }
`

export const OverlayText = styled.div`
  position: absolute;
  z-index: 10;
  color: #fff;
  font-size: 2.5rem;
  font-weight: 700;
  text-align: center;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  user-select: none;
  opacity: 0.75;
`

 

 

혹시 스타일 컴포넌트가 생소한 사람은 이 소스를 보면 이해가 갈 것임.

<div>, <p> 같은 일반적인 태그를 스타일로 감싸는 느낌이라고 해야 하나...? 암튼 이렇게 쓰는것임. 어려울건 없음.

 

 

 

 

결국, 여차저차 후딱 레이아웃만 잡아본다고 이렇게 간단한 메인 스케치를 완성함.

 

다음 시간에는 Swiper를 적용시키면서 좀 더 스타일링해 예쁘게 만들어볼 예정이고,

Swiper와 Carousel의 차이를 알아보는 시간을 가져볼까 싶음.

'🎨 Front-end > React.js' 카테고리의 다른 글

React Hook의 이해 | useState, useEffect  (0) 2025.07.18
TOP

Designed by AndersonLab