From da0ec3217e969c29376e0309baf819d38a2f0217 Mon Sep 17 00:00:00 2001
From: Gyuhan Park <rbgks1937@naver.com>
Date: Tue, 4 Feb 2025 01:46:55 +0900
Subject: [PATCH 01/11] =?UTF-8?q?[YS-243]=20refactor:=20Banner=20=EC=8A=A4?=
 =?UTF-8?q?=ED=83=80=EC=9D=BC=EB=A7=81=20=EB=A7=88=EC=9D=B4=EA=B7=B8?=
 =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/home/components/Banner/Banner.css.ts  | 51 +++++++++++++++++++
 .../home/components/Banner/Banner.styles.ts   | 51 -------------------
 src/app/home/components/Banner/Banner.tsx     | 20 ++++----
 3 files changed, 61 insertions(+), 61 deletions(-)
 create mode 100644 src/app/home/components/Banner/Banner.css.ts
 delete mode 100644 src/app/home/components/Banner/Banner.styles.ts

diff --git a/src/app/home/components/Banner/Banner.css.ts b/src/app/home/components/Banner/Banner.css.ts
new file mode 100644
index 0000000..87d5dd2
--- /dev/null
+++ b/src/app/home/components/Banner/Banner.css.ts
@@ -0,0 +1,51 @@
+import { style } from '@vanilla-extract/css';
+
+export const bannerLayout = style({
+  display: 'flex',
+  flexDirection: 'column',
+  alignItems: 'center',
+  gap: '1.2rem',
+});
+
+export const bannerWrapper = style({
+  position: 'relative',
+  height: '15vh',
+  display: 'flex',
+  alignItems: 'center',
+});
+
+export const navigationLeft = style({
+  position: 'absolute',
+  left: '1.6rem',
+});
+
+export const bannerCarousel = style({
+  position: 'relative',
+  overflow: 'hidden',
+});
+
+export const carouselContainer = style({
+  display: 'flex',
+  transition: 'all 1s',
+});
+
+export const navigationRight = style({
+  position: 'absolute',
+  right: '1.6rem',
+});
+
+export const bannerImage = style({
+  width: '100%',
+  height: 'auto',
+});
+
+export const slideCircleContainer = style({
+  display: 'flex',
+  gap: '0.6rem',
+});
+
+export const slideCircle = style({
+  borderRadius: '50%',
+  width: '0.6rem',
+  height: '0.6rem',
+});
diff --git a/src/app/home/components/Banner/Banner.styles.ts b/src/app/home/components/Banner/Banner.styles.ts
deleted file mode 100644
index 26cbcdf..0000000
--- a/src/app/home/components/Banner/Banner.styles.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { css } from '@emotion/react';
-
-export const bannerLayout = css`
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  gap: 1.2rem;
-`;
-
-export const bannerWrapper = css`
-  position: relative;
-  height: 15vh;
-  display: flex;
-  align-items: center;
-`;
-
-export const navigationLeft = css`
-  position: absolute;
-  left: 1.6rem;
-`;
-
-export const bannerCarousel = css`
-  position: relative;
-  overflow: hidden;
-`;
-
-export const carouselContainer = css`
-  display: flex;
-  transition: all 1s;
-`;
-
-export const navigationRight = css`
-  position: absolute;
-  right: 1.6rem;
-`;
-
-export const bannerImage = css`
-  width: 100%;
-  height: auto;
-`;
-
-export const slideCircleContainer = css`
-  display: flex;
-  gap: 0.6rem;
-`;
-
-export const slideCircle = css`
-  border-radius: 50%;
-  width: 0.6rem;
-  height: 0.6rem;
-`;
diff --git a/src/app/home/components/Banner/Banner.tsx b/src/app/home/components/Banner/Banner.tsx
index 22afc7f..1094b96 100644
--- a/src/app/home/components/Banner/Banner.tsx
+++ b/src/app/home/components/Banner/Banner.tsx
@@ -11,7 +11,7 @@ import {
   carouselContainer,
   navigationLeft,
   navigationRight,
-} from './Banner.styles';
+} from './Banner.css';
 
 import BannerImage from '@/assets/images/banner.svg';
 import Icon from '@/components/Icon';
@@ -31,26 +31,26 @@ const Banner = () => {
   };
 
   return (
-    <div css={bannerLayout}>
-      <div css={bannerWrapper}>
-        <div css={bannerCarousel} ref={carouselRef}>
+    <div className={bannerLayout}>
+      <div className={bannerWrapper}>
+        <div className={bannerCarousel} ref={carouselRef}>
           <div
-            css={carouselContainer}
+            className={carouselContainer}
             style={{
               transform: carouselRef.current
                 ? `translateX(-${(bannerIdx - 1) * carouselRef.current.clientWidth}px)`
                 : 'none',
             }}
           >
-            <Image src={BannerImage} alt="배너1" css={bannerImage} priority />
-            <Image src={BannerImage} alt="배너2" css={bannerImage} priority />
-            <Image src={BannerImage} alt="배너3" css={bannerImage} priority />
+            <Image src={BannerImage} alt="배너1" className={bannerImage} priority />
+            <Image src={BannerImage} alt="배너2" className={bannerImage} priority />
+            <Image src={BannerImage} alt="배너3" className={bannerImage} priority />
           </div>
         </div>
-        <button css={navigationLeft} onClick={handleClickPrev}>
+        <button className={navigationLeft} onClick={handleClickPrev}>
           <Icon icon="ChevronSquare" rotate={-90} cursor="pointer" />
         </button>
-        <button css={navigationRight} onClick={handleClickNext}>
+        <button className={navigationRight} onClick={handleClickNext}>
           <Icon icon="ChevronSquare" rotate={90} cursor="pointer" />
         </button>
       </div>

From ade2409cad1a6ac16049121cf04b04733399b1ee Mon Sep 17 00:00:00 2001
From: Gyuhan Park <rbgks1937@naver.com>
Date: Tue, 4 Feb 2025 03:39:21 +0900
Subject: [PATCH 02/11] =?UTF-8?q?[YS-243]=20refactor:=20FilterContainer=20?=
 =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=EB=A7=81=20=EB=A7=88=EC=9D=B4?=
 =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../AreaFilter/AreaFilter.css.ts              | 211 ++++++++++++++++++
 .../AreaFilter/AreaFilter.styles.ts           | 176 ---------------
 .../PostContainer/AreaFilter/AreaFilter.tsx   |  97 ++++----
 .../ContactTargetFilter.css.ts                | 125 +++++++++++
 .../ContactTargetFilter.styles.ts             | 122 ----------
 .../ContactTargetFilter.tsx                   |  69 +++---
 .../FilterContainer/FilterContainer.css.ts    |   7 +
 .../FilterContainer/FilterContainer.styles.ts |   7 -
 .../FilterContainer/FilterContainer.tsx       |   4 +-
 .../PostContainer/PostContainer.css.ts        |  34 +++
 .../PostContainer/PostContainer.styles.ts     |  31 ---
 .../PostContainer/PostContainer.tsx           |  12 +-
 .../ProgressMethodFilter.css.ts               |  52 +++++
 .../ProgressMethodFilter.styles.ts            |  45 ----
 .../ProgressMethodFilter.tsx                  |  23 +-
 15 files changed, 538 insertions(+), 477 deletions(-)
 create mode 100644 src/app/home/components/PostContainer/AreaFilter/AreaFilter.css.ts
 delete mode 100644 src/app/home/components/PostContainer/AreaFilter/AreaFilter.styles.ts
 create mode 100644 src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.css.ts
 delete mode 100644 src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.styles.ts
 create mode 100644 src/app/home/components/PostContainer/FilterContainer/FilterContainer.css.ts
 delete mode 100644 src/app/home/components/PostContainer/FilterContainer/FilterContainer.styles.ts
 create mode 100644 src/app/home/components/PostContainer/PostContainer.css.ts
 delete mode 100644 src/app/home/components/PostContainer/PostContainer.styles.ts
 create mode 100644 src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.css.ts
 delete mode 100644 src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.styles.ts

diff --git a/src/app/home/components/PostContainer/AreaFilter/AreaFilter.css.ts b/src/app/home/components/PostContainer/AreaFilter/AreaFilter.css.ts
new file mode 100644
index 0000000..b2c66b8
--- /dev/null
+++ b/src/app/home/components/PostContainer/AreaFilter/AreaFilter.css.ts
@@ -0,0 +1,211 @@
+import { style } from '@vanilla-extract/css';
+import { recipe } from '@vanilla-extract/recipes';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+// Popover Trigger (동적 색상은 CSS 변수로 처리)
+export const triggerWrapper = style({
+  ...fonts.label.large.SB14,
+  color: 'var(--trigger-color)',
+  backgroundColor: 'var(--trigger-bg)',
+  display: 'flex',
+  alignItems: 'center',
+  gap: '0.4rem',
+  padding: '0.5rem 1.4rem',
+  borderRadius: '1.2rem',
+  selectors: {
+    '&:hover': {
+      boxShadow: '0px 4px 16px rgba(53, 59, 61, 0.2)',
+    },
+  },
+});
+
+// Popover Content 영역
+export const regionContentContainer = style({
+  backgroundColor: colors.field01,
+  position: 'relative',
+  top: '0.8rem',
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '1.2rem',
+  width: '36.4rem',
+  height: '35rem',
+  padding: '1.2rem',
+  borderRadius: '1.2rem',
+  boxShadow: '0px 4px 16px rgba(53, 59, 61, 0.2)',
+});
+
+// 내부 레이아웃
+export const contentWrapper = style({
+  backgroundColor: colors.field02,
+  borderRadius: '1.2rem',
+  display: 'flex',
+});
+
+// 지역 목록 컨테이너
+export const areaListContainer = style({
+  flex: '0.5',
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.4rem',
+  padding: '1.2rem 0.8rem',
+  backgroundColor: colors.field02,
+  borderRadius: '1.2rem',
+  height: '28rem',
+  overflow: 'scroll',
+});
+
+// 기본 텍스트 스타일
+export const areaName = style({
+  ...fonts.label.large.M14,
+  color: colors.text06,
+});
+
+// 선택된 지역 텍스트 스타일
+export const selectedAreaName = style({
+  color: colors.textPrimary,
+});
+
+// 숫자 표시 스타일
+export const areaCount = style({
+  ...fonts.label.medium.R13,
+  color: colors.text03,
+});
+
+// 지역 버튼을 recipe로 정의 (selected 여부에 따라 스타일이 변경됨)
+export const areaButtonRecipe = recipe({
+  base: {
+    display: 'flex',
+    gap: '0.4rem',
+    alignItems: 'center',
+    padding: '0.6rem 1.2rem',
+    borderRadius: '1.2rem',
+  },
+  variants: {
+    selected: {
+      true: {
+        backgroundColor: colors.primaryTinted,
+        outline: `0.1rem solid ${colors.lineTinted}`,
+        fontWeight: 'bold',
+      },
+      false: {
+        selectors: {
+          '&:hover': {
+            backgroundColor: colors.field03,
+          },
+        },
+      },
+    },
+  },
+  defaultVariants: {
+    selected: false,
+  },
+});
+
+// 세로 구분선
+export const verticalLine = style({
+  position: 'relative',
+  selectors: {
+    '&::after': {
+      content: '""',
+      width: '0.1rem',
+      height: '100%',
+      position: 'absolute',
+      top: '50%',
+      right: '0',
+      transform: 'translateY(-50%)',
+      backgroundColor: colors.line01,
+    },
+    '&:last-child::after': {
+      display: 'none',
+    },
+  },
+});
+
+// 서브 지역 리스트 컨테이너
+export const subAreaListContainer = style({
+  flex: 1,
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.4rem',
+  height: '28rem',
+  overflow: 'scroll',
+  padding: '1.2rem 0.8rem',
+  borderRadius: '1.2rem',
+});
+
+// 서브 지역 항목 (label) 기본 스타일
+export const subAreaItem = style({
+  display: 'flex',
+  alignItems: 'center',
+  justifyContent: 'space-between',
+  gap: '0.4rem',
+  height: '100%',
+  padding: '0.6rem 0.8rem',
+  borderRadius: '1.2rem',
+});
+
+// 선택된 서브 지역 항목 스타일
+export const selectedSubAreaLabel = style({
+  outline: `0.1rem solid ${colors.lineTinted}`,
+  backgroundColor: colors.primaryTinted,
+});
+
+// 체크박스 숨김 처리
+export const checkbox = style({
+  position: 'absolute',
+  opacity: 0,
+  pointerEvents: 'none',
+});
+
+// 서브 지역 정보 컨테이너
+export const subAreaInfo = style({
+  display: 'flex',
+  gap: '0.4rem',
+  alignItems: 'center',
+});
+
+// placeholder (지역 미선택 시)
+export const placeholderArea = style({
+  ...fonts.label.large.R14,
+  color: colors.text03,
+  display: 'flex',
+  justifyContent: 'center',
+  alignItems: 'center',
+  height: '100%',
+});
+
+// Footer 영역들
+export const footerContainer = style({
+  display: 'flex',
+  justifyContent: 'flex-end',
+});
+
+export const footerButtonContainer = style({
+  display: 'flex',
+  gap: '0.8rem',
+  alignItems: 'center',
+});
+
+// 버튼 스타일을 recipe로 재사용 (중복되는 padding, borderRadius 등)
+export const buttonRecipe = recipe({
+  base: {
+    ...fonts.label.large.SB14,
+    padding: '0.6rem 1.4rem',
+    border: 'none',
+    borderRadius: '1.2rem',
+  },
+  variants: {
+    type: {
+      reset: {
+        backgroundColor: colors.field03,
+        color: colors.text06,
+      },
+      save: {
+        backgroundColor: colors.primaryMint,
+        color: colors.text01,
+      },
+    },
+  },
+});
diff --git a/src/app/home/components/PostContainer/AreaFilter/AreaFilter.styles.ts b/src/app/home/components/PostContainer/AreaFilter/AreaFilter.styles.ts
deleted file mode 100644
index 023b641..0000000
--- a/src/app/home/components/PostContainer/AreaFilter/AreaFilter.styles.ts
+++ /dev/null
@@ -1,176 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const triggerWrapper = (theme: Theme) => css`
-  ${theme.fonts.label.large.SB14};
-  color: ${theme.colors.text06};
-  display: flex;
-  align-items: center;
-  gap: 0.4rem;
-  padding: 0.5rem 1.4rem;
-  border-radius: 1.2rem;
-  background-color: ${theme.colors.field01};
-
-  &:hover {
-    box-shadow: 0px 4px 16px rgba(53, 59, 61, 0.2);
-  }
-`;
-
-export const regionContentContainer = (theme: Theme) => css`
-  background-color: ${theme.colors.field01};
-  position: relative;
-  top: 0.8rem;
-  display: flex;
-  flex-direction: column;
-  gap: 1.2rem;
-  width: 36.4rem;
-  height: 35rem;
-  padding: 1.2rem;
-  border-radius: 1.2rem;
-  box-shadow: 0px 4px 16px rgba(53, 59, 61, 0.2);
-`;
-
-export const contentWrapper = (theme: Theme) => css`
-  background-color: ${theme.colors.field02};
-  border-radius: 1.2rem;
-  display: flex;
-`;
-
-export const areaListContainer = (theme: Theme) => css`
-  flex: 0.5;
-  display: flex;
-  flex-direction: column;
-  gap: 0.4rem;
-  padding: 1.2rem 0.8rem;
-  background-color: ${theme.colors.field02};
-  border-radius: 1.2rem;
-
-  height: 28rem;
-  overflow: scroll;
-`;
-
-export const areaName = (theme: Theme) => css`
-  ${theme.fonts.label.large.M14};
-  color: ${theme.colors.text06};
-`;
-
-export const selectedAreaName = (theme: Theme) => css`
-  color: ${theme.colors.textPrimary};
-`;
-
-export const areaCount = (theme: Theme) => css`
-  ${theme.fonts.label.medium.R13};
-  color: ${theme.colors.text03};
-`;
-
-export const areaButton = (theme: Theme) => css`
-  display: flex;
-  gap: 0.4rem;
-  align-items: center;
-  padding: 0.6rem 1.2rem;
-  border-radius: 1.2rem;
-
-  &:hover {
-    background-color: ${theme.colors.field03};
-  }
-`;
-
-export const selectedAreaButton = (theme: Theme) => css`
-  background-color: ${theme.colors.primaryTinted};
-  outline: 0.1rem solid ${theme.colors.lineTinted};
-  font-weight: bold;
-`;
-
-export const verticalLine = (theme: Theme) => css`
-  position: relative;
-
-  ::after {
-    content: '';
-    width: 0.1rem;
-    height: 100%;
-    position: absolute;
-    top: 50%;
-    right: 0;
-    transform: translateY(-50%);
-    background-color: ${theme.colors.line01};
-  }
-
-  :last-child::after {
-    display: none;
-  }
-`;
-
-export const subAreaListContainer = css`
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  gap: 0.4rem;
-  height: 28rem;
-  overflow: scroll;
-  padding: 1.2rem 0.8rem;
-  border-radius: 1.2rem;
-`;
-
-export const subAreaItem = css`
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 0.4rem;
-  height: 100%;
-  padding: 0.6rem 0.8rem;
-  border-radius: 1.2rem;
-`;
-
-export const selectedSubAreaLabel = (theme: Theme) => css`
-  outline: 0.1rem solid ${theme.colors.lineTinted};
-  background-color: ${theme.colors.primaryTinted};
-`;
-
-export const checkbox = css`
-  position: absolute;
-  opacity: 0;
-  pointer-events: none;
-`;
-
-export const subAreaInfo = css`
-  display: flex;
-  gap: 0.4rem;
-  align-items: center;
-`;
-
-export const placeholderArea = (theme: Theme) => css`
-  ${theme.fonts.label.large.R14};
-  color: ${theme.colors.text03};
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  height: 100%;
-`;
-
-export const footerContainer = css`
-  display: flex;
-  justify-content: flex-end;
-`;
-
-export const footerButtonContainer = css`
-  display: flex;
-  gap: 0.8rem;
-  align-items: center;
-`;
-
-export const resetButton = (theme: Theme) => css`
-  ${theme.fonts.label.large.SB14};
-  background-color: ${theme.colors.field03};
-  color: ${theme.colors.text06};
-  padding: 0.6rem 1.4rem;
-  border: none;
-  border-radius: 1.2rem;
-`;
-
-export const saveButton = (theme: Theme) => css`
-  ${theme.fonts.label.large.SB14};
-  background-color: ${theme.colors.primaryMint};
-  color: ${theme.colors.text01};
-  padding: 0.6rem 1.4rem;
-  border: none;
-  border-radius: 1.2rem;
-`;
diff --git a/src/app/home/components/PostContainer/AreaFilter/AreaFilter.tsx b/src/app/home/components/PostContainer/AreaFilter/AreaFilter.tsx
index 933e597..835f244 100644
--- a/src/app/home/components/PostContainer/AreaFilter/AreaFilter.tsx
+++ b/src/app/home/components/PostContainer/AreaFilter/AreaFilter.tsx
@@ -1,37 +1,36 @@
 'use client';
 
 import * as Popover from '@radix-ui/react-popover';
+import { assignInlineVars } from '@vanilla-extract/dynamic';
 import { useState } from 'react';
 
 import {
-  areaButton,
-  areaCount,
+  triggerWrapper,
+  regionContentContainer,
+  contentWrapper,
   areaListContainer,
   areaName,
-  checkbox,
-  contentWrapper,
-  footerButtonContainer,
-  footerContainer,
-  placeholderArea,
-  regionContentContainer,
-  resetButton,
-  saveButton,
-  selectedAreaButton,
   selectedAreaName,
+  areaCount,
+  areaButtonRecipe,
+  verticalLine,
+  subAreaListContainer,
+  subAreaItem,
   selectedSubAreaLabel,
+  checkbox,
   subAreaInfo,
-  subAreaItem,
-  subAreaListContainer,
-  triggerWrapper,
-  verticalLine,
-} from './AreaFilter.styles';
+  placeholderArea,
+  footerContainer,
+  footerButtonContainer,
+  buttonRecipe,
+} from './AreaFilter.css';
 
 import { areaMapper, subAreaMapper } from '@/app/home/home.constants';
 import { Area } from '@/app/home/home.types';
 import useFilterAreaQuery from '@/app/home/hooks/useFilterAreaQuery';
 import useFilterSubAreaQuery from '@/app/home/hooks/useFilterSubAreaQuery';
 import Icon from '@/components/Icon';
-import theme from '@/styles/theme';
+import { colors } from '@/styles/colors';
 
 interface AreaFilterProps {
   onChange: (key: string, value: string | number) => void;
@@ -68,13 +67,13 @@ const AreaFilter = ({ onChange }: AreaFilterProps) => {
   const [isSelected, setIsSelected] = useState(false);
 
   return (
-    <Popover.Root open={isOpen} onOpenChange={(open) => setIsOpen(open)}>
+    <Popover.Root open={isOpen} onOpenChange={setIsOpen}>
       <Popover.Trigger
-        css={triggerWrapper}
-        style={{
-          color: isSelected ? theme.colors.text01 : theme.colors.text06,
-          backgroundColor: isSelected ? theme.colors.field09 : theme.colors.field01,
-        }}
+        className={triggerWrapper}
+        style={assignInlineVars({
+          '--trigger-color': isSelected ? colors.text01 : colors.text06,
+          '--trigger-bg': isSelected ? colors.field09 : colors.field01,
+        })}
       >
         <span>
           {isSelected
@@ -86,49 +85,55 @@ const AreaFilter = ({ onChange }: AreaFilterProps) => {
         <Icon icon="Chevron" width={20} rotate={isOpen ? -180 : 0} cursor="pointer" />
       </Popover.Trigger>
       <Popover.Portal>
-        <Popover.Content css={regionContentContainer}>
-          <div css={contentWrapper}>
-            <div css={areaListContainer}>
+        <Popover.Content className={regionContentContainer}>
+          <div className={contentWrapper}>
+            <div className={areaListContainer}>
               <button
-                css={[areaButton, selectedArea === 'ALL' && selectedAreaButton]}
+                className={areaButtonRecipe({ selected: selectedArea === 'ALL' })}
                 onClick={() => handleAreaClick('ALL')}
               >
-                <span css={[areaName, selectedArea === 'ALL' && selectedAreaName]}>
+                <span className={`${areaName} ${selectedArea === 'ALL' && selectedAreaName}`}>
                   {areaMapper['ALL']}
                 </span>
-                <span css={areaCount}>{postArea?.reduce((acc, cur) => acc + cur.count, 0)}</span>
+                <span className={areaCount}>
+                  {postArea?.reduce((acc, cur) => acc + cur.count, 0)}
+                </span>
               </button>
-
               {postArea?.map((area, idx) => (
                 <button
                   key={idx}
-                  css={[areaButton, area.name === selectedArea && selectedAreaButton]}
+                  className={areaButtonRecipe({ selected: area.name === selectedArea })}
                   onClick={() => handleAreaClick(area.name)}
                 >
-                  <span css={[areaName, area.name === selectedArea && selectedAreaName]}>
+                  <span className={`${areaName} ${area.name === selectedArea && selectedAreaName}`}>
                     {areaMapper[area.name]}
                   </span>
-                  <span css={areaCount}>{area.count}</span>
+                  <span className={areaCount}>{area.count}</span>
                 </button>
               ))}
             </div>
-            <span css={verticalLine} />
-            <div css={subAreaListContainer}>
+            <span className={verticalLine} />
+            <div className={subAreaListContainer}>
               {selectedArea ? (
                 postSubArea?.map((subArea, idx) => (
                   <label
                     key={idx}
-                    css={[subAreaItem, checkedSubAreas[subArea.name] && selectedSubAreaLabel]}
+                    className={`${subAreaItem} 
+                      ${checkedSubAreas[subArea.name] && selectedSubAreaLabel}`}
                   >
-                    <div css={subAreaInfo}>
-                      <span css={[areaName, checkedSubAreas[subArea.name] && selectedAreaName]}>
+                    <div className={subAreaInfo}>
+                      <span
+                        className={`${areaName} ${
+                          checkedSubAreas[subArea.name] && selectedAreaName
+                        }`}
+                      >
                         {subAreaMapper[subArea.name]}
                       </span>
-                      <span css={areaCount}>{subArea.count}</span>
+                      <span className={areaCount}>{subArea.count}</span>
                     </div>
                     <input
                       type="checkbox"
-                      css={checkbox}
+                      className={checkbox}
                       checked={!!checkedSubAreas[subArea.name]}
                       onChange={() => handleSubAreaCheck(subArea.name)}
                     />
@@ -137,7 +142,7 @@ const AreaFilter = ({ onChange }: AreaFilterProps) => {
                         icon="CheckSquareFill"
                         width={20}
                         height={20}
-                        color={theme.colors.primaryMint}
+                        color={colors.primaryMint}
                       />
                     ) : (
                       <Icon icon="CheckSquareEmpty" width={20} height={20} />
@@ -145,16 +150,16 @@ const AreaFilter = ({ onChange }: AreaFilterProps) => {
                   </label>
                 ))
               ) : (
-                <div css={placeholderArea}>
+                <div className={placeholderArea}>
                   <span>지역을 먼저 선택해 주세요</span>
                 </div>
               )}
             </div>
           </div>
-          <div css={footerContainer}>
-            <div css={footerButtonContainer}>
+          <div className={footerContainer}>
+            <div className={footerButtonContainer}>
               <button
-                css={resetButton}
+                className={buttonRecipe({ type: 'reset' })}
                 onClick={() => {
                   setSelectedArea('');
                   setCheckedSubAreas({});
@@ -162,7 +167,7 @@ const AreaFilter = ({ onChange }: AreaFilterProps) => {
               >
                 초기화
               </button>
-              <button onClick={handleClickSave} css={saveButton}>
+              <button onClick={handleClickSave} className={buttonRecipe({ type: 'save' })}>
                 저장
               </button>
             </div>
diff --git a/src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.css.ts b/src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.css.ts
new file mode 100644
index 0000000..72f239c
--- /dev/null
+++ b/src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.css.ts
@@ -0,0 +1,125 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const genderSelectWrapper = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.8rem',
+});
+
+export const ageSelectWrapper = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.8rem',
+});
+
+export const popoverTrigger = style({
+  ...fonts.label.large.SB14,
+  display: 'flex',
+  gap: '0.4rem',
+  alignItems: 'center',
+  width: 'fit-content',
+  padding: '0.6rem 1.4rem',
+  border: 'none',
+  borderRadius: '1.2rem',
+  color: 'var(--popover-trigger-color)',
+  backgroundColor: 'var(--popover-trigger-bg)',
+  selectors: {
+    '&:hover': {
+      boxShadow: '0px 4px 16px rgba(53, 59, 61, 0.2)',
+    },
+  },
+});
+
+export const popoverContent = style({
+  display: 'flex',
+  flexDirection: 'column',
+  padding: '2rem',
+  borderRadius: '1.2rem',
+  backgroundColor: colors.field01,
+  boxShadow: '0px 4px 16px rgba(53, 59, 61, 0.2)',
+  gap: '1.6rem',
+  marginTop: '0.6rem',
+});
+
+export const labelWrapper = style({
+  display: 'flex',
+  gap: '0.4rem',
+  alignItems: 'center',
+});
+
+export const label = style({
+  ...fonts.body.normal.M16,
+  color: colors.text06,
+});
+
+export const subLabel = style({
+  ...fonts.label.large.R14,
+  color: colors.text03,
+});
+
+export const genderButtonGroup = style({
+  display: 'flex',
+  gap: '0.8rem',
+});
+
+export const genderButton = style({
+  ...fonts.label.large.M14,
+  color: colors.text06,
+  padding: '0.6rem 2.15rem',
+  borderRadius: '1.2rem',
+  outline: `1px solid ${colors.line01}`,
+  transition: 'color 0.1s, background-color 0.1s, outline 0.1s',
+  width: '10.6rem',
+  selectors: {
+    '&.active': {
+      backgroundColor: colors.primaryTinted,
+      outline: `0.1rem solid ${colors.lineTinted}`,
+      color: colors.textPrimary,
+    },
+  },
+});
+
+export const ageInputContainer = style({
+  display: 'flex',
+  gap: '1rem',
+  alignItems: 'center',
+});
+
+export const ageInput = style({
+  ...fonts.label.large.M14,
+  width: '100%',
+  padding: '0.6rem 0 0.6rem 1.6rem',
+  border: `0.1rem solid ${colors.line01}`,
+  borderRadius: '1.2rem',
+  selectors: {
+    '&:focus': {
+      outline: 'none',
+      border: `0.1rem solid ${colors.primaryMint}`,
+    },
+  },
+});
+
+export const footerButtonContainer = style({
+  display: 'flex',
+  gap: '0.8rem',
+  justifyContent: 'flex-end',
+});
+
+export const resetButton = style({
+  ...fonts.label.large.SB14,
+  padding: '0.6rem 1.4rem',
+  borderRadius: '1.2rem',
+  backgroundColor: colors.field03,
+  color: colors.text06,
+});
+
+export const saveButton = style({
+  ...fonts.label.large.SB14,
+  padding: '0.6rem 1.4rem',
+  borderRadius: '1.2rem',
+  backgroundColor: colors.primaryMint,
+  color: colors.text01,
+});
diff --git a/src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.styles.ts b/src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.styles.ts
deleted file mode 100644
index 7cd2d71..0000000
--- a/src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.styles.ts
+++ /dev/null
@@ -1,122 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const genderSelectWrapper = css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.8rem;
-`;
-
-export const ageSelectWrapper = css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.8rem;
-`;
-
-export const popoverTrigger = (theme: Theme) => css`
-  ${theme.fonts.label.large.SB14};
-  display: flex;
-  gap: 0.4rem;
-  align-items: center;
-  width: fit-content;
-  padding: 0.6rem 1.4rem;
-  border: none;
-  border-radius: 1.2rem;
-
-  &:hover {
-    box-shadow: 0px 4px 16px rgba(53, 59, 61, 0.2);
-  }
-`;
-
-export const popoverContent = (theme: Theme) => css`
-  display: flex;
-  flex-direction: column;
-  padding: 2rem;
-  border-radius: 1.2rem;
-  background-color: ${theme.colors.field01};
-  box-shadow: 0px 4px 16px rgba(53, 59, 61, 0.2);
-  gap: 1.6rem;
-  margin-top: 0.6rem;
-`;
-
-export const labelWrapper = css`
-  display: flex;
-  gap: 0.4rem;
-  align-items: center;
-`;
-
-export const label = (theme: Theme) => css`
-  ${theme.fonts.body.normal.M16};
-  color: ${theme.colors.text06};
-`;
-
-export const subLabel = (theme: Theme) => css`
-  ${theme.fonts.label.large.R14};
-  color: ${theme.colors.text03};
-`;
-
-export const buttonGroup = (theme: Theme) => css`
-  display: flex;
-  gap: 0.8rem;
-
-  button {
-    ${theme.fonts.label.large.M14};
-    color: ${theme.colors.text06};
-    padding: 0.6rem 2.15rem;
-    border-radius: 1.2rem;
-    outline: 1px solid ${theme.colors.line01};
-    transition: color 0.1s, background-color 0.1s, outline 0.1s;
-    width: 10.6rem;
-  }
-
-  button.active {
-    background-color: ${theme.colors.primaryTinted};
-    outline: 0.1rem solid ${theme.colors.lineTinted};
-    color: ${theme.colors.textPrimary};
-  }
-`;
-
-export const ageInputContainer = (theme: Theme) => css`
-  display: flex;
-  gap: 1rem;
-  align-items: center;
-
-  input {
-    ${theme.fonts.label.large.M14};
-    width: 100%;
-    padding: 0.6rem 0 0.6rem 1.6rem;
-    border: 0.1rem solid ${theme.colors.line01};
-    border-radius: 1.2rem;
-
-    :focus {
-      outline: none;
-      border: 0.1rem solid ${theme.colors.primaryMint};
-    }
-  }
-`;
-
-export const ageButtonWrapper = css`
-  display: flex;
-  gap: 0.4rem;
-`;
-
-export const footerButtonContainer = (theme: Theme) => css`
-  display: flex;
-  gap: 0.8rem;
-  justify-content: flex-end;
-
-  button {
-    ${theme.fonts.label.large.SB14};
-    padding: 0.6rem 1.4rem;
-    border-radius: 1.2rem;
-  }
-`;
-
-export const resetButton = (theme: Theme) => css`
-  background-color: ${theme.colors.field03};
-  color: ${theme.colors.text06};
-`;
-
-export const saveButton = (theme: Theme) => css`
-  background-color: ${theme.colors.primaryMint};
-  color: ${theme.colors.text01};
-`;
diff --git a/src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.tsx b/src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.tsx
index b92648a..669256c 100644
--- a/src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.tsx
+++ b/src/app/home/components/PostContainer/ContactTargetPopover/ContactTargetFilter.tsx
@@ -1,24 +1,27 @@
 'use client';
 
 import * as Popover from '@radix-ui/react-popover';
+import { assignInlineVars } from '@vanilla-extract/dynamic';
 import { ChangeEvent, useState } from 'react';
 
 import {
-  ageInputContainer,
-  ageSelectWrapper,
-  buttonGroup,
-  footerButtonContainer,
   genderSelectWrapper,
-  label,
-  labelWrapper,
-  popoverContent,
+  ageSelectWrapper,
   popoverTrigger,
+  popoverContent,
+  labelWrapper,
+  label,
+  ageInputContainer,
+  footerButtonContainer,
   resetButton,
   saveButton,
-} from './ContactTargetFilter.styles';
+  ageInput,
+  genderButton,
+  genderButtonGroup,
+} from './ContactTargetFilter.css';
 
 import Icon from '@/components/Icon';
-import theme from '@/styles/theme';
+import { colors } from '@/styles/colors';
 
 const isEqualByKeys = (
   obj1: Record<string, string>,
@@ -81,44 +84,48 @@ const ContactTargetFilter = ({ onChange }: ContactTargetFilterProps) => {
   return (
     <Popover.Root open={isOpen} onOpenChange={() => setIsOpen((prev) => !prev)}>
       <Popover.Trigger
-        css={popoverTrigger}
-        style={{
-          color: isSelected ? theme.colors.text01 : theme.colors.text06,
-          backgroundColor: isSelected ? theme.colors.field09 : theme.colors.field01,
-        }}
+        className={popoverTrigger}
+        style={assignInlineVars({
+          '--popover-trigger-color': isSelected ? colors.text01 : colors.text06,
+          '--popover-trigger-bg': isSelected ? colors.field09 : colors.field01,
+        })}
       >
         <span>{isSelected ? `${age}세 ${gender.label}` : '모집 대상'}</span>
         <Icon icon="Chevron" width={20} rotate={isOpen ? -180 : 0} cursor="pointer" />
       </Popover.Trigger>
       <Popover.Portal>
-        <Popover.Content css={popoverContent}>
-          <div css={genderSelectWrapper}>
-            <span css={label}>성별</span>
-            <div css={buttonGroup}>
-              {GENDER.map((gender) => (
+        <Popover.Content className={popoverContent}>
+          <div className={genderSelectWrapper}>
+            <span className={label}>성별</span>
+            <div className={genderButtonGroup}>
+              {GENDER.map((g) => (
                 <button
-                  key={gender.value}
-                  className={gender === filteredGender ? 'active' : ''}
-                  onClick={() => setFilteredGender(gender as Gender)}
+                  key={g.value}
+                  className={`${genderButton} ${g === filteredGender ? 'active' : ''}`}
+                  onClick={() => setFilteredGender(g as Gender)}
                 >
-                  {gender.label}
+                  {g.label}
                 </button>
               ))}
             </div>
           </div>
-          <div css={ageSelectWrapper}>
-            <div css={labelWrapper}>
-              <span css={label}>나이</span>
+          <div className={ageSelectWrapper}>
+            <div className={labelWrapper}>
+              <span className={label}>나이</span>
             </div>
-            <div css={ageInputContainer}>
-              <input onChange={handleChangeFilteredAge} placeholder="만 나이 입력" />
+            <div className={ageInputContainer}>
+              <input
+                className={ageInput}
+                onChange={handleChangeFilteredAge}
+                placeholder="만 나이 입력"
+              />
             </div>
           </div>
-          <div css={footerButtonContainer}>
-            <button onClick={handleReset} css={resetButton}>
+          <div className={footerButtonContainer}>
+            <button onClick={handleReset} className={resetButton}>
               초기화
             </button>
-            <button onClick={handleSave} css={saveButton}>
+            <button onClick={handleSave} className={saveButton}>
               저장
             </button>
           </div>
diff --git a/src/app/home/components/PostContainer/FilterContainer/FilterContainer.css.ts b/src/app/home/components/PostContainer/FilterContainer/FilterContainer.css.ts
new file mode 100644
index 0000000..f413ced
--- /dev/null
+++ b/src/app/home/components/PostContainer/FilterContainer/FilterContainer.css.ts
@@ -0,0 +1,7 @@
+import { style } from '@vanilla-extract/css';
+
+export const filterLayout = style({
+  display: 'flex',
+  gap: '0.8rem',
+  height: '3.2rem',
+});
diff --git a/src/app/home/components/PostContainer/FilterContainer/FilterContainer.styles.ts b/src/app/home/components/PostContainer/FilterContainer/FilterContainer.styles.ts
deleted file mode 100644
index 526f540..0000000
--- a/src/app/home/components/PostContainer/FilterContainer/FilterContainer.styles.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { css } from '@emotion/react';
-
-export const filterLayout = css`
-  display: flex;
-  gap: 0.8rem;
-  height: 3.2rem;
-`;
diff --git a/src/app/home/components/PostContainer/FilterContainer/FilterContainer.tsx b/src/app/home/components/PostContainer/FilterContainer/FilterContainer.tsx
index 151616a..d507a8a 100644
--- a/src/app/home/components/PostContainer/FilterContainer/FilterContainer.tsx
+++ b/src/app/home/components/PostContainer/FilterContainer/FilterContainer.tsx
@@ -1,4 +1,4 @@
-import { filterLayout } from './FilterContainer.styles';
+import { filterLayout } from './FilterContainer.css';
 import AreaFilter from '../AreaFilter/AreaFilter';
 import ContactTargetFilter from '../ContactTargetPopover/ContactTargetFilter';
 import ProgressMethodFilter from '../ProgressMethodFilter/ProgressMethodFilter';
@@ -9,7 +9,7 @@ interface FilterContainerProps {
 
 const FilterContainer = ({ handleFilterChange }: FilterContainerProps) => {
   return (
-    <div css={filterLayout}>
+    <div className={filterLayout}>
       <ProgressMethodFilter onChange={(value) => handleFilterChange('matchType', value)} />
       <ContactTargetFilter onChange={handleFilterChange} />
       <AreaFilter onChange={handleFilterChange} />
diff --git a/src/app/home/components/PostContainer/PostContainer.css.ts b/src/app/home/components/PostContainer/PostContainer.css.ts
new file mode 100644
index 0000000..7e359b0
--- /dev/null
+++ b/src/app/home/components/PostContainer/PostContainer.css.ts
@@ -0,0 +1,34 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const postContainerLayout = style({
+  margin: '2rem 0',
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '1.6rem',
+});
+
+export const postContainerTitle = style({
+  ...fonts.title.medium.SB20,
+  color: colors.text06,
+});
+
+export const postCardContainer = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.6rem',
+  minHeight: '40rem',
+});
+
+export const totalPostCount = style({
+  ...fonts.label.large.R14,
+  color: colors.text03,
+});
+
+export const filterWrapper = style({
+  display: 'flex',
+  alignItems: 'center',
+  justifyContent: 'space-between',
+});
diff --git a/src/app/home/components/PostContainer/PostContainer.styles.ts b/src/app/home/components/PostContainer/PostContainer.styles.ts
deleted file mode 100644
index 535d32d..0000000
--- a/src/app/home/components/PostContainer/PostContainer.styles.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const postContainerLayout = css`
-  margin: 2rem 0;
-  display: flex;
-  flex-direction: column;
-  gap: 1.6rem;
-`;
-
-export const postContainerTitle = (theme: Theme) => css`
-  ${theme.fonts.title.medium.SB20};
-  color: ${theme.colors.text06};
-`;
-
-export const postCardContainer = css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.6rem;
-  min-height: 40rem;
-`;
-
-export const totalPostCount = (theme: Theme) => css`
-  ${theme.fonts.label.large.R14};
-  color: ${theme.colors.text03};
-`;
-
-export const filterWrapper = css`
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-`;
diff --git a/src/app/home/components/PostContainer/PostContainer.tsx b/src/app/home/components/PostContainer/PostContainer.tsx
index dbdb730..babfddb 100644
--- a/src/app/home/components/PostContainer/PostContainer.tsx
+++ b/src/app/home/components/PostContainer/PostContainer.tsx
@@ -11,7 +11,7 @@ import {
   postContainerLayout,
   postContainerTitle,
   totalPostCount,
-} from './PostContainer.styles';
+} from './PostContainer.css';
 import { filterParticipantInfo } from '../../home.utils';
 import useUserInfo from '../../hooks/useUserInfo';
 
@@ -63,9 +63,9 @@ const PostContainer = () => {
   }, [participantInfo]);
 
   return (
-    <div css={postContainerLayout}>
-      <h2 css={postContainerTitle}>공고를 확인해 보세요</h2>
-      <div css={filterWrapper}>
+    <div className={postContainerLayout}>
+      <h2 className={postContainerTitle}>공고를 확인해 보세요</h2>
+      <div className={filterWrapper}>
         <FilterContainer handleFilterChange={handleFilterChange} />
         <JoinCheckbox
           label="모집 중인 공고만 보기"
@@ -74,8 +74,8 @@ const PostContainer = () => {
           isArrow={false}
         />
       </div>
-      <div css={postCardContainer}>
-        <span css={totalPostCount}>총 {postListData?.content.length}개</span>
+      <div className={postCardContainer}>
+        <span className={totalPostCount}>총 {postListData?.content.length}개</span>
         <PostCardList postList={postListData?.content} />
       </div>
     </div>
diff --git a/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.css.ts b/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.css.ts
new file mode 100644
index 0000000..36ade5b
--- /dev/null
+++ b/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.css.ts
@@ -0,0 +1,52 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const triggerWrapper = style({
+  ...fonts.label.large.SB14,
+  //   color: colors.text06,
+  color: 'var(--trigger-color)',
+  backgroundColor: 'var(--trigger-bg)',
+  display: 'flex',
+  alignItems: 'center',
+  justifyContent: 'space-between',
+  gap: '0.4rem',
+  borderRadius: '1.2rem',
+  padding: '0.5rem 1.4rem',
+  //   backgroundColor: colors.field01,
+  cursor: 'pointer',
+  selectors: {
+    '&:hover': {
+      boxShadow: '0px 4px 16px rgba(53, 59, 61, 0.2)',
+    },
+  },
+});
+
+export const contentContainer = style({
+  position: 'relative',
+  top: '3.6rem',
+  width: '10rem',
+  padding: '0.8rem',
+  backgroundColor: colors.field01,
+  borderRadius: '0.8rem',
+  boxShadow: '0px 4px 16px rgba(53, 59, 61, 0.2)',
+  overflow: 'hidden',
+});
+
+export const selectItem = style({
+  ...fonts.label.large.M14,
+  color: colors.text06,
+  display: 'flex',
+  paddingLeft: '1.2rem',
+  alignItems: 'center',
+  height: '3.4rem',
+  borderRadius: '1.2rem',
+  cursor: 'pointer',
+  selectors: {
+    '&[data-highlighted]': {
+      backgroundColor: colors.field02,
+      outline: 'none',
+    },
+  },
+});
diff --git a/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.styles.ts b/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.styles.ts
deleted file mode 100644
index da7435c..0000000
--- a/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.styles.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const triggerWrapper = (theme: Theme) => css`
-  ${theme.fonts.label.large.SB14};
-  color: ${theme.colors.text06};
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 0.4rem;
-  border-radius: 1.2rem;
-  padding: 0.5rem 1.4rem;
-  background-color: ${theme.colors.field01};
-  cursor: pointer;
-
-  &:hover {
-    box-shadow: 0px 4px 16px rgba(53, 59, 61, 0.2);
-  }
-`;
-
-export const contentContainer = (theme: Theme) => css`
-  position: relative;
-  top: 3.6rem;
-  width: 10rem;
-  padding: 0.8rem;
-  background-color: ${theme.colors.field01};
-  border-radius: 0.8rem;
-  box-shadow: 0px 4px 16px rgba(53, 59, 61, 0.2);
-  overflow: hidden;
-`;
-
-export const selectItem = (theme: Theme) => css`
-  ${theme.fonts.label.large.M14};
-  color: ${theme.colors.text06};
-  display: flex;
-  padding-left: 1.2rem;
-  align-items: center;
-  height: 3.4rem;
-  border-radius: 1.2rem;
-  cursor: pointer;
-
-  &[data-highlighted] {
-    background-color: ${theme.colors.field02};
-    outline: none;
-  }
-`;
diff --git a/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx b/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx
index 56fe2cb..da88816 100644
--- a/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx
+++ b/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx
@@ -1,12 +1,13 @@
 'use client';
 
 import * as Select from '@radix-ui/react-select';
+import { assignInlineVars } from '@vanilla-extract/dynamic';
 import { useState } from 'react';
 
-import { contentContainer, selectItem, triggerWrapper } from './ProgressMethodFilter.styles';
+import { triggerWrapper, contentContainer, selectItem } from './ProgressMethodFilter.css';
 
 import Icon from '@/components/Icon';
-import theme from '@/styles/theme';
+import { colors } from '@/styles/colors';
 
 interface FilterOption {
   label: '전체' | '대면' | '비대면';
@@ -45,27 +46,27 @@ const ProgressMethodFilter = ({ onChange }: ProgressMethodFilterProps) => {
       onOpenChange={(open) => setIsOpen(open)}
     >
       <Select.Trigger
-        css={triggerWrapper}
-        style={{
-          color: isSelected ? theme.colors.text01 : theme.colors.text06,
-          backgroundColor: isSelected ? theme.colors.field09 : theme.colors.field01,
-        }}
+        className={triggerWrapper}
+        style={assignInlineVars({
+          '--trigger-color': isSelected ? colors.text01 : colors.text06,
+          '--trigger-bg': isSelected ? colors.field09 : colors.field01,
+        })}
       >
         <span>{selectedValue.label}</span>
         <Select.Icon>
           <Icon icon="Chevron" width={20} rotate={isOpen ? -180 : 0} cursor="pointer" />
         </Select.Icon>
       </Select.Trigger>
-      <Select.Content css={contentContainer}>
+      <Select.Content className={contentContainer}>
         <Select.Viewport>
           <Select.Group>
-            <Select.Item value="ALL" css={selectItem}>
+            <Select.Item value="ALL" className={selectItem}>
               <Select.ItemText>전체</Select.ItemText>
             </Select.Item>
-            <Select.Item value="ONLINE" css={selectItem}>
+            <Select.Item value="ONLINE" className={selectItem}>
               <Select.ItemText>대면</Select.ItemText>
             </Select.Item>
-            <Select.Item value="OFFLINE" css={selectItem}>
+            <Select.Item value="OFFLINE" className={selectItem}>
               <Select.ItemText>비대면</Select.ItemText>
             </Select.Item>
           </Select.Group>

From ed1215632e295c887abc13ec7bb86b2a4a18c94b Mon Sep 17 00:00:00 2001
From: Gyuhan Park <rbgks1937@naver.com>
Date: Tue, 4 Feb 2025 04:00:05 +0900
Subject: [PATCH 03/11] =?UTF-8?q?[YS-243]=20refactor:=20PostCardList=20?=
 =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=EB=A7=81=20=EB=A7=88=EC=9D=B4?=
 =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../home/components/PostCard/PostCard.css.ts  | 88 +++++++++++++++++++
 .../components/PostCard/PostCard.styles.ts    | 83 -----------------
 src/app/home/components/PostCard/PostCard.tsx | 30 +++----
 .../EmptyPostCard/EmptyPostCard.css.ts        | 30 +++++++
 .../EmptyPostCard/EmptyPostCard.styles.ts     | 27 ------
 .../EmptyPostCard/EmptyPostCard.tsx           | 10 +--
 .../PostCardList/PostCardList.css.ts          |  7 ++
 .../PostCardList/PostCardList.styles.ts       |  7 --
 .../components/PostCardList/PostCardList.tsx  |  4 +-
 .../JoinCheckbox/JoinCheckbox.css.ts          | 52 +++++++++++
 .../JoinCheckbox/JoinCheckbox.tsx             | 26 +++---
 src/app/page.tsx                              |  1 -
 src/components/Icon/icons/AllEmpty.tsx        |  5 +-
 13 files changed, 215 insertions(+), 155 deletions(-)
 create mode 100644 src/app/home/components/PostCard/PostCard.css.ts
 delete mode 100644 src/app/home/components/PostCard/PostCard.styles.ts
 create mode 100644 src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.css.ts
 delete mode 100644 src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.styles.ts
 create mode 100644 src/app/home/components/PostCardList/PostCardList.css.ts
 delete mode 100644 src/app/home/components/PostCardList/PostCardList.styles.ts
 create mode 100644 src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.css.ts

diff --git a/src/app/home/components/PostCard/PostCard.css.ts b/src/app/home/components/PostCard/PostCard.css.ts
new file mode 100644
index 0000000..d3e656c
--- /dev/null
+++ b/src/app/home/components/PostCard/PostCard.css.ts
@@ -0,0 +1,88 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const postCardLayout = style({
+  display: 'flex',
+  flexDirection: 'column',
+  justifyContent: 'space-between',
+  gap: '1.6rem',
+
+  border: 'none',
+  borderRadius: '1.2rem',
+
+  height: '20rem',
+  padding: '1.6rem 2rem',
+  backgroundColor: colors.field01,
+
+  selectors: {
+    '&:hover': {
+      boxShadow: '0px 4px 16px rgba(53, 59, 61, 0.08)',
+    },
+  },
+});
+
+export const postHeader = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.8rem',
+});
+
+export const postCardHeader = style({
+  display: 'flex',
+  justifyContent: 'space-between',
+  alignItems: 'center',
+  gap: '1.6rem',
+});
+
+export const postLocation = style({
+  ...fonts.label.medium.R13,
+  color: colors.text03,
+});
+
+export const postCardRightHeader = style({
+  display: 'flex',
+  alignItems: 'center',
+  gap: '0.4rem',
+});
+
+export const postViews = style({
+  ...fonts.label.medium.R13,
+  color: colors.text03,
+});
+
+export const postTitle = style({
+  ...fonts.title.small.SB18,
+  color: colors.text06,
+});
+
+export const contactedPostTag = style({
+  width: 'fit-content',
+  padding: '0.6rem 0.85rem',
+  ...fonts.label.small.SB12,
+  color: colors.text05,
+  backgroundColor: colors.field03,
+  borderRadius: '3rem',
+});
+
+export const postRewardContainer = style({
+  display: 'flex',
+  gap: '0.8rem',
+  alignItems: 'center',
+});
+
+export const announceText = style({
+  ...fonts.label.medium.M13,
+  color: colors.text03,
+});
+
+export const postReward = style({
+  ...fonts.label.medium.SB13,
+  color: colors.primaryMint,
+});
+
+export const postDate = style({
+  ...fonts.label.medium.M13,
+  color: colors.text04,
+});
diff --git a/src/app/home/components/PostCard/PostCard.styles.ts b/src/app/home/components/PostCard/PostCard.styles.ts
deleted file mode 100644
index fee3c45..0000000
--- a/src/app/home/components/PostCard/PostCard.styles.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const postCardLayout = (theme: Theme) => css`
-  display: flex;
-  flex-direction: column;
-  gap: 1.6rem;
-  border: 1px solid black;
-  padding: 1.6rem 2rem;
-
-  border: none;
-  border-radius: 1.2rem;
-  justify-content: space-between;
-  height: 20rem;
-  background-color: ${theme.colors.field01};
-
-  &:hover {
-    box-shadow: 0px 4px 16px rgba(53, 59, 61, 0.08);
-  }
-`;
-
-export const postHeader = css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.8rem;
-`;
-
-export const postCardHeader = css`
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  gap: 1.6rem;
-`;
-
-export const postLocation = (theme: Theme) => css`
-  ${theme.fonts.label.medium.R13};
-  color: ${theme.colors.text03};
-`;
-
-export const postCardRightHeader = css`
-  display: flex;
-  align-items: center;
-  gap: 0.4rem;
-`;
-
-export const postViews = (theme: Theme) => css`
-  ${theme.fonts.label.medium.R13};
-  color: ${theme.colors.text03};
-`;
-
-export const postTitle = (theme: Theme) => css`
-  ${theme.fonts.title.small.SB18};
-  color: ${theme.colors.text06};
-`;
-
-export const contactedPostTag = (theme: Theme) => css`
-  width: fit-content;
-  padding: 0.6rem 0.85rem;
-  ${theme.fonts.label.small.SB12};
-  color: ${theme.colors.text05};
-  background-color: ${theme.colors.field03};
-  border-radius: 3rem;
-`;
-
-export const postRewardContainer = css`
-  display: flex;
-  gap: 0.8rem;
-  align-items: center;
-`;
-
-export const announceText = (theme: Theme) => css`
-  ${theme.fonts.label.medium.M13};
-  color: ${theme.colors.text03};
-`;
-
-export const postReward = (theme: Theme) => css`
-  ${theme.fonts.label.medium.SB13};
-  color: ${theme.colors.primaryMint};
-`;
-
-export const postDate = (theme: Theme) => css`
-  ${theme.fonts.label.medium.M13};
-  color: ${theme.colors.text04};
-`;
diff --git a/src/app/home/components/PostCard/PostCard.tsx b/src/app/home/components/PostCard/PostCard.tsx
index dd573fd..86667d9 100644
--- a/src/app/home/components/PostCard/PostCard.tsx
+++ b/src/app/home/components/PostCard/PostCard.tsx
@@ -13,7 +13,7 @@ import {
   postRewardContainer,
   postTitle,
   postViews,
-} from './PostCard.styles';
+} from './PostCard.css';
 import { formatPostDate } from '../../home.utils';
 
 import Icon from '@/components/Icon';
@@ -31,27 +31,27 @@ const PostCard = ({ post }: PostCardProps) => {
 
   return (
     <li>
-      <Link href={`/post/${experimentPostId}`} key={experimentPostId} css={postCardLayout}>
-        <div css={postHeader}>
-          <div css={postCardHeader}>
-            <span css={postLocation}>{univName}</span>
-            <div css={postCardRightHeader}>
+      <Link href={`/post/${experimentPostId}`} key={experimentPostId} className={postCardLayout}>
+        <div className={postHeader}>
+          <div className={postCardHeader}>
+            <span className={postLocation}>{univName}</span>
+            <div className={postCardRightHeader}>
               <Icon icon="Eye" width={18} />
-              <span css={postViews}>{views}</span>
+              <span className={postViews}>{views}</span>
             </div>
           </div>
-          <h3 css={postTitle}>{title}</h3>
+          <h3 className={postTitle}>{title}</h3>
         </div>
         <div>
           {recruitStatus ? (
             <>
-              <div css={postRewardContainer}>
-                <span css={announceText}>보상</span>
-                <span css={postReward}>{reward}</span>
+              <div className={postRewardContainer}>
+                <span className={announceText}>보상</span>
+                <span className={postReward}>{reward}</span>
               </div>
-              <div css={postRewardContainer}>
-                <span css={announceText}>일시</span>
-                <span css={postDate}>
+              <div className={postRewardContainer}>
+                <span className={announceText}>일시</span>
+                <span className={postDate}>
                   {formatPostDate({
                     startDate: durationInfo.startDate,
                     endDate: durationInfo.endDate,
@@ -60,7 +60,7 @@ const PostCard = ({ post }: PostCardProps) => {
               </div>
             </>
           ) : (
-            <div css={contactedPostTag}>
+            <div className={contactedPostTag}>
               <span>모집 완료</span>
             </div>
           )}
diff --git a/src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.css.ts b/src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.css.ts
new file mode 100644
index 0000000..b1ab253
--- /dev/null
+++ b/src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.css.ts
@@ -0,0 +1,30 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const emptyPostCardLayout = style({
+  display: 'flex',
+  flexDirection: 'column',
+  alignItems: 'center',
+  justifyContent: 'center',
+  gap: '1.6rem',
+  height: '30.8rem',
+});
+
+export const emptyPostCardContent = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.8rem',
+  textAlign: 'center',
+});
+
+export const emptyListTitle = style({
+  ...fonts.body.normal.M16,
+  color: colors.text04,
+});
+
+export const emptyListContent = style({
+  ...fonts.label.small.M12,
+  color: colors.text04,
+});
diff --git a/src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.styles.ts b/src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.styles.ts
deleted file mode 100644
index b613d0a..0000000
--- a/src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.styles.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const emptyPostCardLayout = css`
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  gap: 1.6rem;
-  height: 30.8rem;
-`;
-
-export const emptyPostCardContent = css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.8rem;
-  text-align: center;
-`;
-
-export const emptyListTitle = (theme: Theme) => css`
-  ${theme.fonts.body.normal.M16};
-  color: ${theme.colors.text04};
-`;
-
-export const emptyListContent = (theme: Theme) => css`
-  ${theme.fonts.label.small.M12};
-  color: ${theme.colors.text04};
-`;
diff --git a/src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.tsx b/src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.tsx
index 208770e..4682729 100644
--- a/src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.tsx
+++ b/src/app/home/components/PostCardList/EmptyPostCard/EmptyPostCard.tsx
@@ -5,17 +5,17 @@ import {
   emptyListTitle,
   emptyPostCardContent,
   emptyPostCardLayout,
-} from './EmptyPostCard.styles';
+} from './EmptyPostCard.css';
 
 import Icon from '@/components/Icon';
 
 const EmptyPostCard = () => {
   return (
-    <div css={emptyPostCardLayout}>
+    <div className={emptyPostCardLayout}>
       <Icon icon="AllEmpty" width={60} height={60} />
-      <div css={emptyPostCardContent}>
-        <h4 css={emptyListTitle}>검색 결과가 없어요</h4>
-        <div css={emptyListContent}>
+      <div className={emptyPostCardContent}>
+        <h4 className={emptyListTitle}>검색 결과가 없어요</h4>
+        <div className={emptyListContent}>
           <span>조건을 다르게 하면</span>
           <br />
           <span>좋은 공고가 더 많을지도 몰라요</span>
diff --git a/src/app/home/components/PostCardList/PostCardList.css.ts b/src/app/home/components/PostCardList/PostCardList.css.ts
new file mode 100644
index 0000000..2d5fb03
--- /dev/null
+++ b/src/app/home/components/PostCardList/PostCardList.css.ts
@@ -0,0 +1,7 @@
+import { style } from '@vanilla-extract/css';
+
+export const postCardListLayout = style({
+  display: 'grid',
+  gridTemplateColumns: '1fr 1fr 1fr',
+  gap: '1.2rem',
+});
diff --git a/src/app/home/components/PostCardList/PostCardList.styles.ts b/src/app/home/components/PostCardList/PostCardList.styles.ts
deleted file mode 100644
index 1a2fae6..0000000
--- a/src/app/home/components/PostCardList/PostCardList.styles.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { css } from '@emotion/react';
-
-export const postCardListLayout = css`
-  display: grid;
-  grid-template-columns: 1fr 1fr 1fr;
-  gap: 1.2rem;
-`;
diff --git a/src/app/home/components/PostCardList/PostCardList.tsx b/src/app/home/components/PostCardList/PostCardList.tsx
index aed1284..0595bc4 100644
--- a/src/app/home/components/PostCardList/PostCardList.tsx
+++ b/src/app/home/components/PostCardList/PostCardList.tsx
@@ -1,6 +1,6 @@
-import { postCardListLayout } from './PostCardList.styles';
 import PostCard from '../PostCard/PostCard';
 import EmptyPostCard from './EmptyPostCard/EmptyPostCard';
+import { postCardListLayout } from './PostCardList.css';
 
 import { Post } from '@/types/post';
 
@@ -18,7 +18,7 @@ const PostCardList = ({ postList }: PostCardListProps) => {
   }
 
   return (
-    <ul css={postCardListLayout}>
+    <ul className={postCardListLayout}>
       {postList.map((post) => (
         <PostCard key={post.postInfo.experimentPostId} post={post} />
       ))}
diff --git a/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.css.ts b/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.css.ts
new file mode 100644
index 0000000..645851a
--- /dev/null
+++ b/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.css.ts
@@ -0,0 +1,52 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const checkboxLayout = style({
+  display: 'flex',
+  justifyContent: 'space-between',
+  alignItems: 'center',
+});
+
+export const checkboxWrapper = style({
+  display: 'flex',
+  alignItems: 'center',
+  gap: '0.8rem',
+  selectors: {
+    '&:span': {
+      userSelect: 'none',
+    },
+  },
+});
+
+export const allCheckWrapper = style({
+  borderBottom: `0.1rem solid ${colors.line01}`,
+  paddingBottom: '1.6rem',
+});
+
+export const checkbox = style({
+  position: 'absolute',
+  opacity: 0,
+  pointerEvents: 'none',
+});
+
+export const requiredCheckboxText = style({
+  color: colors.textPrimary,
+});
+
+export const labelWrapper = style({
+  display: 'flex',
+  gap: '0.4rem',
+});
+
+export const tipWrapper = style({
+  ...fonts.label.small.M12,
+  display: 'flex',
+  gap: '0.4rem',
+  color: colors.text02,
+});
+
+export const tipAlert = style({
+  color: colors.textPrimary,
+});
diff --git a/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.tsx b/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.tsx
index e0f0551..f64b077 100644
--- a/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.tsx
+++ b/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.tsx
@@ -1,12 +1,13 @@
 import {
-  allCheckWrapper,
-  checkbox,
   checkboxLayout,
   checkboxWrapper,
-  labelWrapper,
+  allCheckWrapper,
+  checkbox,
   requiredCheckboxText,
-} from './JoinCheckbox.styles';
-import { tipAlert, tipWrapper } from '../../JoinInput/JoinInput.styles';
+  labelWrapper,
+  tipWrapper,
+  tipAlert,
+} from './JoinCheckbox.css';
 
 import Icon from '@/components/Icon';
 import theme from '@/styles/theme';
@@ -32,27 +33,26 @@ const JoinCheckbox = ({
   isAlert,
 }: JoinCheckboxProps) => {
   return (
-    <div css={[checkboxLayout, isAllCheck && allCheckWrapper]}>
-      <label css={checkboxWrapper}>
-        <input css={checkbox} type="checkbox" checked={isChecked} onChange={onChange} />
+    <div className={`${checkboxLayout} ${isAllCheck ? allCheckWrapper : ''}`}>
+      <label className={checkboxWrapper}>
+        <input className={checkbox} type="checkbox" checked={isChecked} onChange={onChange} />
         {isChecked ? (
           <Icon icon="CheckSquareFill" color={theme.colors.primaryMint} />
         ) : (
           <Icon icon="CheckSquareEmpty" />
         )}
         <div>
-          <div css={labelWrapper}>
-            {isRequired && <span css={requiredCheckboxText}>[필수]</span>}
+          <div className={labelWrapper}>
+            {isRequired && <span className={requiredCheckboxText}>[필수]</span>}
             <span>{label}</span>
           </div>
           {isAlert && (
-            <div css={tipWrapper}>
-              <span css={tipAlert}>* 내가 참여할 수 있는 실험 알림을 보내드려요</span>
+            <div className={tipWrapper}>
+              <span className={tipAlert}>* 내가 참여할 수 있는 실험 알림을 보내드려요</span>
             </div>
           )}
         </div>
       </label>
-
       {isArrow && <Icon icon="Chevron" width={20} height={20} />}
     </div>
   );
diff --git a/src/app/page.tsx b/src/app/page.tsx
index ea34a95..750fe02 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,4 +1,3 @@
-
 import Banner from './home/components/Banner/Banner';
 import PostContainer from './home/components/PostContainer/PostContainer';
 
diff --git a/src/components/Icon/icons/AllEmpty.tsx b/src/components/Icon/icons/AllEmpty.tsx
index 1db0b07..2b182ba 100644
--- a/src/components/Icon/icons/AllEmpty.tsx
+++ b/src/components/Icon/icons/AllEmpty.tsx
@@ -5,11 +5,12 @@ import theme from '@/styles/theme';
 function AllEmpty(props: SVGProps<SVGSVGElement>) {
   return (
     <svg
-      width={props.width}
-      height={props.height}
+      width="24"
+      height="24"
       viewBox="0 0 24 24"
       fill="none"
       xmlns="http://www.w3.org/2000/svg"
+      {...props}
     >
       <path
         fillRule="evenodd"

From 6372d78c2393bf10fc0c1070f6c016ab0fd8c350 Mon Sep 17 00:00:00 2001
From: Gyuhan Park <rbgks1937@naver.com>
Date: Tue, 4 Feb 2025 04:29:37 +0900
Subject: [PATCH 04/11] =?UTF-8?q?[YS-243]=20refactor:=20=ED=9A=8C=EC=9B=90?=
 =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EA=B3=B5=ED=86=B5=20=EC=BB=B4=ED=8F=AC?=
 =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=EB=A7=81=20?=
 =?UTF-8?q?=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/join/JoinPage.css.ts                  | 65 ++++++++++++++++
 src/app/join/JoinPage.styles.ts               | 48 ------------
 .../components/EmailToast/EmailToast.css.ts   | 37 +++++++++
 .../EmailToast/EmailToast.styles.ts           | 43 ----------
 .../join/components/EmailToast/EmailToast.tsx | 10 ++-
 .../JoinCheckbox/JoinCheckbox.css.ts          |  6 +-
 .../JoinCheckbox/JoinCheckbox.styles.ts       | 37 ---------
 .../JoinCheckboxContainer.css.ts              | 16 ++++
 .../JoinCheckboxContainer.styles.ts           | 13 ----
 .../JoinCheckboxContainer.tsx                 |  4 +-
 .../components/JoinInput/JoinInput.css.ts     | 78 +++++++++++++++++++
 .../components/JoinInput/JoinInput.styles.ts  | 78 -------------------
 .../join/components/JoinInput/JoinInput.tsx   | 35 +++++----
 .../JoinSuccessStep/JoinSuccessStep.css.ts    | 39 ++++++++++
 .../JoinSuccessStep/JoinSuccessStep.styles.ts | 36 ---------
 .../JoinSuccessStep/JoinSuccessStep.tsx       | 14 ++--
 src/app/join/page.tsx                         | 30 +++++--
 17 files changed, 294 insertions(+), 295 deletions(-)
 create mode 100644 src/app/join/JoinPage.css.ts
 create mode 100644 src/app/join/components/EmailToast/EmailToast.css.ts
 delete mode 100644 src/app/join/components/EmailToast/EmailToast.styles.ts
 delete mode 100644 src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.styles.ts
 create mode 100644 src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.css.ts
 delete mode 100644 src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.styles.ts
 create mode 100644 src/app/join/components/JoinInput/JoinInput.css.ts
 delete mode 100644 src/app/join/components/JoinInput/JoinInput.styles.ts
 create mode 100644 src/app/join/components/JoinSuccessStep/JoinSuccessStep.css.ts
 delete mode 100644 src/app/join/components/JoinSuccessStep/JoinSuccessStep.styles.ts

diff --git a/src/app/join/JoinPage.css.ts b/src/app/join/JoinPage.css.ts
new file mode 100644
index 0000000..5ddbc56
--- /dev/null
+++ b/src/app/join/JoinPage.css.ts
@@ -0,0 +1,65 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const joinLayout = style({
+  display: 'flex',
+  flexDirection: 'column',
+  alignItems: 'center',
+  gap: '4rem',
+  paddingTop: '8.4rem',
+  flexGrow: 1,
+});
+
+export const contentContainer = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '1.6rem',
+  width: '100%',
+});
+
+export const titleContainer = style({
+  display: 'flex',
+  justifyContent: 'space-between',
+  alignItems: 'center',
+});
+
+export const joinContentContainer = style({
+  backgroundColor: colors.field02,
+  width: '100%',
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '2.8rem',
+  borderRadius: '1.2rem',
+  padding: '3.2rem 4rem',
+});
+
+export const joinTitle = style({
+  ...fonts.title.medium.SB20,
+  color: colors.text06,
+});
+
+export const progressBarContainer = style({
+  width: '8rem',
+  height: '0.6rem',
+  backgroundColor: colors.field03,
+  borderRadius: '0.6rem',
+});
+
+export const progressBarFill = style({
+  width: 'var(--progress-width)',
+  height: '100%',
+  backgroundColor: colors.primaryMint,
+  borderRadius: '0.6rem',
+  transition: 'width 1s',
+});
+
+export const joinForm = style({
+  width: '100%',
+  height: '100%',
+  display: 'flex',
+  flexDirection: 'column',
+  alignItems: 'center',
+  gap: '4rem',
+});
diff --git a/src/app/join/JoinPage.styles.ts b/src/app/join/JoinPage.styles.ts
index 61b8884..d639fba 100644
--- a/src/app/join/JoinPage.styles.ts
+++ b/src/app/join/JoinPage.styles.ts
@@ -1,27 +1,5 @@
 import { css, Theme } from '@emotion/react';
 
-export const joinLayout = css`
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  gap: 4rem;
-  padding-top: 8.4rem;
-  flex-grow: 1;
-`;
-
-export const contentContainer = css`
-  display: flex;
-  flex-direction: column;
-  gap: 1.6rem;
-  width: 100%;
-`;
-
-export const titleContainer = css`
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-`;
-
 export const joinContentContainer = (theme: Theme) => css`
   background-color: ${theme.colors.field02};
   width: 100%;
@@ -32,32 +10,6 @@ export const joinContentContainer = (theme: Theme) => css`
   padding: 3.2rem 4rem;
 `;
 
-export const joinTitle = (theme: Theme) => css`
-  ${theme.fonts.title.medium.SB20};
-  color: ${theme.colors.text06};
-`;
-
-export const progressBar = (theme: Theme) => css`
-  width: 8rem;
-  height: 0.6rem;
-  background-color: ${theme.colors.field03};
-  border-radius: 3rem;
-`;
-
-export const progressBarContainer = (theme: Theme) => css`
-  width: 8rem;
-  height: 0.6rem;
-  background-color: ${theme.colors.field03};
-  border-radius: 0.6rem;
-`;
-
-export const progressBarFill = (theme: Theme) => css`
-  height: 100%;
-  background-color: ${theme.colors.primaryMint};
-  border-radius: 0.6rem;
-  transition: width 1s;
-`;
-
 export const joinForm = css`
   width: 100%;
   height: 100%;
diff --git a/src/app/join/components/EmailToast/EmailToast.css.ts b/src/app/join/components/EmailToast/EmailToast.css.ts
new file mode 100644
index 0000000..a5fcee9
--- /dev/null
+++ b/src/app/join/components/EmailToast/EmailToast.css.ts
@@ -0,0 +1,37 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+import theme from '@/styles/theme';
+
+export const toastLayout = style({
+  color: colors.text06,
+  height: '5.2rem',
+  backgroundColor: colors.fieldToast,
+  boxShadow: '0px 4px 16px rgba(53, 59, 61, 0.2)',
+  borderRadius: '8rem',
+  padding: '1.4rem 2.4rem',
+  position: 'fixed',
+  top: 0,
+  left: '50%',
+  transform: 'translateX(-50%)',
+  zIndex: theme.zIndex.toastContent,
+});
+
+export const toastTitle = style({
+  ...fonts.body.normal.SB16,
+  color: colors.text01,
+  display: 'flex',
+  flexFlow: 'row nowrap',
+  alignItems: 'center',
+  gap: '1rem',
+  width: 'max-content',
+});
+
+export const toastViewport = style({
+  position: 'fixed',
+  top: '6rem',
+  left: '50%',
+  transform: 'translateX(-50%)',
+  zIndex: theme.zIndex.toastViewport,
+});
diff --git a/src/app/join/components/EmailToast/EmailToast.styles.ts b/src/app/join/components/EmailToast/EmailToast.styles.ts
deleted file mode 100644
index 362ac22..0000000
--- a/src/app/join/components/EmailToast/EmailToast.styles.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const toastLayout = (theme: Theme) => css`
-  color: ${theme.colors.text06};
-
-  height: 5.2rem;
-
-  background-color: ${theme.colors.fieldToast};
-  color: ${theme.colors.text06};
-  box-shadow: 0px 4px 16px rgba(53, 59, 61, 0.2);
-
-  border-radius: 8rem;
-
-  padding: 1.4rem 2.4rem;
-
-  position: fixed;
-  top: 0;
-  left: 50%;
-  transform: translateX(-50%);
-
-  z-index: ${theme.zIndex.toastContent};
-`;
-
-export const toastTitle = (theme: Theme) => css`
-  ${theme.fonts.body.normal.SB16};
-  color: ${theme.colors.text01};
-
-  display: flex;
-  flex-flow: row nowrap;
-  align-items: center;
-  gap: 1rem;
-
-  width: max-content;
-`;
-
-export const toastViewport = (theme: Theme) => css`
-  position: fixed;
-  top: 6rem;
-  left: 50%;
-  transform: translateX(-50%);
-
-  z-index: ${theme.zIndex.toastViewport};
-`;
diff --git a/src/app/join/components/EmailToast/EmailToast.tsx b/src/app/join/components/EmailToast/EmailToast.tsx
index ebfbf6d..b9bd973 100644
--- a/src/app/join/components/EmailToast/EmailToast.tsx
+++ b/src/app/join/components/EmailToast/EmailToast.tsx
@@ -1,6 +1,8 @@
+'use client';
+
 import * as Toast from '@radix-ui/react-toast';
 
-import { toastLayout, toastTitle, toastViewport } from './EmailToast.styles';
+import { toastLayout, toastTitle, toastViewport } from './EmailToast.css';
 
 import Icon from '@/components/Icon';
 import theme from '@/styles/theme';
@@ -15,17 +17,17 @@ const EmailToast = ({ title, isToastOpen, setIsToastOpen }: EmailToastProps) =>
   return (
     <Toast.Provider swipeDirection="up">
       <Toast.Root
-        css={toastLayout}
+        className={toastLayout}
         open={isToastOpen}
         onOpenChange={setIsToastOpen}
         duration={1500}
       >
-        <Toast.Title css={toastTitle}>
+        <Toast.Title className={toastTitle}>
           <Icon icon="CheckRound" color={theme.colors.primaryMint} width={24} height={24} />
           <p>{title}</p>
         </Toast.Title>
       </Toast.Root>
-      <Toast.Viewport css={toastViewport} />
+      <Toast.Viewport className={toastViewport} />
     </Toast.Provider>
   );
 };
diff --git a/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.css.ts b/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.css.ts
index 645851a..ea75858 100644
--- a/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.css.ts
+++ b/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.css.ts
@@ -13,11 +13,7 @@ export const checkboxWrapper = style({
   display: 'flex',
   alignItems: 'center',
   gap: '0.8rem',
-  selectors: {
-    '&:span': {
-      userSelect: 'none',
-    },
-  },
+  userSelect: 'none',
 });
 
 export const allCheckWrapper = style({
diff --git a/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.styles.ts b/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.styles.ts
deleted file mode 100644
index a85cfcb..0000000
--- a/src/app/join/components/JoinCheckboxContainer/JoinCheckbox/JoinCheckbox.styles.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const checkboxLayout = css`
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-`;
-
-export const checkboxWrapper = css`
-  display: flex;
-  align-items: center;
-  gap: 0.8rem;
-
-  span {
-    user-select: none;
-  }
-`;
-
-export const allCheckWrapper = (theme: Theme) => css`
-  border-bottom: 0.1rem solid ${theme.colors.line01};
-  padding-bottom: 1.6rem;
-`;
-
-export const checkbox = css`
-  position: absolute;
-  opacity: 0;
-  pointer-events: none;
-`;
-
-export const requiredCheckboxText = (theme: Theme) => css`
-  color: ${theme.colors.textPrimary};
-`;
-
-export const labelWrapper = css`
-  display: flex;
-  gap: 0.4rem;
-`;
diff --git a/src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.css.ts b/src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.css.ts
new file mode 100644
index 0000000..37bb1b2
--- /dev/null
+++ b/src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.css.ts
@@ -0,0 +1,16 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const termContainer = style({
+  ...fonts.body.normal.M16,
+  color: colors.text06,
+  backgroundColor: colors.field01,
+  border: `0.1rem solid ${colors.line01}`,
+  borderRadius: '1.2rem',
+  display: 'flex',
+  flexDirection: 'column',
+  padding: '1.6rem',
+  gap: '1.2rem',
+});
diff --git a/src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.styles.ts b/src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.styles.ts
deleted file mode 100644
index f0fd978..0000000
--- a/src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.styles.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const termContainer = (theme: Theme) => css`
-  ${theme.fonts.body.normal.M16};
-  color: ${theme.colors.text06};
-  background-color: ${theme.colors.field01};
-  border: 0.1rem solid ${theme.colors.line01};
-  border-radius: 1.2rem;
-  display: flex;
-  flex-direction: column;
-  padding: 1.6rem;
-  gap: 1.2rem;
-`;
diff --git a/src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.tsx b/src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.tsx
index 519a3be..8d80967 100644
--- a/src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.tsx
+++ b/src/app/join/components/JoinCheckboxContainer/JoinCheckboxContainer.tsx
@@ -1,6 +1,6 @@
 import { ServiceAgreeCheck } from '../../JoinPage.types';
 import JoinCheckbox from './JoinCheckbox/JoinCheckbox';
-import { termContainer } from './JoinCheckboxContainer.styles';
+import { termContainer } from './JoinCheckboxContainer.css';
 
 interface JoinCheckboxContainerProps {
   serviceAgreeCheck: ServiceAgreeCheck;
@@ -18,7 +18,7 @@ const JoinCheckboxContainer = ({
   const isAllCheck = isTermOfService && isPrivacy && isAdvertise && (isRecommend ?? true);
 
   return (
-    <div css={termContainer}>
+    <div className={termContainer}>
       <JoinCheckbox
         label="이용약관에 모두 동의합니다"
         isChecked={isAllCheck}
diff --git a/src/app/join/components/JoinInput/JoinInput.css.ts b/src/app/join/components/JoinInput/JoinInput.css.ts
new file mode 100644
index 0000000..bf8dc34
--- /dev/null
+++ b/src/app/join/components/JoinInput/JoinInput.css.ts
@@ -0,0 +1,78 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const inputContainer = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.6rem',
+});
+
+export const inputLabel = style({
+  ...fonts.label.large.M14,
+  color: colors.text06,
+  display: 'flex',
+  gap: '0.4rem',
+});
+
+export const joinInput = style({
+  ...fonts.body.normal.M16,
+  color: colors.text06,
+  border: `0.1rem solid ${colors.line01}`,
+  padding: '1.6rem',
+  borderRadius: '1.2rem',
+  selectors: {
+    '&:disabled': {
+      color: colors.text03,
+      backgroundColor: colors.field03,
+    },
+    '&::placeholder': {
+      color: colors.text03,
+    },
+    '&:focus': {
+      outline: `0.1rem solid ${colors.lineTinted}`,
+    },
+    "&[aria-invalid='true']": {
+      outline: `0.1rem solid ${colors.textAlert}`,
+    },
+  },
+});
+
+export const requiredStar = style({
+  color: colors.textAlert,
+});
+
+export const inputWrapper = style({
+  position: 'relative',
+});
+
+export const inputResetButton = style({
+  position: 'absolute',
+  top: '50%',
+  transform: 'translateY(-50%)',
+  right: '1.7rem',
+});
+
+export const errorMessage = style({
+  ...fonts.label.large.R14,
+  color: colors.textAlert,
+});
+
+export const tipWrapper = style({
+  ...fonts.label.small.M12,
+  display: 'flex',
+  gap: '0.4rem',
+  color: colors.text02,
+});
+
+export const textCount = style({
+  ...fonts.label.small.M12,
+  display: 'flex',
+  justifyContent: 'flex-end',
+  color: colors.text02,
+});
+
+export const tipAlert = style({
+  color: colors.textPrimary,
+});
diff --git a/src/app/join/components/JoinInput/JoinInput.styles.ts b/src/app/join/components/JoinInput/JoinInput.styles.ts
deleted file mode 100644
index 19eb2c0..0000000
--- a/src/app/join/components/JoinInput/JoinInput.styles.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const inputContainer = (theme: Theme) => css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.6rem;
-
-  label {
-    ${theme.fonts.label.large.M14};
-    color: ${theme.colors.text06};
-    display: flex;
-    gap: 0.4rem;
-  }
-
-  input,
-  textarea {
-    ${theme.fonts.body.normal.M16};
-    color: ${theme.colors.text06};
-    border: 0.1rem solid ${theme.colors.line01};
-    padding: 1.6rem;
-    border-radius: 1.2rem;
-
-    :disabled {
-      color: ${theme.colors.text03};
-      background-color: ${theme.colors.field03};
-    }
-
-    ::placeholder {
-      color: ${theme.colors.text03};
-    }
-
-    :focus {
-      outline: 0.1rem solid ${theme.colors.lineTinted};
-    }
-
-    &[aria-invalid='true'] {
-      outline: 0.1rem solid ${theme.colors.textAlert};
-    }
-  }
-`;
-
-export const requiredStar = (theme: Theme) => css`
-  color: ${theme.colors.textAlert};
-`;
-
-export const inputWrapper = css`
-  position: relative;
-`;
-
-export const inputResetButton = css`
-  position: absolute;
-  top: 50%;
-  transform: translateY(-50%);
-  right: 1.7rem;
-`;
-
-export const errorMessage = (theme: Theme) => css`
-  ${theme.fonts.label.large.R14};
-  color: ${theme.colors.textAlert};
-`;
-
-export const tipWrapper = (theme: Theme) => css`
-  ${theme.fonts.label.small.M12};
-  display: flex;
-  gap: 0.4rem;
-  color: ${theme.colors.text02};
-`;
-
-export const textCount = (theme: Theme) => css`
-  ${theme.fonts.label.small.M12};
-  display: flex;
-  justify-content: flex-end;
-  color: ${theme.colors.text02};
-`;
-
-export const tipAlert = (theme: Theme) => css`
-  color: ${theme.colors.textPrimary};
-`;
diff --git a/src/app/join/components/JoinInput/JoinInput.tsx b/src/app/join/components/JoinInput/JoinInput.tsx
index 525a06f..7434537 100644
--- a/src/app/join/components/JoinInput/JoinInput.tsx
+++ b/src/app/join/components/JoinInput/JoinInput.tsx
@@ -1,16 +1,20 @@
+'use client';
+
 import { useRef, useState } from 'react';
 import { Control, Controller, FieldValues, Path, PathValue } from 'react-hook-form';
 
 import {
-  errorMessage,
   inputContainer,
   inputResetButton,
-  inputWrapper,
   requiredStar,
+  errorMessage,
+  tipWrapper,
   textCount,
   tipAlert,
-  tipWrapper,
-} from './JoinInput.styles';
+  joinInput,
+  inputWrapper,
+  inputLabel,
+} from './JoinInput.css';
 
 import Icon from '@/components/Icon';
 
@@ -55,7 +59,6 @@ const JoinInput = <T extends FieldValues>({
     if (resetButtonRef.current && resetButtonRef.current.contains(e.relatedTarget)) {
       return;
     }
-
     onBlur();
     setIsFocused(false);
   };
@@ -67,11 +70,11 @@ const JoinInput = <T extends FieldValues>({
   };
 
   return (
-    <div css={inputContainer}>
+    <div className={inputContainer}>
       {label && (
-        <label>
+        <label className={inputLabel}>
           <span>{label}</span>
-          {required && <span css={requiredStar}>*</span>}
+          {required && <span className={requiredStar}>*</span>}
         </label>
       )}
       <Controller
@@ -81,7 +84,7 @@ const JoinInput = <T extends FieldValues>({
         defaultValue={value}
         render={({ field, fieldState }) => (
           <>
-            <div css={inputWrapper}>
+            <div className={inputWrapper}>
               {type === 'input' ? (
                 <input
                   {...field}
@@ -91,6 +94,7 @@ const JoinInput = <T extends FieldValues>({
                   maxLength={maxLength}
                   aria-invalid={fieldState.invalid ? true : false}
                   style={{ width: '100%' }}
+                  className={joinInput}
                   onFocus={() => setIsFocused(true)}
                   onBlur={(e) => handleBlur(e, field.onBlur)}
                 />
@@ -104,12 +108,13 @@ const JoinInput = <T extends FieldValues>({
                   rows={3}
                   maxLength={maxLength ?? 0}
                   style={{ width: '100%' }}
+                  className={joinInput}
                   onFocus={() => setIsFocused(true)}
                   onBlur={(e) => handleBlur(e, field.onBlur)}
                 />
               )}
               {isFocused && field.value && !disabled && (
-                <button css={inputResetButton} ref={resetButtonRef}>
+                <button className={inputResetButton} ref={resetButtonRef}>
                   <Icon
                     icon="CloseRound"
                     width={22}
@@ -120,15 +125,15 @@ const JoinInput = <T extends FieldValues>({
                 </button>
               )}
             </div>
-            {fieldState.error && <span css={errorMessage}>{fieldState.error.message}</span>}
+            {fieldState.error && <span className={errorMessage}>{fieldState.error.message}</span>}
             {type === 'textarea' && (
-              <span css={textCount}>
+              <span className={textCount}>
                 {field.value?.length || 0}/{maxLength}
               </span>
             )}
-            {tip && Boolean(!fieldState.error) && (
-              <div css={tipWrapper}>
-                {isTip && <span css={tipAlert}>Tip</span>}
+            {tip && !fieldState.error && (
+              <div className={tipWrapper}>
+                {isTip && <span className={tipAlert}>Tip</span>}
                 <span>{tip}</span>
               </div>
             )}
diff --git a/src/app/join/components/JoinSuccessStep/JoinSuccessStep.css.ts b/src/app/join/components/JoinSuccessStep/JoinSuccessStep.css.ts
new file mode 100644
index 0000000..16d2998
--- /dev/null
+++ b/src/app/join/components/JoinSuccessStep/JoinSuccessStep.css.ts
@@ -0,0 +1,39 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const joinSuccessLayout = style({
+  display: 'flex',
+  flexDirection: 'column',
+  alignItems: 'center',
+  justifyContent: 'center',
+  gap: '9rem',
+  paddingTop: '3.6rem',
+});
+
+export const joinTitleContainer = style({
+  display: 'flex',
+  flexDirection: 'column',
+  alignItems: 'center',
+  gap: '6.4rem',
+  textAlign: 'center',
+});
+
+export const title = style({
+  ...fonts.title.large.SB28,
+  color: colors.text06,
+});
+
+export const image = style({
+  margin: '3.2rem 0',
+});
+
+export const homeLink = style({
+  ...fonts.body.normal.SB16,
+  backgroundColor: colors.primaryMint,
+  color: colors.text01,
+  borderRadius: '1.2rem',
+  padding: '1.2rem 3.35rem',
+  marginTop: '2.6rem',
+});
diff --git a/src/app/join/components/JoinSuccessStep/JoinSuccessStep.styles.ts b/src/app/join/components/JoinSuccessStep/JoinSuccessStep.styles.ts
deleted file mode 100644
index 8b3fb2a..0000000
--- a/src/app/join/components/JoinSuccessStep/JoinSuccessStep.styles.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const joinSuccessLayout = css`
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  gap: 9rem;
-  padding-top: 3.6rem;
-`;
-
-export const joinTitleContainer = css`
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  gap: 6.4rem;
-  text-align: center;
-`;
-
-export const title = (theme: Theme) => css`
-  ${theme.fonts.title.large.SB28};
-  color: ${theme.colors.text06};
-`;
-
-export const image = css`
-  margin: 3.2rem 0;
-`;
-
-export const homeLink = (theme: Theme) => css`
-  ${theme.fonts.body.normal.SB16};
-  background-color: ${theme.colors.primaryMint};
-  color: ${theme.colors.text01};
-  border-radius: 1.2rem;
-  padding: 1.2rem 3.35rem;
-  margin-top: 2.6rem;
-`;
diff --git a/src/app/join/components/JoinSuccessStep/JoinSuccessStep.tsx b/src/app/join/components/JoinSuccessStep/JoinSuccessStep.tsx
index 1656d15..df13d16 100644
--- a/src/app/join/components/JoinSuccessStep/JoinSuccessStep.tsx
+++ b/src/app/join/components/JoinSuccessStep/JoinSuccessStep.tsx
@@ -8,7 +8,7 @@ import {
   joinSuccessLayout,
   joinTitleContainer,
   title,
-} from './JoinSuccessStep.styles';
+} from './JoinSuccessStep.css';
 
 import JoinSuccess from '@/assets/images/joinSuccess.svg';
 
@@ -18,15 +18,15 @@ interface JoinSuccessStepProps {
 
 const JoinSuccessStep = ({ name }: JoinSuccessStepProps) => {
   return (
-    <div css={joinSuccessLayout}>
-      <div css={joinTitleContainer}>
+    <div className={joinSuccessLayout}>
+      <div className={joinTitleContainer}>
         <div>
-          <h2 css={title}>{name} 님,</h2>
-          <h2 css={title}>그라밋 가입을 환영해요!</h2>
+          <h2 className={title}>{name} 님,</h2>
+          <h2 className={title}>그라밋 가입을 환영해요!</h2>
         </div>
-        <Image src={JoinSuccess} alt="회원가입 성공" width={160} height={140} css={image} />
+        <Image src={JoinSuccess} alt="회원가입 성공" width={160} height={140} className={image} />
       </div>
-      <Link href="/" css={homeLink}>
+      <Link href="/" className={homeLink}>
         홈 화면으로 돌아가기
       </Link>
     </div>
diff --git a/src/app/join/page.tsx b/src/app/join/page.tsx
index df1f882..9a19748 100644
--- a/src/app/join/page.tsx
+++ b/src/app/join/page.tsx
@@ -1,5 +1,6 @@
 'use client';
 
+import { assignInlineVars } from '@vanilla-extract/dynamic';
 import Image from 'next/image';
 
 import ParticipantForm from './components/Participant/ParticipantForm';
@@ -13,7 +14,7 @@ import {
   progressBarContainer,
   progressBarFill,
   titleContainer,
-} from './JoinPage.styles';
+} from './JoinPage.css';
 
 import Logo from '@/assets/images/logo.svg';
 import { ROLE } from '@/constants/config';
@@ -26,16 +27,31 @@ export default function JoinPage() {
   // TODO: 추후 스켈레톤 처리
   if (!role) return null;
 
+  if (step === STEP.success) {
+    return (
+      <section className={joinLayout}>
+        <div className={contentContainer}>
+          {role === ROLE.researcher ? <ResearcherForm /> : <ParticipantForm />}
+        </div>
+      </section>
+    );
+  }
+
   return (
-    <section css={joinLayout}>
+    <section className={joinLayout}>
       <Image src={Logo} alt="로고" width={80} height={28} />
-      <div css={contentContainer}>
-        <div css={titleContainer}>
-          <h2 css={joinTitle}>
+      <div className={contentContainer}>
+        <div className={titleContainer}>
+          <h2 className={joinTitle}>
             {role === ROLE.researcher ? '연구자 회원가입' : '참여자 회원가입'}
           </h2>
-          <div css={progressBarContainer}>
-            <div css={progressBarFill} style={{ width: step === STEP.email ? '50%' : '100%' }} />
+          <div className={progressBarContainer}>
+            <div
+              className={progressBarFill}
+              style={assignInlineVars({
+                '--progress-width': step === STEP.email ? '50%' : '100%',
+              })}
+            />
           </div>
         </div>
         {role === ROLE.researcher ? <ResearcherForm /> : <ParticipantForm />}

From c8750687e19f860528da000fc3de0ee30ca8ffff Mon Sep 17 00:00:00 2001
From: Gyuhan Park <rbgks1937@naver.com>
Date: Wed, 5 Feb 2025 00:51:31 +0900
Subject: [PATCH 05/11] =?UTF-8?q?[YS-243]=20refactor:=20=EB=A1=9C=EA=B7=B8?=
 =?UTF-8?q?=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=8A=A4=ED=83=80?=
 =?UTF-8?q?=EC=9D=BC=EB=A7=81=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88?=
 =?UTF-8?q?=EC=9D=B4=EC=85=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/login/LoginPage.css.ts                | 44 +++++++++++++++++
 src/app/login/LoginPage.styles.ts             | 34 --------------
 src/app/login/components/LoginCard.styles.ts  | 43 -----------------
 .../components/LoginCard/LoginCard.css.ts     | 47 +++++++++++++++++++
 .../components/{ => LoginCard}/LoginCard.tsx  | 30 +++++++-----
 src/app/login/google/GoogleLoginPage.css.ts   |  5 ++
 .../login/google/GoogleLoginPage.styles.ts    |  5 --
 src/app/login/google/page.tsx                 |  4 +-
 src/app/login/layout.tsx                      | 14 +-----
 src/app/login/naver/NaverLoginPage.css.ts     |  5 ++
 src/app/login/naver/NaverLoginPage.styles.ts  |  5 --
 src/app/login/naver/page.tsx                  |  6 +--
 src/app/login/page.tsx                        | 18 ++++---
 13 files changed, 133 insertions(+), 127 deletions(-)
 create mode 100644 src/app/login/LoginPage.css.ts
 delete mode 100644 src/app/login/LoginPage.styles.ts
 delete mode 100644 src/app/login/components/LoginCard.styles.ts
 create mode 100644 src/app/login/components/LoginCard/LoginCard.css.ts
 rename src/app/login/components/{ => LoginCard}/LoginCard.tsx (74%)
 create mode 100644 src/app/login/google/GoogleLoginPage.css.ts
 delete mode 100644 src/app/login/google/GoogleLoginPage.styles.ts
 create mode 100644 src/app/login/naver/NaverLoginPage.css.ts
 delete mode 100644 src/app/login/naver/NaverLoginPage.styles.ts

diff --git a/src/app/login/LoginPage.css.ts b/src/app/login/LoginPage.css.ts
new file mode 100644
index 0000000..d489d71
--- /dev/null
+++ b/src/app/login/LoginPage.css.ts
@@ -0,0 +1,44 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const loginLayout = style({
+  backgroundColor: colors.field01,
+  minWidth: '100rem',
+  height: 'calc(100vh - 12.2rem)',
+  margin: '0 auto',
+});
+
+export const loginPageLayout = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '6.5rem',
+  paddingTop: '8.4rem',
+});
+
+export const descriptionWrapper = style({
+  ...fonts.title.medium.SB20,
+  color: colors.text06,
+});
+
+export const sloganContainer = style({
+  display: 'flex',
+  flexDirection: 'column',
+  alignItems: 'center',
+  gap: '1rem',
+});
+
+export const sloganWrapper = style({
+  ...fonts.title.medium.SB20,
+  color: colors.text06,
+  textAlign: 'center',
+});
+
+export const loginCardContainer = style({
+  display: 'flex',
+  justifyContent: 'center',
+  gap: '2rem',
+  marginBottom: '2.8rem',
+  minHeight: '37rem',
+});
diff --git a/src/app/login/LoginPage.styles.ts b/src/app/login/LoginPage.styles.ts
deleted file mode 100644
index 0a47687..0000000
--- a/src/app/login/LoginPage.styles.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const loginLayout = css`
-  display: flex;
-  flex-direction: column;
-  gap: 6.5rem;
-  padding-top: 8.4rem;
-`;
-
-export const descriptionWrapper = (theme: Theme) => css`
-  ${theme.fonts.title.medium.SB20};
-  color: ${theme.colors.text06};
-`;
-
-export const sloganContainer = css`
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  gap: 1rem;
-`;
-
-export const sloganWrapper = (theme: Theme) => css`
-  ${theme.fonts.title.medium.SB20};
-  color: ${theme.colors.text06};
-  text-align: center;
-`;
-
-export const loginCardContainer = css`
-  display: flex;
-  justify-content: center;
-  gap: 2rem;
-  margin-bottom: 2.8rem;
-  min-height: 37rem;
-`;
diff --git a/src/app/login/components/LoginCard.styles.ts b/src/app/login/components/LoginCard.styles.ts
deleted file mode 100644
index 89db78a..0000000
--- a/src/app/login/components/LoginCard.styles.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const loginCardLayout = (theme: Theme) => css`
-  background-color: ${theme.colors.field02};
-  width: 32.7rem;
-
-  display: flex;
-  flex-direction: column;
-  border-radius: 1.2rem;
-  padding: 3rem;
-  justify-content: space-between;
-`;
-
-export const cardTitleContainer = css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.8rem;
-`;
-
-export const badge = (theme: Theme) => css`
-  ${theme.fonts.label.large.SB14};
-  width: fit-content;
-  padding: 0.4rem 0.8rem;
-  border-radius: 1.2rem;
-`;
-
-export const buttonContainer = css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.8rem;
-`;
-
-export const loginButton = (theme: Theme) => css`
-  ${theme.fonts.label.large.R14};
-  background-color: ${theme.colors.field01};
-  color: ${theme.colors.text06};
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  gap: 0.4rem;
-  padding: 0.8rem;
-  border-radius: 0.8rem;
-`;
diff --git a/src/app/login/components/LoginCard/LoginCard.css.ts b/src/app/login/components/LoginCard/LoginCard.css.ts
new file mode 100644
index 0000000..ee508e6
--- /dev/null
+++ b/src/app/login/components/LoginCard/LoginCard.css.ts
@@ -0,0 +1,47 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const loginCardLayout = style({
+  backgroundColor: colors.field02,
+  width: '32.7rem',
+  display: 'flex',
+  flexDirection: 'column',
+  borderRadius: '1.2rem',
+  padding: '3rem',
+  justifyContent: 'space-between',
+});
+
+export const cardTitleContainer = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.8rem',
+});
+
+export const badge = style({
+  ...fonts.label.large.SB14,
+  width: 'fit-content',
+  padding: '0.4rem 0.8rem',
+  borderRadius: '1.2rem',
+  color: 'var(--badge-color)',
+  backgroundColor: 'var(--badge-bg)',
+});
+
+export const buttonContainer = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.8rem',
+});
+
+export const loginButton = style({
+  ...fonts.label.large.R14,
+  backgroundColor: colors.field01,
+  color: colors.text06,
+  display: 'flex',
+  alignItems: 'center',
+  justifyContent: 'center',
+  gap: '0.4rem',
+  padding: '0.8rem',
+  borderRadius: '0.8rem',
+});
diff --git a/src/app/login/components/LoginCard.tsx b/src/app/login/components/LoginCard/LoginCard.tsx
similarity index 74%
rename from src/app/login/components/LoginCard.tsx
rename to src/app/login/components/LoginCard/LoginCard.tsx
index 85e2b25..7324211 100644
--- a/src/app/login/components/LoginCard.tsx
+++ b/src/app/login/components/LoginCard/LoginCard.tsx
@@ -1,3 +1,6 @@
+'use client';
+
+import { assignInlineVars } from '@vanilla-extract/dynamic';
 import Image from 'next/image';
 import { useRouter } from 'next/navigation';
 
@@ -7,8 +10,8 @@ import {
   cardTitleContainer,
   loginButton,
   loginCardLayout,
-} from './LoginCard.styles';
-import { descriptionWrapper } from '../LoginPage.styles';
+} from './LoginCard.css';
+import { descriptionWrapper } from '../../LoginPage.css';
 
 import Google from '@/assets/images/google.svg';
 import Naver from '@/assets/images/naver.svg';
@@ -40,19 +43,20 @@ const LoginCard = ({ role, description }: LoginCardProps) => {
   };
 
   return (
-    <div css={loginCardLayout}>
-      <div css={cardTitleContainer}>
+    <div className={loginCardLayout}>
+      <div className={cardTitleContainer}>
         <div
-          css={badge}
-          style={{
-            color: role === '연구자' ? theme.colors.secondaryPink : theme.colors.primaryMint,
-            backgroundColor:
+          className={badge}
+          style={assignInlineVars({
+            '--badge-color':
+              role === '연구자' ? theme.colors.secondaryPink : theme.colors.primaryMint,
+            '--badge-bg':
               role === '연구자' ? theme.colors.secondaryTinted : theme.colors.primaryTinted,
-          }}
+          })}
         >
           {role}
         </div>
-        <div css={descriptionWrapper}>
+        <div className={descriptionWrapper}>
           {description.map((text, idx) => (
             <span key={idx}>
               {text}
@@ -61,12 +65,12 @@ const LoginCard = ({ role, description }: LoginCardProps) => {
           ))}
         </div>
       </div>
-      <div css={buttonContainer}>
-        <button css={loginButton} onClick={goToLoginNaver}>
+      <div className={buttonContainer}>
+        <button className={loginButton} onClick={goToLoginNaver}>
           <Image src={Naver} alt="네이버" width={24} height={24} />
           <span>네이버 계정으로 로그인</span>
         </button>
-        <button css={loginButton} onClick={goToLoginGoogle}>
+        <button className={loginButton} onClick={goToLoginGoogle}>
           <Image src={Google} alt="구글" width={24} height={24} />
           <span>구글 계정으로 로그인</span>
         </button>
diff --git a/src/app/login/google/GoogleLoginPage.css.ts b/src/app/login/google/GoogleLoginPage.css.ts
new file mode 100644
index 0000000..750f61f
--- /dev/null
+++ b/src/app/login/google/GoogleLoginPage.css.ts
@@ -0,0 +1,5 @@
+import { style } from '@vanilla-extract/css';
+
+export const emptyLayout = style({
+  height: 'calc(100vh - 17.8rem)',
+});
diff --git a/src/app/login/google/GoogleLoginPage.styles.ts b/src/app/login/google/GoogleLoginPage.styles.ts
deleted file mode 100644
index 470c7c4..0000000
--- a/src/app/login/google/GoogleLoginPage.styles.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { css } from '@emotion/react';
-
-export const emptyLayout = css`
-  height: calc(100vh - 17.8rem);
-`;
diff --git a/src/app/login/google/page.tsx b/src/app/login/google/page.tsx
index 7f2fd44..01df6c5 100644
--- a/src/app/login/google/page.tsx
+++ b/src/app/login/google/page.tsx
@@ -3,7 +3,7 @@
 import { useSearchParams } from 'next/navigation';
 import React, { useEffect, useRef } from 'react';
 
-import { emptyLayout } from './GoogleLoginPage.styles';
+import { emptyLayout } from './GoogleLoginPage.css';
 import useGoogleLoginMutation from '../hooks/useGoogleLoginMutation';
 
 export default function GoogleLoginPage() {
@@ -23,5 +23,5 @@ export default function GoogleLoginPage() {
     }
   }, [code, role, googleLogin]);
 
-  return <div css={emptyLayout}></div>;
+  return <div className={emptyLayout}></div>;
 }
diff --git a/src/app/login/layout.tsx b/src/app/login/layout.tsx
index a8ae284..3ff28e5 100644
--- a/src/app/login/layout.tsx
+++ b/src/app/login/layout.tsx
@@ -1,17 +1,7 @@
-'use client';
-
-import { Theme } from '@emotion/react';
-import { css } from '@emotion/react';
+import { loginLayout } from './LoginPage.css';
 
 function LoginLayout({ children }: { children: React.ReactNode }) {
-  return <div css={loginLayout}>{children}</div>;
+  return <div className={loginLayout}>{children}</div>;
 }
 
-const loginLayout = (theme: Theme) => css`
-  background-color: ${theme.colors.field01};
-  min-width: 100rem;
-  height: calc(100vh - 12.2rem);
-  margin: 0 auto;
-`;
-
 export default LoginLayout;
diff --git a/src/app/login/naver/NaverLoginPage.css.ts b/src/app/login/naver/NaverLoginPage.css.ts
new file mode 100644
index 0000000..750f61f
--- /dev/null
+++ b/src/app/login/naver/NaverLoginPage.css.ts
@@ -0,0 +1,5 @@
+import { style } from '@vanilla-extract/css';
+
+export const emptyLayout = style({
+  height: 'calc(100vh - 17.8rem)',
+});
diff --git a/src/app/login/naver/NaverLoginPage.styles.ts b/src/app/login/naver/NaverLoginPage.styles.ts
deleted file mode 100644
index 470c7c4..0000000
--- a/src/app/login/naver/NaverLoginPage.styles.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { css } from '@emotion/react';
-
-export const emptyLayout = css`
-  height: calc(100vh - 17.8rem);
-`;
diff --git a/src/app/login/naver/page.tsx b/src/app/login/naver/page.tsx
index d8910be..274deb8 100644
--- a/src/app/login/naver/page.tsx
+++ b/src/app/login/naver/page.tsx
@@ -1,9 +1,9 @@
 'use client';
 
 import { useSearchParams } from 'next/navigation';
-import React, { useEffect, useRef } from 'react';
+import { useEffect, useRef } from 'react';
 
-import { emptyLayout } from './NaverLoginPage.styles';
+import { emptyLayout } from './NaverLoginPage.css';
 import useNaverLoginMutation from '../hooks/useNaverLoginMutation';
 
 export default function NaverLoginPage() {
@@ -24,5 +24,5 @@ export default function NaverLoginPage() {
     }
   }, [code, stateParams, naverLogin]);
 
-  return <div css={emptyLayout}></div>;
+  return <div className={emptyLayout}></div>;
 }
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
index 1d77a28..be9caa4 100644
--- a/src/app/login/page.tsx
+++ b/src/app/login/page.tsx
@@ -1,29 +1,27 @@
-'use client';
-
 import Image from 'next/image';
 
-import LoginCard from './components/LoginCard';
+import LoginCard from './components/LoginCard/LoginCard';
 import {
-  loginCardContainer,
-  loginLayout,
+  loginPageLayout,
   sloganContainer,
   sloganWrapper,
-} from './LoginPage.styles';
+  loginCardContainer,
+} from './LoginPage.css';
 
 import Logo from '@/assets/images/logo.svg';
 
 export default function LoginPage() {
   return (
-    <div css={loginLayout}>
-      <div css={sloganContainer}>
+    <div className={loginPageLayout}>
+      <div className={sloganContainer}>
         <Image src={Logo} alt="로고" width={80} height={28} />
-        <div css={sloganWrapper}>
+        <div className={sloganWrapper}>
           <span>작은 연결로 시작되는 큰 발견</span>
           <br />
           <span>그라밋이 도울게요</span>
         </div>
       </div>
-      <div css={loginCardContainer}>
+      <div className={loginCardContainer}>
         <LoginCard role="연구자" description={['그라밋에서 손쉽게', '연구 참여자를 모아보세요']} />
         <LoginCard role="참여자" description={['정보를 등록하면', '딱 맞는 실험을 찾아드려요']} />
       </div>

From e415bda56f6fe6bf6d42045489ed181e8e98b684 Mon Sep 17 00:00:00 2001
From: Gyuhan Park <rbgks1937@naver.com>
Date: Wed, 5 Feb 2025 01:19:28 +0900
Subject: [PATCH 06/11] =?UTF-8?q?[YS-243]=20refactor:=20=EC=B0=B8=EC=97=AC?=
 =?UTF-8?q?=EC=9E=90=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=8A=A4?=
 =?UTF-8?q?=ED=83=80=EC=9D=BC=EB=A7=81=20=EB=A7=88=EC=9D=B4=EA=B7=B8?=
 =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../JoinEmailStep/JoinEmailStep.css.ts        | 21 ++++++
 .../JoinEmailStep/JoinEmailStep.styles.ts     | 43 ------------
 .../JoinEmailStep/JoinEmailStep.tsx           | 16 +++--
 .../AreaTooltip/AreaTooltip.css.ts            | 33 ++++++++++
 .../AreaTooltip/AreaTooltip.styles.ts         | 36 ----------
 .../JoinInfoStep/AreaTooltip/AreaTooltip.tsx  |  8 ++-
 .../JoinInfoStep/JoinInfoStep.css.ts          | 48 ++++++++++++++
 .../JoinInfoStep/JoinInfoStep.styles.ts       | 54 ---------------
 .../Participant/JoinInfoStep/JoinInfoStep.tsx | 63 +++++++++---------
 .../JoinInfoStep/JoinSelect/JoinSelect.css.ts | 65 ++++++++++++++++++
 .../JoinSelect/JoinSelect.styles.ts           | 66 -------------------
 .../JoinInfoStep/JoinSelect/JoinSelect.tsx    | 12 ++--
 .../RadioButtonGroup/RadioButtonGroup.css.ts  | 45 +++++++++++++
 .../RadioButtonGroup.styles.ts                | 48 --------------
 .../RadioButtonGroup/RadioButtonGroup.tsx     | 16 +++--
 .../RadioButtonGroupContainer.css.ts          | 28 ++++++++
 .../RadioButtonGroupContainer.styles.ts       | 25 -------
 .../RadioButtonGroupContainer.tsx             | 12 ++--
 18 files changed, 308 insertions(+), 331 deletions(-)
 create mode 100644 src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.css.ts
 delete mode 100644 src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.styles.ts
 create mode 100644 src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.css.ts
 delete mode 100644 src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.styles.ts
 create mode 100644 src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.css.ts
 delete mode 100644 src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.styles.ts
 create mode 100644 src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.css.ts
 delete mode 100644 src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.styles.ts
 create mode 100644 src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.css.ts
 delete mode 100644 src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.styles.ts
 create mode 100644 src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.css.ts
 delete mode 100644 src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.styles.ts

diff --git a/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.css.ts b/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.css.ts
new file mode 100644
index 0000000..f279b0b
--- /dev/null
+++ b/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.css.ts
@@ -0,0 +1,21 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const nextButton = style({
+  ...fonts.body.normal.SB16,
+  backgroundColor: colors.primaryMint,
+  color: colors.text01,
+  borderRadius: '1.2rem',
+  padding: '1.2rem 0',
+  width: '20rem',
+  alignItems: 'center',
+  marginBottom: '5.6rem',
+  selectors: {
+    '&:disabled': {
+      color: colors.text02,
+      backgroundColor: colors.field04,
+    },
+  },
+});
diff --git a/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.styles.ts b/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.styles.ts
deleted file mode 100644
index c3a60de..0000000
--- a/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.styles.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const joinContentContainer = (theme: Theme) => css`
-  background-color: ${theme.colors.field02};
-  width: 100%;
-  display: flex;
-  flex-direction: column;
-  gap: 2.8rem;
-  border-radius: 1.2rem;
-  padding: 3.2rem 4rem;
-`;
-
-export const nextButton = (theme: Theme) => css`
-  ${theme.fonts.body.normal.SB16};
-  background-color: ${theme.colors.primaryMint};
-  color: ${theme.colors.text01};
-  border-radius: 1.2rem;
-  padding: 1.2rem 0;
-  width: 20rem;
-  align-items: center;
-  margin-bottom: 5.6rem;
-
-  :disabled {
-    color: ${theme.colors.text02};
-    background-color: ${theme.colors.field04};
-  }
-`;
-
-export const joinButton = (theme: Theme) => css`
-  ${theme.fonts.body.normal.SB16};
-  background-color: ${theme.colors.primaryMint};
-  color: ${theme.colors.text01};
-  border-radius: 1.2rem;
-  padding: 1.2rem 0;
-  width: 20rem;
-  align-items: center;
-  margin-bottom: 5.6rem;
-
-  :disabled {
-    color: ${theme.colors.text02};
-    background-color: ${theme.colors.field04};
-  }
-`;
diff --git a/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.tsx b/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.tsx
index 39c034f..40401ab 100644
--- a/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.tsx
+++ b/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.tsx
@@ -1,11 +1,13 @@
+'use client';
+
 import { useFormContext, useWatch } from 'react-hook-form';
 
-import { nextButton } from './JoinEmailStep.styles';
+import { nextButton } from './JoinEmailStep.css';
 import JoinCheckboxContainer from '../../JoinCheckboxContainer/JoinCheckboxContainer';
 import JoinInput from '../../JoinInput/JoinInput';
 
 import useServiceAgreeCheck from '@/app/join/hooks/useServiceAgreeCheck';
-import { joinContentContainer, joinForm } from '@/app/join/JoinPage.styles';
+import { joinContentContainer, joinForm } from '@/app/join/JoinPage.css';
 import { ParticipantJoinSchemaType } from '@/schema/join/ParticipantJoinSchema';
 
 interface JoinEmailStepProps {
@@ -18,27 +20,27 @@ const JoinEmailStep = ({ onNext }: JoinEmailStepProps) => {
     trigger,
     formState: { errors },
   } = useFormContext<ParticipantJoinSchemaType>();
+
   const { serviceAgreeCheck, handleAllCheck, handleChangeCheck } = useServiceAgreeCheck();
   const oauthEmail = useWatch({ name: 'oauthEmail', control });
   const contactEmail = useWatch({ name: 'contactEmail', control });
 
   const allValid =
     contactEmail &&
-    Boolean(!errors.contactEmail) &&
+    !errors.contactEmail &&
     serviceAgreeCheck.isTermOfService &&
     serviceAgreeCheck.isPrivacy;
 
   const handleNextStep = async () => {
     const isValid = await trigger(['oauthEmail', 'contactEmail']);
-
     if (isValid) {
       onNext();
     }
   };
 
   return (
-    <section css={joinForm}>
-      <div css={joinContentContainer}>
+    <section className={joinForm}>
+      <div className={joinContentContainer}>
         <JoinInput<ParticipantJoinSchemaType>
           name="oauthEmail"
           control={control}
@@ -61,7 +63,7 @@ const JoinEmailStep = ({ onNext }: JoinEmailStepProps) => {
           handleChange={handleChangeCheck}
         />
       </div>
-      <button css={nextButton} onClick={handleNextStep} disabled={!allValid}>
+      <button className={nextButton} onClick={handleNextStep} disabled={!allValid}>
         다음
       </button>
     </section>
diff --git a/src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.css.ts b/src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.css.ts
new file mode 100644
index 0000000..c7c6c9f
--- /dev/null
+++ b/src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.css.ts
@@ -0,0 +1,33 @@
+import { style, keyframes } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const slideDownAndFade = keyframes({
+  from: { opacity: 0, transform: 'translateY(-2px)' },
+  to: { opacity: 1, transform: 'translateY(0)' },
+});
+
+export const tooltipContent = style({
+  ...fonts.label.medium.M13,
+  width: '20rem',
+  left: '2.4rem',
+  backgroundColor: colors.field01,
+  borderRadius: '0.6rem',
+  padding: '0.8rem 1.6rem',
+  color: colors.text05,
+  boxShadow: '0 4px 8px rgba(16, 17, 18, 0.1)',
+  border: `0.15rem solid ${colors.line01}`,
+  userSelect: 'none',
+  animationDuration: '100ms',
+  animationTimingFunction: 'cubic-bezier(0.16, 1, 0.3, 1)',
+  selectors: {
+    '&[data-state="delayed-open"][data-side="bottom"]': {
+      animationName: slideDownAndFade,
+    },
+  },
+});
+
+export const tooltipArrow = style({
+  fill: colors.field01,
+});
diff --git a/src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.styles.ts b/src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.styles.ts
deleted file mode 100644
index bf1cfaf..0000000
--- a/src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.styles.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { css, keyframes, Theme } from '@emotion/react';
-
-export const tooltipContent = (theme: Theme) => css`
-  ${theme.fonts.label.medium.M13};
-  width: 20rem;
-  left: 2.4rem;
-  background-color: ${theme.colors.field01};
-  border-radius: 0.6rem;
-  padding: 0.8rem 1.6rem;
-  color: ${theme.colors.text05};
-  box-shadow: 0 4px 8px rgba(16, 17, 18, 0.1);
-  border: 0.15rem solid ${theme.colors.line01};
-
-  user-select: none;
-  animation-duration: 100ms;
-  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
-
-  &[data-state='delayed-open'][data-side='bottom'] {
-    animation-name: ${slideDownAndFade};
-  }
-`;
-
-export const tooltipArrow = (theme: Theme) => css`
-  fill: ${theme.colors.field01};
-`;
-
-export const slideDownAndFade = keyframes`
-	from {
-		opacity: 0;
-		transform: translateY(-2px);
-	}
-	to {
-		opacity: 1;
-		transform: translateY(0);
-	}
-`;
diff --git a/src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.tsx b/src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.tsx
index c19f241..ae21952 100644
--- a/src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.tsx
+++ b/src/app/join/components/Participant/JoinInfoStep/AreaTooltip/AreaTooltip.tsx
@@ -1,6 +1,8 @@
+'use client';
+
 import * as Tooltip from '@radix-ui/react-tooltip';
 
-import { tooltipArrow, tooltipContent } from './AreaTooltip.styles';
+import { tooltipArrow, tooltipContent } from './AreaTooltip.css';
 
 import Icon from '@/components/Icon';
 
@@ -14,9 +16,9 @@ const AreaTooltip = () => {
           </button>
         </Tooltip.Trigger>
         <Tooltip.Portal>
-          <Tooltip.Content side="bottom" sideOffset={2} css={tooltipContent}>
+          <Tooltip.Content side="bottom" sideOffset={2} className={tooltipContent}>
             학교 소재지 등 자주 가는 지역을 추가로 입력할 수 있어요
-            <Tooltip.Arrow css={tooltipArrow} />
+            <Tooltip.Arrow className={tooltipArrow} />
           </Tooltip.Content>
         </Tooltip.Portal>
       </Tooltip.Root>
diff --git a/src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.css.ts b/src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.css.ts
new file mode 100644
index 0000000..fde409d
--- /dev/null
+++ b/src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.css.ts
@@ -0,0 +1,48 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const joinButton = style({
+  ...fonts.body.normal.SB16,
+  backgroundColor: colors.primaryMint,
+  color: colors.text01,
+  borderRadius: '1.2rem',
+  padding: '1.2rem 0',
+  width: '20rem',
+  alignItems: 'center',
+  marginBottom: '5.6rem',
+  selectors: {
+    '&:disabled': {
+      color: colors.text02,
+      backgroundColor: colors.field04,
+    },
+  },
+});
+
+export const joinAreaFilterContainer = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.6rem',
+});
+
+export const filterTitleWrapper = style({
+  ...fonts.label.large.M14,
+  color: colors.text06,
+  display: 'flex',
+  gap: '0.4rem',
+  alignItems: 'center',
+});
+
+export const filterTitle = style({
+  ...fonts.label.large.M14,
+});
+
+export const requiredStar = style({
+  color: colors.textAlert,
+});
+
+export const joinAreaFilterWrapper = style({
+  display: 'flex',
+  gap: '0.8rem',
+});
diff --git a/src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.styles.ts b/src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.styles.ts
deleted file mode 100644
index d02c8f5..0000000
--- a/src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.styles.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const joinContentContainer = (theme: Theme) => css`
-  background-color: ${theme.colors.field02};
-  width: 100%;
-  display: flex;
-  flex-direction: column;
-  gap: 2.8rem;
-  border-radius: 1.2rem;
-  padding: 3.2rem 4rem;
-`;
-
-export const joinButton = (theme: Theme) => css`
-  ${theme.fonts.body.normal.SB16};
-  background-color: ${theme.colors.primaryMint};
-  color: ${theme.colors.text01};
-  border-radius: 1.2rem;
-  padding: 1.2rem 0;
-  width: 20rem;
-  align-items: center;
-  margin-bottom: 5.6rem;
-
-  :disabled {
-    color: ${theme.colors.text02};
-    background-color: ${theme.colors.field04};
-  }
-`;
-
-export const joinAreaFilterContainer = css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.6rem;
-`;
-
-export const filterTitleWrapper = (theme: Theme) => css`
-  ${theme.fonts.label.large.M14};
-  color: ${theme.colors.text06};
-  display: flex;
-  gap: 0.4rem;
-  align-items: center;
-`;
-
-export const filterTitle = (theme: Theme) => css`
-  ${theme.fonts.label.large.M14};
-`;
-
-export const requiredStar = (theme: Theme) => css`
-  color: ${theme.colors.textAlert};
-`;
-
-export const joinAreaFilterWrapper = css`
-  display: flex;
-  gap: 0.8rem;
-`;
diff --git a/src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.tsx b/src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.tsx
index 434c87c..b1aa4ac 100644
--- a/src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.tsx
+++ b/src/app/join/components/Participant/JoinInfoStep/JoinInfoStep.tsx
@@ -1,21 +1,22 @@
+'use client';
+
 import { Controller, useFormContext, useWatch } from 'react-hook-form';
 
 import AreaTooltip from './AreaTooltip/AreaTooltip';
 import {
-  filterTitle,
-  filterTitleWrapper,
-  joinAreaFilterContainer,
-  joinAreaFilterWrapper,
   joinButton,
-  joinContentContainer,
+  joinAreaFilterContainer,
+  filterTitleWrapper,
+  filterTitle,
   requiredStar,
-} from './JoinInfoStep.styles';
+  joinAreaFilterWrapper,
+} from './JoinInfoStep.css';
 import JoinSelect from './JoinSelect/JoinSelect';
 import RadioButtonGroupContainer from './RadioButtonGroupContainer/RadioButtonGroupContainer';
 import JoinInput from '../../JoinInput/JoinInput';
 
 import { JOIN_REGION, JOIN_SUB_REGION } from '@/app/join/JoinPage.constants';
-import { joinForm } from '@/app/join/JoinPage.styles';
+import { joinContentContainer, joinForm } from '@/app/join/JoinPage.css'; // 이미 vanilla‐extract로 변환된 JoinPage 스타일 파일
 import { Gender, MatchType } from '@/app/join/JoinPage.types';
 import { ParticipantJoinSchemaType } from '@/schema/join/ParticipantJoinSchema';
 
@@ -41,8 +42,8 @@ const JoinInfoStep = ({ handleSubmit }: JoinInfoStepProps) => {
   const isAllFilled = values.every((value) => (value ?? '').trim() !== '' && value !== undefined);
 
   return (
-    <section css={joinForm}>
-      <div css={joinContentContainer}>
+    <section className={joinForm}>
+      <div className={joinContentContainer}>
         {/* 이름 */}
         <JoinInput<ParticipantJoinSchemaType>
           name="name"
@@ -80,12 +81,12 @@ const JoinInfoStep = ({ handleSubmit }: JoinInfoStepProps) => {
         />
 
         {/* 거주 지역 */}
-        <div css={joinAreaFilterContainer}>
-          <div css={filterTitleWrapper}>
-            <span css={filterTitle}>거주 지역</span>
-            <span css={requiredStar}>*</span>
+        <div className={joinAreaFilterContainer}>
+          <div className={filterTitleWrapper}>
+            <span className={filterTitle}>거주 지역</span>
+            <span className={requiredStar}>*</span>
           </div>
-          <div css={joinAreaFilterWrapper}>
+          <div className={joinAreaFilterWrapper}>
             <Controller
               name="basicAddressInfo.region"
               control={control}
@@ -95,7 +96,7 @@ const JoinInfoStep = ({ handleSubmit }: JoinInfoStepProps) => {
                   onChange={(value) => setValue('basicAddressInfo.region', value)}
                   placeholder="시·도"
                   options={JOIN_REGION}
-                  isError={Boolean(fieldState.error) && Boolean(!field.value)}
+                  isError={Boolean(fieldState.error) && !field.value}
                 />
               )}
             />
@@ -109,7 +110,7 @@ const JoinInfoStep = ({ handleSubmit }: JoinInfoStepProps) => {
                   onChange={(value) => setValue('basicAddressInfo.area', value)}
                   placeholder="시·군·구"
                   options={JOIN_SUB_REGION[selectedArea] || []}
-                  isError={Boolean(fieldState.error) && Boolean(!field.value)}
+                  isError={Boolean(fieldState.error) && !field.value}
                 />
               )}
             />
@@ -117,26 +118,24 @@ const JoinInfoStep = ({ handleSubmit }: JoinInfoStepProps) => {
         </div>
 
         {/* 추가 활동 지역 */}
-        <div css={joinAreaFilterContainer}>
-          <div css={filterTitleWrapper}>
-            <span css={filterTitle}>추가 활동 지역</span>
+        <div className={joinAreaFilterContainer}>
+          <div className={filterTitleWrapper}>
+            <span className={filterTitle}>추가 활동 지역</span>
             <AreaTooltip />
           </div>
-          <div css={joinAreaFilterWrapper}>
+          <div className={joinAreaFilterWrapper}>
             <Controller
               name="additionalAddressInfo.region"
               control={control}
-              render={({ field, fieldState }) => {
-                return (
-                  <JoinSelect
-                    value={field.value}
-                    onChange={(value) => setValue('additionalAddressInfo.region', value)}
-                    placeholder="시·도"
-                    options={JOIN_REGION}
-                    isError={Boolean(fieldState.error)}
-                  />
-                );
-              }}
+              render={({ field, fieldState }) => (
+                <JoinSelect
+                  value={field.value}
+                  onChange={(value) => setValue('additionalAddressInfo.region', value)}
+                  placeholder="시·도"
+                  options={JOIN_REGION}
+                  isError={Boolean(fieldState.error)}
+                />
+              )}
             />
 
             <Controller
@@ -170,7 +169,7 @@ const JoinInfoStep = ({ handleSubmit }: JoinInfoStepProps) => {
       </div>
 
       <button
-        css={joinButton}
+        className={joinButton}
         onClick={handleSubmit}
         disabled={!(isAllFilled && Object.keys(errors).length === 0)}
       >
diff --git a/src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.css.ts b/src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.css.ts
new file mode 100644
index 0000000..fdf2e5d
--- /dev/null
+++ b/src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.css.ts
@@ -0,0 +1,65 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const triggerWrapper = style({
+  ...fonts.body.normal.M16,
+  color: colors.text06,
+  display: 'flex',
+  alignItems: 'center',
+  justifyContent: 'space-between',
+  gap: '0.4rem',
+  borderRadius: '1.2rem',
+  padding: '1.6rem',
+  backgroundColor: colors.field01,
+  cursor: 'pointer',
+  width: '100%',
+  selectors: {
+    '&[data-placeholder]': {
+      ...fonts.body.normal.M16,
+      color: colors.text02,
+    },
+    "&[aria-invalid='true']": {
+      outline: `0.1rem solid ${colors.textAlert}`,
+    },
+  },
+});
+
+export const selectContent = style({
+  width: '23.6rem',
+  maxHeight: '34rem',
+  padding: '1rem 0.8rem',
+  backgroundColor: colors.field01,
+  border: `0.1rem solid ${colors.line01}`,
+  borderRadius: '1.2rem',
+  boxShadow: '0px 4px 16px rgba(53, 59, 61, 0.2)',
+  overflowY: 'scroll',
+});
+
+export const selectList = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.8rem',
+});
+
+export const selectItem = style({
+  ...fonts.body.normal.M16,
+  color: colors.text06,
+  display: 'flex',
+  padding: '0.6rem 1.2rem',
+  alignItems: 'center',
+  borderRadius: '1.2rem',
+  cursor: 'pointer',
+  selectors: {
+    '&[data-highlighted]': {
+      backgroundColor: colors.field02,
+      outline: 'none',
+    },
+    "&[data-state='checked']": {
+      backgroundColor: colors.primaryTinted,
+      color: colors.textPrimary,
+      border: `0.1rem solid ${colors.textPrimary}`,
+    },
+  },
+});
diff --git a/src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.styles.ts b/src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.styles.ts
deleted file mode 100644
index 90a6726..0000000
--- a/src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.styles.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const triggerWrapper = (theme: Theme) => css`
-  ${theme.fonts.body.normal.M16};
-  color: ${theme.colors.text06};
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 0.4rem;
-  border-radius: 1.2rem;
-  padding: 1.6rem;
-  background-color: ${theme.colors.field01};
-  cursor: pointer;
-  width: 100%;
-
-  &[data-placeholder] {
-    ${theme.fonts.body.normal.M16};
-    color: ${theme.colors.text02};
-  }
-
-  &[aria-invalid='true'] {
-    outline: 0.1rem solid ${theme.colors.textAlert};
-  }
-`;
-
-export const selectContent = (theme: Theme) => css`
-  width: 23.6rem;
-  max-height: 34rem;
-
-  padding: 1rem 0.8rem;
-
-  background-color: ${theme.colors.field01};
-  border: 0.1rem solid ${theme.colors.line01};
-  border-radius: 1.2rem;
-
-  box-shadow: 0px 4px 16px rgba(53, 59, 61, 0.2);
-  overflow-y: scroll;
-`;
-
-export const selectList = css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.8rem;
-`;
-
-export const selectItem = (theme: Theme) => css`
-  ${theme.fonts.body.normal.M16};
-  color: ${theme.colors.text06};
-  display: flex;
-  padding: 0.6rem 1.2rem;
-  align-items: center;
-  border-radius: 1.2rem;
-  cursor: pointer;
-
-  &[data-highlighted] {
-    background-color: ${theme.colors.field02};
-    outline: none;
-  }
-
-  &[data-state='checked'] {
-    background-color: ${theme.colors.primaryTinted};
-    color: ${theme.colors.textPrimary};
-
-    border: 0.1rem solid ${theme.colors.textPrimary};
-  }
-`;
diff --git a/src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.tsx b/src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.tsx
index 84d84ec..1e893df 100644
--- a/src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.tsx
+++ b/src/app/join/components/Participant/JoinInfoStep/JoinSelect/JoinSelect.tsx
@@ -1,7 +1,9 @@
+'use client';
+
 import * as Select from '@radix-ui/react-select';
 import { useState } from 'react';
 
-import { selectContent, selectItem, selectList, triggerWrapper } from './JoinSelect.styles';
+import { triggerWrapper, selectContent, selectList, selectItem } from './JoinSelect.css';
 
 import { FilterOption } from '@/app/join/JoinPage.types';
 import Icon from '@/components/Icon';
@@ -19,17 +21,17 @@ const JoinSelect = ({ placeholder, onChange, isError, options, value }: JoinSele
 
   return (
     <Select.Root value={value} onValueChange={onChange} onOpenChange={(open) => setIsOpen(open)}>
-      <Select.Trigger css={triggerWrapper} aria-invalid={isError}>
+      <Select.Trigger className={triggerWrapper} aria-invalid={isError}>
         <Select.Value placeholder={placeholder} />
         <Select.Icon>
           <Icon icon="Chevron" width={20} rotate={isOpen ? -180 : 0} cursor="pointer" />
         </Select.Icon>
       </Select.Trigger>
       <Select.Portal>
-        <Select.Content css={selectContent} position="popper" sideOffset={4}>
-          <Select.Group css={selectList}>
+        <Select.Content className={selectContent} position="popper" sideOffset={4}>
+          <Select.Group className={selectList}>
             {options?.map((option) => (
-              <Select.Item key={option.value} value={option.value} css={selectItem}>
+              <Select.Item key={option.value} value={option.value} className={selectItem}>
                 <Select.ItemText>{option.label}</Select.ItemText>
               </Select.Item>
             ))}
diff --git a/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.css.ts b/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.css.ts
new file mode 100644
index 0000000..b767afd
--- /dev/null
+++ b/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.css.ts
@@ -0,0 +1,45 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const customRadioGroup = style({
+  display: 'flex',
+  flexFlow: 'row nowrap',
+  gap: '0.8rem',
+});
+
+export const customRadioButton = style({
+  ...fonts.label.large.M14,
+  flexGrow: 1,
+  height: '4.8rem',
+  padding: '1rem 2rem',
+  border: `0.1rem solid ${colors.line01}`,
+  borderRadius: '1.2rem',
+  backgroundColor: colors.field01,
+  cursor: 'pointer',
+  transition: 'all 0.2s ease',
+  selectors: {
+    '&:hover': {
+      backgroundColor: colors.field02,
+    },
+    "&[aria-invalid='true']": {
+      outline: `0.1rem solid ${colors.textAlert}`,
+    },
+  },
+});
+
+export const activeRadioButton = style({
+  border: `0.1rem solid ${colors.lineTinted}`,
+  backgroundColor: colors.primaryTinted,
+  color: colors.textPrimary,
+  selectors: {
+    '&:hover': {
+      backgroundColor: colors.primaryTinted,
+    },
+  },
+});
+
+export const errorRadioButton = style({
+  borderColor: colors.textAlert,
+});
diff --git a/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.styles.ts b/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.styles.ts
deleted file mode 100644
index f20781e..0000000
--- a/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.styles.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const customRadioGroup = css`
-  display: flex;
-  flex-flow: row nowrap;
-  gap: 0.8rem;
-`;
-
-export const customRadioButton = (theme: Theme) => css`
-  ${theme.fonts.label.large.M14};
-
-  flex-grow: 1;
-  height: 4.8rem;
-
-  padding: 1rem 2rem;
-
-  border: 0.1rem solid ${theme.colors.line01};
-  border-radius: 1.2rem;
-
-  background-color: ${theme.colors.field01};
-
-  cursor: pointer;
-
-  transition: all 0.2s ease;
-
-  &:hover {
-    background-color: ${theme.colors.field02};
-  }
-
-  &[aria-invalid='true'] {
-    outline: 0.1rem solid ${theme.colors.textAlert};
-  }
-`;
-
-export const activeRadioButton = (theme: Theme) => css`
-  border: 0.1rem solid ${theme.colors.lineTinted};
-
-  background-color: ${theme.colors.primaryTinted};
-  color: ${theme.colors.textPrimary};
-
-  &:hover {
-    background-color: ${theme.colors.primaryTinted};
-  }
-`;
-
-export const errorRadioButton = (theme: Theme) => css`
-  border-color: ${theme.colors.textAlert};
-`;
diff --git a/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.tsx b/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.tsx
index d31fc8e..05005b9 100644
--- a/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.tsx
+++ b/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroup/RadioButtonGroup.tsx
@@ -1,9 +1,11 @@
+'use client';
+
 import {
   customRadioGroup,
   customRadioButton,
   activeRadioButton,
   errorRadioButton,
-} from './RadioButtonGroup.styles';
+} from './RadioButtonGroup.css';
 
 interface RadioButtonGroupProps<T> {
   options: { value: T; label: string }[];
@@ -19,16 +21,16 @@ const RadioButtonGroup = <T extends string>({
   isError,
 }: RadioButtonGroupProps<T>) => {
   return (
-    <div css={customRadioGroup}>
+    <div className={customRadioGroup}>
       {options.map((option) => (
         <button
           key={option.value}
           type="button"
-          css={[
-            customRadioButton,
-            selectedValue === option.value && activeRadioButton,
-            isError && errorRadioButton,
-          ]}
+          className={`
+            ${customRadioButton}
+            ${selectedValue === option.value ? activeRadioButton : ''}
+            ${isError ? errorRadioButton : ''}
+          `}
           onClick={() => onChange(option.value)}
         >
           {option.label}
diff --git a/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.css.ts b/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.css.ts
new file mode 100644
index 0000000..a50f196
--- /dev/null
+++ b/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.css.ts
@@ -0,0 +1,28 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const radioButtonGroupContainerLayout = style({
+  display: 'flex',
+  flexDirection: 'column',
+  gap: '0.6rem',
+});
+
+export const labelWrapper = style({
+  ...fonts.label.large.M14,
+  display: 'flex',
+  gap: '0.4rem',
+  color: colors.text06,
+});
+
+export const requiredStar = style({
+  color: colors.textAlert,
+});
+
+export const tipWrapper = style({
+  ...fonts.label.small.M12,
+  display: 'flex',
+  gap: '0.4rem',
+  color: colors.text02,
+});
diff --git a/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.styles.ts b/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.styles.ts
deleted file mode 100644
index c6d4e61..0000000
--- a/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.styles.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const radioButtonGroupContainerLayout = css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.6rem;
-`;
-
-export const labelWrapper = (theme: Theme) => css`
-  display: flex;
-  gap: 0.4rem;
-  ${theme.fonts.label.large.M14};
-  color: ${theme.colors.text06};
-`;
-
-export const requiredStar = (theme: Theme) => css`
-  color: ${theme.colors.textAlert};
-`;
-
-export const tipWrapper = (theme: Theme) => css`
-  ${theme.fonts.label.small.M12};
-  display: flex;
-  gap: 0.4rem;
-  color: ${theme.colors.text02};
-`;
diff --git a/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.tsx b/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.tsx
index 2d715f5..ce3c801 100644
--- a/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.tsx
+++ b/src/app/join/components/Participant/JoinInfoStep/RadioButtonGroupContainer/RadioButtonGroupContainer.tsx
@@ -1,3 +1,5 @@
+'use client';
+
 import { Controller } from 'react-hook-form';
 
 import RadioButtonGroup from './RadioButtonGroup/RadioButtonGroup';
@@ -6,7 +8,7 @@ import {
   radioButtonGroupContainerLayout,
   requiredStar,
   tipWrapper,
-} from './RadioButtonGroupContainer.styles';
+} from './RadioButtonGroupContainer.css';
 
 interface RadioButtonGroupProps<T> {
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -29,10 +31,10 @@ const RadioButtonGroupContainer = <T extends string>({
   tip,
 }: RadioButtonGroupProps<T>) => {
   return (
-    <div css={radioButtonGroupContainerLayout}>
-      <div css={labelWrapper}>
+    <div className={radioButtonGroupContainerLayout}>
+      <div className={labelWrapper}>
         <span>{title}</span>
-        {required && <span css={requiredStar}>*</span>}
+        {required && <span className={requiredStar}>*</span>}
       </div>
 
       <Controller
@@ -51,7 +53,7 @@ const RadioButtonGroupContainer = <T extends string>({
       />
 
       {tip && (
-        <div css={tipWrapper}>
+        <div className={tipWrapper}>
           <span>{tip}</span>
         </div>
       )}

From 0ce1c19b89bdb1c1a71576b559e6a23e4205b2fa Mon Sep 17 00:00:00 2001
From: Gyuhan Park <rbgks1937@naver.com>
Date: Wed, 5 Feb 2025 01:50:17 +0900
Subject: [PATCH 07/11] =?UTF-8?q?[YS-243]=20refactor:=20=EC=97=B0=EA=B5=AC?=
 =?UTF-8?q?=EC=9E=90=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=8A=A4?=
 =?UTF-8?q?=ED=83=80=EC=9D=BC=EB=A7=81=20=EB=A7=88=EC=9D=B4=EA=B7=B8?=
 =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/join/JoinPage.css.ts                  | 17 ++++
 .../JoinEmailStep/JoinEmailStep.tsx           |  3 +-
 .../JoinEmailStep/JoinEmailStep.css.ts        |  0
 .../JoinEmailStep/JoinEmailStep.styles.ts     | 43 ----------
 .../JoinEmailStep/JoinEmailStep.tsx           | 17 ++--
 .../AuthCodeInput/AuthCodeInput.css.ts        | 44 ++++++++++
 .../AuthCodeInput/AuthCodeInput.tsx           | 26 +++---
 .../UnivAuthInput/UnivAuthInput.css.ts        | 43 ++++++++++
 .../UnivAuthInput/UnivAuthInput.styles.ts     | 86 -------------------
 .../UnivAuthInput/UnivAuthInput.tsx           | 73 +++++++++-------
 .../JoinInfoStep/JoinInfoStep.styles.ts       | 27 ------
 .../Researcher/JoinInfoStep/JoinInfoStep.tsx  | 17 ++--
 12 files changed, 178 insertions(+), 218 deletions(-)
 rename src/app/join/components/{Participant => Researcher}/JoinEmailStep/JoinEmailStep.css.ts (100%)
 delete mode 100644 src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.styles.ts
 create mode 100644 src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.css.ts
 create mode 100644 src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.css.ts
 delete mode 100644 src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.styles.ts
 delete mode 100644 src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.styles.ts

diff --git a/src/app/join/JoinPage.css.ts b/src/app/join/JoinPage.css.ts
index 5ddbc56..60820c3 100644
--- a/src/app/join/JoinPage.css.ts
+++ b/src/app/join/JoinPage.css.ts
@@ -63,3 +63,20 @@ export const joinForm = style({
   alignItems: 'center',
   gap: '4rem',
 });
+
+export const nextButton = style({
+  ...fonts.body.normal.SB16,
+  backgroundColor: colors.primaryMint,
+  color: colors.text01,
+  borderRadius: '1.2rem',
+  padding: '1.2rem 0',
+  width: '20rem',
+  alignItems: 'center',
+  marginBottom: '5.6rem',
+  selectors: {
+    '&:disabled': {
+      color: colors.text02,
+      backgroundColor: colors.field04,
+    },
+  },
+});
diff --git a/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.tsx b/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.tsx
index 40401ab..3e8f13a 100644
--- a/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.tsx
+++ b/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.tsx
@@ -2,12 +2,11 @@
 
 import { useFormContext, useWatch } from 'react-hook-form';
 
-import { nextButton } from './JoinEmailStep.css';
 import JoinCheckboxContainer from '../../JoinCheckboxContainer/JoinCheckboxContainer';
 import JoinInput from '../../JoinInput/JoinInput';
 
 import useServiceAgreeCheck from '@/app/join/hooks/useServiceAgreeCheck';
-import { joinContentContainer, joinForm } from '@/app/join/JoinPage.css';
+import { joinContentContainer, joinForm, nextButton } from '@/app/join/JoinPage.css';
 import { ParticipantJoinSchemaType } from '@/schema/join/ParticipantJoinSchema';
 
 interface JoinEmailStepProps {
diff --git a/src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.css.ts b/src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.css.ts
similarity index 100%
rename from src/app/join/components/Participant/JoinEmailStep/JoinEmailStep.css.ts
rename to src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.css.ts
diff --git a/src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.styles.ts b/src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.styles.ts
deleted file mode 100644
index c3a60de..0000000
--- a/src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.styles.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const joinContentContainer = (theme: Theme) => css`
-  background-color: ${theme.colors.field02};
-  width: 100%;
-  display: flex;
-  flex-direction: column;
-  gap: 2.8rem;
-  border-radius: 1.2rem;
-  padding: 3.2rem 4rem;
-`;
-
-export const nextButton = (theme: Theme) => css`
-  ${theme.fonts.body.normal.SB16};
-  background-color: ${theme.colors.primaryMint};
-  color: ${theme.colors.text01};
-  border-radius: 1.2rem;
-  padding: 1.2rem 0;
-  width: 20rem;
-  align-items: center;
-  margin-bottom: 5.6rem;
-
-  :disabled {
-    color: ${theme.colors.text02};
-    background-color: ${theme.colors.field04};
-  }
-`;
-
-export const joinButton = (theme: Theme) => css`
-  ${theme.fonts.body.normal.SB16};
-  background-color: ${theme.colors.primaryMint};
-  color: ${theme.colors.text01};
-  border-radius: 1.2rem;
-  padding: 1.2rem 0;
-  width: 20rem;
-  align-items: center;
-  margin-bottom: 5.6rem;
-
-  :disabled {
-    color: ${theme.colors.text02};
-    background-color: ${theme.colors.field04};
-  }
-`;
diff --git a/src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.tsx b/src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.tsx
index f21a023..c904290 100644
--- a/src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.tsx
+++ b/src/app/join/components/Researcher/JoinEmailStep/JoinEmailStep.tsx
@@ -1,13 +1,15 @@
+'use client';
+
 import { useState } from 'react';
 import { useFormContext, useWatch } from 'react-hook-form';
 
-import { joinContentContainer, nextButton } from './JoinEmailStep.styles';
+import { nextButton } from './JoinEmailStep.css';
 import UnivAuthInput from './UnivAuthInput/UnivAuthInput';
 import JoinCheckboxContainer from '../../JoinCheckboxContainer/JoinCheckboxContainer';
 import JoinInput from '../../JoinInput/JoinInput';
 
 import useServiceAgreeCheck from '@/app/join/hooks/useServiceAgreeCheck';
-import { joinForm } from '@/app/join/JoinPage.styles';
+import { joinContentContainer, joinForm } from '@/app/join/JoinPage.css';
 import { ResearcherJoinSchemaType } from '@/schema/join/ResearcherJoinSchema';
 
 interface JoinEmailStepProps {
@@ -28,7 +30,6 @@ const JoinEmailStep = ({ onNext }: JoinEmailStepProps) => {
 
   const handleNextStep = async () => {
     const isValid = await trigger(['oauthEmail', 'contactEmail', 'univEmail']);
-
     if (isValid) {
       onNext();
     }
@@ -37,8 +38,8 @@ const JoinEmailStep = ({ onNext }: JoinEmailStepProps) => {
   const allValid =
     oauthEmail &&
     univEmail &&
-    Boolean(!errors.contactEmail) &&
-    Boolean(!errors.univEmail) &&
+    !errors.contactEmail &&
+    !errors.univEmail &&
     isEmailVerified &&
     serviceAgreeCheck.isTermOfService &&
     serviceAgreeCheck.isPrivacy;
@@ -48,8 +49,8 @@ const JoinEmailStep = ({ onNext }: JoinEmailStepProps) => {
   };
 
   return (
-    <section css={joinForm}>
-      <div css={joinContentContainer}>
+    <section className={joinForm}>
+      <div className={joinContentContainer}>
         <JoinInput<ResearcherJoinSchemaType>
           name="oauthEmail"
           control={control}
@@ -73,7 +74,7 @@ const JoinEmailStep = ({ onNext }: JoinEmailStepProps) => {
           handleChange={handleChangeCheck}
         />
       </div>
-      <button css={nextButton} onClick={handleNextStep} disabled={!allValid}>
+      <button className={nextButton} onClick={handleNextStep} disabled={!allValid}>
         다음
       </button>
     </section>
diff --git a/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.css.ts b/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.css.ts
new file mode 100644
index 0000000..e7415a8
--- /dev/null
+++ b/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.css.ts
@@ -0,0 +1,44 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const authInputLayout = style({
+  display: 'flex',
+  flexDirection: 'column',
+});
+
+export const authTimerWrapper = style({
+  position: 'absolute',
+  right: '1.2rem',
+  top: '1rem',
+  display: 'flex',
+  alignItems: 'center',
+  gap: '0.8rem',
+});
+
+export const authTimerText = style({
+  ...fonts.label.large.M14,
+  color: colors.text03,
+});
+
+export const authCodeButton = style({
+  ...fonts.label.large.SB14,
+  padding: '0.7rem 1.6rem',
+  borderRadius: '1rem',
+  backgroundColor: colors.primaryMint,
+  color: colors.text01,
+  selectors: {
+    '&:disabled': {
+      backgroundColor: colors.field04,
+      color: colors.text02,
+    },
+  },
+});
+
+export const sendAgainButton = style({
+  ...fonts.label.large.M14,
+  color: colors.text03,
+  textDecorationLine: 'underline',
+  alignSelf: 'flex-end',
+});
diff --git a/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.tsx b/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.tsx
index 3630ae7..c765b01 100644
--- a/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.tsx
+++ b/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/AuthCodeInput/AuthCodeInput.tsx
@@ -1,15 +1,19 @@
+'use client';
+
 import { ChangeEvent, useState } from 'react';
 import { useFormContext } from 'react-hook-form';
 
 import {
-  authCodeButton,
   authInputLayout,
   authTimerWrapper,
+  authCodeButton,
   sendAgainButton,
-} from './AuthCodeInput.styles';
-import { univInputWrapper } from '../UnivAuthInput.styles';
+  authTimerText,
+} from './AuthCodeInput.css';
+import { univInputWrapper } from '../UnivAuthInput.css';
 
 import EmailToast from '@/app/join/components/EmailToast/EmailToast';
+import { joinInput } from '@/app/join/components/JoinInput/JoinInput.css';
 import useVerifyUnivAuthCodeMutation from '@/app/join/hooks/useVerifyUnivAuthCodeMutation';
 import { formatAuthTimer } from '@/app/join/JoinPage.utils';
 import { ResearcherJoinSchemaType } from '@/schema/join/ResearcherJoinSchema';
@@ -29,7 +33,6 @@ const AuthCodeInput = ({
 }: AuthCodeInputProps) => {
   const { getValues } = useFormContext<ResearcherJoinSchemaType>();
   const { mutate: verifyEmail, isSuccess: isUnivVerify } = useVerifyUnivAuthCodeMutation();
-
   const [isToastOpen, setIsToastOpen] = useState(false);
   const [authCode, setAuthCode] = useState('');
 
@@ -41,7 +44,6 @@ const AuthCodeInput = ({
 
   const handleVerifyUniv = () => {
     const univEmail = getValues('univEmail');
-
     verifyEmail(
       { univEmail, inputCode: authCode },
       {
@@ -55,9 +57,11 @@ const AuthCodeInput = ({
 
   return (
     <>
-      <div css={authInputLayout}>
-        <div css={univInputWrapper}>
+      <div className={authInputLayout}>
+        <div className={univInputWrapper}>
           <input
+            style={{ width: '100%' }}
+            className={joinInput}
             placeholder="인증번호 6자리 입력"
             type="number"
             disabled={isUnivVerify}
@@ -65,11 +69,11 @@ const AuthCodeInput = ({
             onChange={handleChangeAuthCode}
           />
           {!isUnivVerify && (
-            <div css={authTimerWrapper}>
-              <span>{formatAuthTimer(authTimer)}</span>
+            <div className={authTimerWrapper}>
+              <span className={authTimerText}>{formatAuthTimer(authTimer)}</span>
               <button
                 type="button"
-                css={authCodeButton}
+                className={authCodeButton}
                 disabled={!authCode || authCode.length < AUTH_CODE_VALID_LENGTH}
                 onClick={handleVerifyUniv}
               >
@@ -78,7 +82,7 @@ const AuthCodeInput = ({
             </div>
           )}
         </div>
-        <button type="button" css={sendAgainButton} onClick={handleSendUnivAuthCode}>
+        <button type="button" className={sendAgainButton} onClick={handleSendUnivAuthCode}>
           인증번호 재전송
         </button>
       </div>
diff --git a/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.css.ts b/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.css.ts
new file mode 100644
index 0000000..5d9cb45
--- /dev/null
+++ b/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.css.ts
@@ -0,0 +1,43 @@
+import { style } from '@vanilla-extract/css';
+
+import { colors } from '@/styles/colors';
+import { fonts } from '@/styles/fonts.css';
+
+export const univInputWrapper = style({
+  position: 'relative',
+  backgroundColor: colors.field01,
+  borderRadius: '1rem',
+});
+
+export const required = style({
+  color: colors.textAlert,
+});
+
+export const univAuthButton = style({
+  ...fonts.label.large.SB14,
+  position: 'absolute',
+  right: '1.2rem',
+  top: '1rem',
+  padding: '0.7rem 1.6rem',
+  borderRadius: '1rem',
+  color: colors.text01,
+  backgroundColor: colors.primaryMint,
+  border: 'none',
+  selectors: {
+    '&:disabled': {
+      color: colors.text02,
+      backgroundColor: colors.field04,
+    },
+  },
+});
+
+export const editButton = style({
+  color: colors.text06,
+  backgroundColor: colors.field01,
+  border: `0.1rem solid ${colors.line02}`,
+});
+
+export const errorMessage = style({
+  ...fonts.label.large.R14,
+  color: colors.textAlert,
+});
diff --git a/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.styles.ts b/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.styles.ts
deleted file mode 100644
index bbdcd97..0000000
--- a/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.styles.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const inputContainer = (theme: Theme) => css`
-  display: flex;
-  flex-direction: column;
-  gap: 0.6rem;
-
-  label {
-    ${theme.fonts.label.large.M14};
-    color: ${theme.colors.text06};
-    display: flex;
-    gap: 0.4rem;
-  }
-
-  input {
-    ${theme.fonts.body.normal.M16};
-    color: ${theme.colors.text06};
-    border: 0.1rem solid ${theme.colors.line01};
-    padding: 1.6rem;
-    border-radius: 1.2rem;
-
-    :disabled {
-      color: ${theme.colors.text03};
-      background-color: ${theme.colors.field03};
-    }
-
-    ::placeholder {
-      color: ${theme.colors.text03};
-    }
-
-    :focus {
-      outline: 0.1rem solid ${theme.colors.lineTinted};
-    }
-  }
-
-  input[aria-invalid='true'] {
-    outline: 0.1rem solid ${theme.colors.textAlert};
-  }
-`;
-
-export const univInputWrapper = (theme: Theme) => css`
-  position: relative;
-  background-color: ${theme.colors.field01};
-  border-radius: 1rem;
-
-  input {
-    width: 100%;
-  }
-
-  button:disabled {
-    background-color: ${theme.colors.field04};
-    color: ${theme.colors.text02};
-  }
-`;
-
-export const required = (theme: Theme) => css`
-  color: ${theme.colors.textAlert};
-`;
-
-export const univAuthButton = (theme: Theme) => css`
-  ${theme.fonts.label.large.SB14};
-  position: absolute;
-  right: 1.2rem;
-  top: 1rem;
-  padding: 0.7rem 1.6rem;
-  border-radius: 1rem;
-  color: ${theme.colors.text01};
-  background-color: ${theme.colors.primaryMint};
-  border: none;
-
-  :disabled {
-    color: ${theme.colors.text02};
-    background-color: ${theme.colors.field04};
-  }
-`;
-
-export const editButton = (theme: Theme) => css`
-  color: ${theme.colors.text06};
-  background-color: ${theme.colors.field01};
-  border: 0.1rem solid ${theme.colors.line02};
-`;
-
-export const errorMessage = (theme: Theme) => css`
-  ${theme.fonts.label.large.R14};
-  color: ${theme.colors.textAlert};
-`;
diff --git a/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.tsx b/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.tsx
index 50110a3..a45ef46 100644
--- a/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.tsx
+++ b/src/app/join/components/Researcher/JoinEmailStep/UnivAuthInput/UnivAuthInput.tsx
@@ -1,16 +1,18 @@
+'use client';
+
 import { useState } from 'react';
 import { Controller, useFormContext, useWatch } from 'react-hook-form';
 
 import AuthCodeInput from './AuthCodeInput/AuthCodeInput';
 import {
-  editButton,
-  errorMessage,
-  inputContainer,
+  univInputWrapper,
   required,
   univAuthButton,
-  univInputWrapper,
-} from './UnivAuthInput.styles';
+  editButton,
+  errorMessage,
+} from './UnivAuthInput.css';
 import EmailToast from '../../../EmailToast/EmailToast';
+import { inputContainer, inputLabel, joinInput } from '../../../JoinInput/JoinInput.css';
 
 import useAuthCodeTimer from '@/app/join/hooks/useAuthCodeTimer';
 import useSendUnivAuthCodeMutation from '@/app/join/hooks/useSendUnivAuthCodeMutation';
@@ -24,7 +26,12 @@ interface UnivAuthInputProps {
 const UnivAuthInput = ({ isEmailVerified, handleVerifyEmail }: UnivAuthInputProps) => {
   const { control } = useFormContext<ResearcherJoinSchemaType>();
 
-  const { mutate: sendEmail, error: sendError } = useSendUnivAuthCodeMutation();
+  const {
+    mutate: sendEmail,
+    error: sendError,
+    isPending: isLoadingSend,
+  } = useSendUnivAuthCodeMutation();
+
   const [isEmailSent, setIsEmailSent] = useState(false);
   const [isToastOpen, setIsToastOpen] = useState(false);
 
@@ -52,39 +59,39 @@ const UnivAuthInput = ({ isEmailVerified, handleVerifyEmail }: UnivAuthInputProp
   };
 
   return (
-    <div css={inputContainer}>
-      <label>
+    <div className={inputContainer}>
+      <label className={inputLabel}>
         <span>학교 메일 인증</span>
-        <span css={required}>*</span>
+        <span className={required}>*</span>
       </label>
 
       <Controller
         name="univEmail"
         control={control}
-        render={({ field, fieldState }) => {
-          return (
-            <>
-              <div css={univInputWrapper}>
-                <input
-                  {...field}
-                  placeholder="학교 메일 입력"
-                  aria-invalid={fieldState.invalid ? true : false}
-                  disabled={isEmailSent || isEmailVerified}
-                />
-                <button
-                  type="button"
-                  css={[univAuthButton, isEmailSent && editButton]}
-                  disabled={(!isEmailSent && !field.value) || isEmailVerified}
-                  onClick={isEmailSent ? handleClickEdit : handleSendUnivAuthCode}
-                >
-                  {isEmailSent ? '수정' : '인증번호 전송'}
-                </button>
-              </div>
-              {fieldState.error && <span css={errorMessage}>{fieldState.error.message}</span>}
-              {sendError && <span css={errorMessage}>{sendError.message}</span>}
-            </>
-          );
-        }}
+        render={({ field, fieldState }) => (
+          <>
+            <div className={univInputWrapper}>
+              <input
+                {...field}
+                style={{ width: '100%' }}
+                className={joinInput}
+                placeholder="학교 메일 입력"
+                aria-invalid={fieldState.invalid ? true : false}
+                disabled={isEmailSent || isEmailVerified}
+              />
+              <button
+                type="button"
+                className={`${univAuthButton} ${isEmailSent ? editButton : ''}`}
+                disabled={(!isEmailSent && !field.value) || isEmailVerified || isLoadingSend}
+                onClick={isEmailSent ? handleClickEdit : handleSendUnivAuthCode}
+              >
+                {isLoadingSend ? '전송 중...' : isEmailSent ? '수정' : '인증번호 전송'}
+              </button>
+            </div>
+            {fieldState.error && <span className={errorMessage}>{fieldState.error.message}</span>}
+            {sendError && <span className={errorMessage}>{sendError.message}</span>}
+          </>
+        )}
       />
 
       {isEmailSent && (
diff --git a/src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.styles.ts b/src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.styles.ts
deleted file mode 100644
index 8694378..0000000
--- a/src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.styles.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { css, Theme } from '@emotion/react';
-
-export const joinContentContainer = (theme: Theme) => css`
-  background-color: ${theme.colors.field02};
-  width: 100%;
-  display: flex;
-  flex-direction: column;
-  gap: 2.8rem;
-  border-radius: 1.2rem;
-  padding: 3.2rem 4rem;
-`;
-
-export const joinButton = (theme: Theme) => css`
-  ${theme.fonts.body.normal.SB16};
-  background-color: ${theme.colors.primaryMint};
-  color: ${theme.colors.text01};
-  border-radius: 1.2rem;
-  padding: 1.2rem 0;
-  width: 20rem;
-  align-items: center;
-  margin-bottom: 5.6rem;
-
-  :disabled {
-    color: ${theme.colors.text02};
-    background-color: ${theme.colors.field04};
-  }
-`;
diff --git a/src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.tsx b/src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.tsx
index 94ecebc..8646078 100644
--- a/src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.tsx
+++ b/src/app/join/components/Researcher/JoinInfoStep/JoinInfoStep.tsx
@@ -1,9 +1,10 @@
-import { useFormContext, useWatch } from 'react-hook-form';
+'use client';
 
-import { joinButton } from './JoinInfoStep.styles';
-import { joinContentContainer, joinForm } from '../../../JoinPage.styles';
-import JoinInput from '../../JoinInput/JoinInput';
+import { useFormContext, useWatch } from 'react-hook-form';
 
+import JoinInput from '@/app/join/components/JoinInput/JoinInput';
+import { joinButton } from '@/app/join/components/Participant/JoinInfoStep/JoinInfoStep.css';
+import { joinContentContainer, joinForm } from '@/app/join/JoinPage.css';
 import { ResearcherJoinSchemaType } from '@/schema/join/ResearcherJoinSchema';
 
 interface JoinInfoStepProps {
@@ -15,13 +16,13 @@ const JoinInfoStep = ({ handleSubmit }: JoinInfoStepProps) => {
     control,
     formState: { errors },
   } = useFormContext<ResearcherJoinSchemaType>();
-  const values = useWatch({ name: ['name', 'univName', 'major'], control });
 
+  const values = useWatch({ name: ['name', 'univName', 'major'], control });
   const isAllFilled = values.every((value) => (value ?? '').trim() !== '' && value !== undefined);
 
   return (
-    <section css={joinForm}>
-      <div css={joinContentContainer}>
+    <section className={joinForm}>
+      <div className={joinContentContainer}>
         <JoinInput<ResearcherJoinSchemaType>
           name="name"
           control={control}
@@ -53,7 +54,7 @@ const JoinInfoStep = ({ handleSubmit }: JoinInfoStepProps) => {
         />
       </div>
       <button
-        css={joinButton}
+        className={joinButton}
         onClick={handleSubmit}
         disabled={!(isAllFilled && Object.keys(errors).length === 0)}
       >

From f54e675ad87d0f35d20bcb094549935efdec899e Mon Sep 17 00:00:00 2001
From: Gyuhan Park <rbgks1937@naver.com>
Date: Wed, 5 Feb 2025 20:49:24 +0900
Subject: [PATCH 08/11] =?UTF-8?q?[YS-243]=20design:=20joinLayout=20?=
 =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=EB=A7=81=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/join/JoinPage.css.ts |  8 ++++++++
 src/app/join/layout.tsx      | 16 +++-------------
 2 files changed, 11 insertions(+), 13 deletions(-)

diff --git a/src/app/join/JoinPage.css.ts b/src/app/join/JoinPage.css.ts
index 60820c3..47a3da1 100644
--- a/src/app/join/JoinPage.css.ts
+++ b/src/app/join/JoinPage.css.ts
@@ -3,6 +3,14 @@ import { style } from '@vanilla-extract/css';
 import { colors } from '@/styles/colors';
 import { fonts } from '@/styles/fonts.css';
 
+export const joinPageLayout = style({
+  display: 'flex',
+  backgroundColor: colors.field01,
+  width: '56rem',
+  margin: '0 auto',
+  minHeight: 'calc(100vh - 12.2rem)',
+});
+
 export const joinLayout = style({
   display: 'flex',
   flexDirection: 'column',
diff --git a/src/app/join/layout.tsx b/src/app/join/layout.tsx
index 5df3534..260b7b9 100644
--- a/src/app/join/layout.tsx
+++ b/src/app/join/layout.tsx
@@ -1,23 +1,13 @@
-'use client';
-
-import { Theme } from '@emotion/react';
-import { css } from '@emotion/react';
 import { Suspense } from 'react';
 
+import { joinPageLayout } from './JoinPage.css';
+
 function JoinLayout({ children }: { children: React.ReactNode }) {
   return (
-    <div css={joinLayout}>
+    <div className={joinPageLayout}>
       <Suspense>{children}</Suspense>
     </div>
   );
 }
 
-const joinLayout = (theme: Theme) => css`
-  display: flex;
-  background-color: ${theme.colors.field01};
-  width: 56rem;
-  margin: 0 auto;
-  min-height: calc(100vh - 12.2rem);
-`;
-
 export default JoinLayout;

From 628c4f37a0a0025ff5e942fc19e222a908980d1e Mon Sep 17 00:00:00 2001
From: Gyuhan Park <rbgks1937@naver.com>
Date: Wed, 5 Feb 2025 20:56:06 +0900
Subject: [PATCH 09/11] =?UTF-8?q?[YS-243]=20feat:=20beusable=20=EC=8A=A4?=
 =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8=20=ED=83=9C=EA=B7=B8=20=EC=B6=94?=
 =?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/layout.tsx | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 98c004d..035ebc2 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,4 +1,5 @@
 import type { Metadata } from 'next';
+import Script from 'next/script';
 
 import Providers from './providers';
 
@@ -23,6 +24,25 @@ export default function RootLayout({
           {children}
           <Footer />
         </Providers>
+        <Script
+          id="beusable-script"
+          dangerouslySetInnerHTML={{
+            __html: `
+            (function(w, d, a){
+                w.__beusablerumclient__ = {
+                    load: function(src){
+                        var b = d.createElement("script");
+                        b.src = src; 
+                        b.async = true; 
+                        b.type = "text/javascript";
+                        d.getElementsByTagName("head")[0].appendChild(b);
+                    }
+                };
+                w.__beusablerumclient__.load(a + "?url=" + encodeURIComponent(d.URL));
+            })(window, document, "//rum.beusable.net/load/b250203e183750u380");
+          `,
+          }}
+        />
       </body>
     </html>
   );

From 01b1109fcc516c402584d3e65cb907fc09d5572f Mon Sep 17 00:00:00 2001
From: Gyuhan Park <rbgks1937@naver.com>
Date: Wed, 5 Feb 2025 20:56:38 +0900
Subject: [PATCH 10/11] =?UTF-8?q?[YS-243]=20fix:=20=EB=B9=84=EB=8C=80?=
 =?UTF-8?q?=EB=A9=B4=EC=9D=BC=20=EA=B2=BD=EC=9A=B0=20=ED=99=88=20=EC=8B=A4?=
 =?UTF-8?q?=ED=97=98=EA=B3=B5=EA=B3=A0=20=EC=9E=A5=EC=86=8C=20=ED=85=8D?=
 =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B9=84=EB=8C=80=EB=A9=B4=20=EC=B2=98?=
 =?UTF-8?q?=EB=A6=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/home/components/PostCard/PostCard.tsx | 2 +-
 src/types/post.ts                             | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/app/home/components/PostCard/PostCard.tsx b/src/app/home/components/PostCard/PostCard.tsx
index 86667d9..8258577 100644
--- a/src/app/home/components/PostCard/PostCard.tsx
+++ b/src/app/home/components/PostCard/PostCard.tsx
@@ -34,7 +34,7 @@ const PostCard = ({ post }: PostCardProps) => {
       <Link href={`/post/${experimentPostId}`} key={experimentPostId} className={postCardLayout}>
         <div className={postHeader}>
           <div className={postCardHeader}>
-            <span className={postLocation}>{univName}</span>
+            <span className={postLocation}>{univName ? univName : '비대면'}</span>
             <div className={postCardRightHeader}>
               <Icon icon="Eye" width={18} />
               <span className={postViews}>{views}</span>
diff --git a/src/types/post.ts b/src/types/post.ts
index 5ee8c8c..d3d7269 100644
--- a/src/types/post.ts
+++ b/src/types/post.ts
@@ -7,7 +7,7 @@ export interface PostInfo {
   experimentPostId: number;
   title: string;
   views: number;
-  univName: string;
+  univName: string | null;
   reward: string;
   durationInfo: {
     startDate: string | null;

From 03a5b8d9aad4f8dd4d22aea3f6f2a1eabcb09235 Mon Sep 17 00:00:00 2001
From: Gyuhan Park <rbgks1937@naver.com>
Date: Wed, 5 Feb 2025 20:56:57 +0900
Subject: [PATCH 11/11] =?UTF-8?q?[YS-243]=20fix:=20=EB=8C=80=EB=A9=B4/?=
 =?UTF-8?q?=EB=B9=84=EB=8C=80=EB=A9=B4=20value=20=EB=B0=98=EB=8C=80?=
 =?UTF-8?q?=EB=A1=9C=20=EC=B2=98=EB=A6=AC=ED=95=9C=20=EB=AC=B8=EC=A0=9C=20?=
 =?UTF-8?q?=ED=95=B4=EA=B2=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../ProgressMethodFilter/ProgressMethodFilter.tsx         | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx b/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx
index da88816..af35b06 100644
--- a/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx
+++ b/src/app/home/components/PostContainer/ProgressMethodFilter/ProgressMethodFilter.tsx
@@ -16,8 +16,8 @@ interface FilterOption {
 
 const options: FilterOption[] = [
   { label: '전체', value: 'ALL' },
-  { label: '대면', value: 'ONLINE' },
-  { label: '비대면', value: 'OFFLINE' },
+  { label: '대면', value: 'OFFLINE' },
+  { label: '비대면', value: 'ONLINE' },
 ];
 
 interface ProgressMethodFilterProps {
@@ -63,10 +63,10 @@ const ProgressMethodFilter = ({ onChange }: ProgressMethodFilterProps) => {
             <Select.Item value="ALL" className={selectItem}>
               <Select.ItemText>전체</Select.ItemText>
             </Select.Item>
-            <Select.Item value="ONLINE" className={selectItem}>
+            <Select.Item value="OFFLINE" className={selectItem}>
               <Select.ItemText>대면</Select.ItemText>
             </Select.Item>
-            <Select.Item value="OFFLINE" className={selectItem}>
+            <Select.Item value="ONLINE" className={selectItem}>
               <Select.ItemText>비대면</Select.ItemText>
             </Select.Item>
           </Select.Group>