diff --git a/.github/workflows/cicd-be.yml b/.github/workflows/cicd-be.yml
new file mode 100644
index 00000000..362c33fc
--- /dev/null
+++ b/.github/workflows/cicd-be.yml
@@ -0,0 +1,112 @@
+name: Backend CI/CD
+
+on:
+ push:
+ branches: [ main ]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out Repository
+ uses: actions/checkout@v3
+ with:
+ token: ${{ secrets.ACTION_TOKEN }}
+ submodules: true
+
+ - name: Display first three lines of application.yml (debug)
+ run: head -n 3 src/main/resources/application.yml
+
+ - name: Set up JDK21
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'corretto'
+ java-version: '21'
+
+ - name: Gradle 캐싱
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ ${{ runner.os }}-gradle-
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Build with Gradle
+ run: ./gradlew clean build
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: cicdsample
+ path: build/libs/*.jar
+
+ - name: Slack notification when build fail
+ if: failure()
+ uses: 8398a7/action-slack@v3
+ with:
+ status: ${{ job.status }}
+ author_name: [CI/CD] 백엔드 빌드 실패
+ fields: repo, message, commit, author, action, eventName, ref, workflow, job, took
+ env:
+ SLACK_COLOR: '#FF2D00'
+ SLACK_USERNAME: 'Github Action'
+ SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_CICD }}
+
+ deploy:
+ needs: build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download artifact
+ uses: actions/download-artifact@v2
+ with:
+ name: cicdsample
+
+ - name: Setup SSH
+ uses: webfactory/ssh-agent@v0.5.4
+ with:
+ ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
+
+ - name: Add known hosts
+ run: |
+ ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts
+ chmod 644 ~/.ssh/known_hosts
+
+ - name: SCP transfer
+ run: scp *.jar ${{ secrets.SSH_USER }}@${{ secrets.SERVER_IP }}:~/deploy
+
+ - name: Execute remote shell script
+ run: |
+ ssh ${{ secrets.SSH_USER }}@${{ secrets.SERVER_IP }} "chmod +x ./deploy.sh"
+ ssh ${{ secrets.SSH_USER }}@${{ secrets.SERVER_IP }} "./deploy.sh"
+
+ - name: Slack notification when deploy fail
+ if: failure()
+ uses: 8398a7/action-slack@v3
+ with:
+ status: ${{ job.status }}
+ author_name: [CI/CD] 백엔드 배포 실패
+ fields: repo, message, commit, author, action, eventName, ref, workflow, job, took
+ env:
+ SLACK_COLOR: '#FF2D00'
+ SLACK_USERNAME: 'Github Action'
+ SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_CICD }}
+
+ - name: Slack notification when deploy success
+ if: success()
+ uses: 8398a7/action-slack@v3
+ with:
+ status: ${{ job.status }}
+ author_name: [CI/CD] 백엔드 배포 성공
+ fields: repo, message, commit, author, action, eventName, ref, workflow, job, took
+ env:
+ SLACK_COLOR: '#0019F4'
+ SLACK_USERNAME: 'Github Action'
+ SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_CICD }}
diff --git a/.github/workflows/test-fe.yml b/.github/workflows/test-fe.yml
deleted file mode 100644
index 4c2de232..00000000
--- a/.github/workflows/test-fe.yml
+++ /dev/null
@@ -1,76 +0,0 @@
-name: Frontend PR Test
-
-on:
- pull_request:
- branches:
- - main
- - develop
- paths:
- - '.github/**'
- - 'frontend/**'
-
-jobs:
- test:
- runs-on: ubuntu-latest
- timeout-minutes: 10
-
- permissions:
- checks: write
- pull-requests: write
-
- steps:
- - name: Repository 체크아웃
- uses: actions/checkout@v3
-
- - name: Node 설정
- uses: actions/setup-node@v3
- with:
- node-version: '18.16.1'
-
- - name: node_modules 캐싱
- id: cache
- uses: actions/cache@v3
- with:
- path: '**/frontend/node_modules'
- key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}
- restore-keys: |
- ${{ runner.os }}-node-
-
- - name: 의존성 설치
- working-directory: frontend/
- if: steps.cache.outputs.cache-hit != 'true'
- run: yarn install --pure-lockfile
-
- - name: 테스트 실행
- working-directory: frontend/
- run: yarn test
- continue-on-error: true
-
- - name: 테스트 결과 PR에 코멘트 등록
- uses: EnricoMi/publish-unit-test-result-action@v2
- if: always()
- with:
- files: '**/frontend/test-results/results.xml'
-
- - name: 테스트 실패 시, 실패한 코드 라인에 Check 코멘트를 등록
- uses: mikepenz/action-junit-report@v3
- if: always()
- with:
- report_paths: '**/frontend/test-results/results.xml'
- token: ${{ github.token }}
-
- - name: build 실패 시 Slack으로 알립니다
- uses: 8398a7/action-slack@v3
- with:
- status: ${{ job.status }}
- author_name: 프론트엔드 테스트 실패 알림
- fields: repo, message, commit, author, action, eventName, ref, workflow, job, took
- env:
- SLACK_CHANNEL: group-dev
- SLACK_COLOR: '#FF2D00'
- SLACK_USERNAME: 'Github Action'
- SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
- SLACK_TITLE: Build Failure - ${{ github.event.pull_request.title }}
- SLACK_MESSAGE: PR Url - ${{ github.event.pull_request.url }}
- SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
- if: failure()
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 160ab01e..bd060e54 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -5,9 +5,6 @@ on:
branches:
- main
- develop
- paths:
- - '.github/**'
- - 'backend/**'
jobs:
test:
@@ -22,11 +19,11 @@ jobs:
- name: Repository 체크아웃
uses: actions/checkout@v3
- - name: JDK 11 설정
+ - name: JDK 21 설정
uses: actions/setup-java@v3
with:
- java-version: 11
- distribution: temurin
+ java-version: 21
+ distribution: corretto
- name: Gradle 캐싱
uses: actions/cache@v3
@@ -39,11 +36,9 @@ jobs:
${{ runner.os }}-gradle-
- name: Gradle 권한 부여
- working-directory: backend/
- run: chmod +x ./gradlew
+ run: chmod +x gradlew
- name: 테스트 실행
- working-directory: backend/
run: ./gradlew --info test
- name: 테스트 결과 PR에 코멘트 등록
@@ -72,5 +67,5 @@ jobs:
SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
SLACK_TITLE: Build Failure - ${{ github.event.pull_request.title }}
SLACK_MESSAGE: PR Url - ${{ github.event.pull_request.url }}
- SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_PR_TEST }}
if: failure()
diff --git a/backend/.gitignore b/.gitignore
similarity index 100%
rename from backend/.gitignore
rename to .gitignore
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..8e4ce64a
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "src/main/resources"]
+ path = src/main/resources
+ url = git@github.com:fun-eat/funeat-env.git
+ branch = main
diff --git a/README.md b/README.md
deleted file mode 100644
index a3819192..00000000
--- a/README.md
+++ /dev/null
@@ -1,107 +0,0 @@
-
-
-
-
-
-
-
-
-
-
궁금해? 맛있을걸? 먹어봐!
-🍙 편의점 음식 리뷰 & 꿀조합 공유 서비스 🍙
-
-
-
-[![Application](http://img.shields.io/badge/funeat.site-D8EAFF?style=for-the-badge&logo=aHR0cHM6Ly9naXRodWIuY29tL3dvb3dhY291cnNlLXRlYW1zLzIwMjMtZnVuLWVhdC9hc3NldHMvODA0NjQ5NjEvOWI1OWY3NzktY2M5MS00MTJhLWE3NDUtZGQ3M2IzY2UxZGNk&logoColor=black&link=https://funeat.site/)](https://funeat.site/)
-[![WIKI](http://img.shields.io/badge/-GitHub%20WiKi-FFEC99?style=for-the-badge&logoColor=black&link=https://github.com/woowacourse-teams/2023-fun-eat/wiki)](https://github.com/woowacourse-teams/2023-fun-eat/wiki)
-[![Release](https://img.shields.io/github/v/release/woowacourse-teams/2023-fun-eat?style=for-the-badge&color=FFCFCF)](https://github.com/woowacourse-teams/2023-fun-eat/releases/tag/v1.3.0)
-
-
-
-
-
-# 🥄 서비스 소개
-
-![1_메인페이지](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/9663f7b5-cd38-4f06-86fb-c6636fc364c6)
-
-
-
-## 1. 편의점마다 특색있는 음식 궁금해?
-
-![5_상품목록](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/03fb9955-61fa-4228-a270-ce9dffc710c6)
-![6_상품상세](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/694bc8db-74bd-4fa1-b499-900cd27f5028)
-![4_검색](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/6a157e08-79d8-450b-9511-ffa461000a22)
-
-
-
-
-## 2. 솔직한 리뷰를 보면 더 맛있을걸?
-
-![2_리뷰](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/4bf5ecd7-df08-45d0-b592-8629f3a4e3e6)
-
-
-
-
-## 3. 생각지 못했던 꿀조합, 먹어봐!
-
-![3_꿀조합](https://github.com/woowacourse-teams/2023-fun-eat/assets/55427367/8e560b40-d039-47ce-ad29-5e244cba4bf2)
-
-
-
-
-# 🛠️ 기술 스택
-
-### 백엔드
-
-
-
-
-
-
-
-### 프론트엔드
-
-
-
-
-
-
-
-### 인프라
-
-
-
-
-
-
-
-
-# 인프라 구조
-
-### CI/CD
-
-
-
-
-
-### 구조
-
-
-
-
-
-
-
-
-# 👨👨👧👧👩👦👦 팀원
-
-| Frontend | Frontend | Frontend | Backend | Backend | Backend | Backend |
-| :-------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------: |
-| | | | | | | |
-| [🐰 타미](https://github.com/xodms0309) | [🌞 해온](https://github.com/hae-on) | [🐧 황펭](https://github.com/Leejin-Yang) | [😺 로건](https://github.com/70825) | [🥭 망고](https://github.com/Go-Jaecheol) | [👻 오잉](https://github.com/hanueleee) | [🍖 우가](https://github.com/wugawuga) |
-
-
-
-
-
-
diff --git a/backend/build.gradle b/backend/build.gradle
deleted file mode 100644
index 3d46c806..00000000
--- a/backend/build.gradle
+++ /dev/null
@@ -1,40 +0,0 @@
-plugins {
- id 'java'
- id 'org.springframework.boot' version '2.7.13'
- id 'io.spring.dependency-management' version '1.0.15.RELEASE'
-}
-
-group = 'com.funeat'
-version = '0.0.1-SNAPSHOT'
-
-java {
- sourceCompatibility = '11'
-}
-
-repositories {
- mavenCentral()
-}
-
-dependencies {
- implementation 'org.springframework.boot:spring-boot-starter-validation'
- implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
- implementation 'org.springframework.boot:spring-boot-starter-web'
- runtimeOnly 'com.mysql:mysql-connector-j'
- testImplementation 'org.springframework.boot:spring-boot-starter-test'
- testImplementation 'io.rest-assured:rest-assured:4.4.0'
- testRuntimeOnly 'com.h2database:h2'
-
- implementation 'org.springdoc:springdoc-openapi-ui:1.7.0'
- implementation 'com.github.maricn:logback-slack-appender:1.4.0'
-
- implementation 'org.springframework.boot:spring-boot-starter-actuator'
- runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
-
- implementation 'com.amazonaws:aws-java-sdk-s3:1.12.547'
-
- implementation 'org.springframework.session:spring-session-jdbc'
-}
-
-tasks.named('test') {
- useJUnitPlatform()
-}
diff --git a/backend/settings.gradle b/backend/settings.gradle
deleted file mode 100644
index 7f580408..00000000
--- a/backend/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-rootProject.name = 'funeat'
diff --git a/backend/src/main/java/com/funeat/product/dto/SearchProductsResponse.java b/backend/src/main/java/com/funeat/product/dto/SearchProductsResponse.java
deleted file mode 100644
index ccdeade5..00000000
--- a/backend/src/main/java/com/funeat/product/dto/SearchProductsResponse.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.funeat.product.dto;
-
-import com.funeat.common.dto.PageDto;
-import java.util.List;
-
-public class SearchProductsResponse {
-
- private final PageDto page;
- private final List products;
-
- public SearchProductsResponse(final PageDto page, final List products) {
- this.page = page;
- this.products = products;
- }
-
- public static SearchProductsResponse toResponse(final PageDto page, final List products) {
- return new SearchProductsResponse(page, products);
- }
-
- public PageDto getPage() {
- return page;
- }
-
- public List getProducts() {
- return products;
- }
-}
diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml
deleted file mode 100644
index 0c1a7222..00000000
--- a/backend/src/main/resources/application-dev.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-spring:
- datasource:
- driver-class-name: com.mysql.cj.jdbc.Driver
- url: { DEV_DB_URL }
- username: { DEV_DB_USERNAME }
- password: { DEV_DB_PASSWORD }
-
- jpa:
- hibernate:
- ddl-auto: update
- properties:
- hibernate:
- format_sql: true
- show_sql: true
-
-kakao:
- rest-api-key: { DEV_REST_API_KEY }
- redirect-uri: { DEV_REDIRECT_URI }
- admin-key: { DEV_ADMIN_KEY }
-
-management:
- endpoints:
- enabled-by-default: false
- web:
- exposure:
- include: health, metrics, prometheus
- base-path: { ACTUATOR_BASE_PATH }
- jmx:
- exposure:
- exclude: "*"
- endpoint:
- health:
- enabled: true
- metrics:
- enabled: true
- prometheus:
- enabled: true
-
-cloud:
- aws:
- region:
- static: { S3_REGION }
- s3:
- bucket: { S3_BUCKET }
- folder: { S3_DEV_FOLDER }
- cloudfrontPath: { S3_DEV_CLOUDFRONT_PATH }
diff --git a/backend/src/main/resources/application-local.yml b/backend/src/main/resources/application-local.yml
deleted file mode 100644
index 27ba9bcc..00000000
--- a/backend/src/main/resources/application-local.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-spring:
- datasource:
- driver-class-name: com.mysql.cj.jdbc.Driver
- url:
- username:
- password:
-
- jpa:
- hibernate:
- ddl-auto: create
- properties:
- hibernate:
- format_sql: true
- show_sql: true
-logging:
- level:
- org.hibernate.type.descriptor.sql: trace
-
-kakao:
- rest-api-key: { LOCAL_REST_API_KEY }
- redirect-uri: { LOCAL_REDIRECT_URI }
- admin-key: { LOCAL_ADMIN_KEY }
-
-cloud:
- aws:
- region:
- static: { S3_REGION }
- s3:
- bucket: { S3_BUCKET }
- folder: { S3_LOCAL_FOLDER }
- cloudfrontPath: { S3_LOCAL_CLOUDFRONT_PATH }
diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml
deleted file mode 100644
index 1943e2bf..00000000
--- a/backend/src/main/resources/application-prod.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-spring:
- datasource:
- driver-class-name: com.mysql.cj.jdbc.Driver
- url: { PROD_DB_URL }
- username: { PROD_DB_USERNAME }
- password: { PROD_DB_PASSWORD }
-
- jpa:
- hibernate:
- ddl-auto: none
- properties:
- hibernate:
- show_sql: true
-
-kakao:
- rest-api-key: { PROD_REST_API_KEY }
- redirect-uri: { PROD_REDIRECT_URI }
- admin-key: { PROD_ADMIN_KEY }
-
-management:
- endpoints:
- enabled-by-default: false
- web:
- exposure:
- include: health, metrics, prometheus
- base-path: { ACTUATOR_BASE_PATH }
- jmx:
- exposure:
- exclude: "*"
- endpoint:
- health:
- enabled: true
- metrics:
- enabled: true
- prometheus:
- enabled: true
-
-cloud:
- aws:
- region:
- static: { S3_REGION }
- s3:
- bucket: { S3_BUCKET }
- folder: { S3_PROD_FOLDER }
- cloudfrontPath: { S3_PROD_CLOUDFRONT_PATH }
diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml
deleted file mode 100644
index 941adb9c..00000000
--- a/backend/src/main/resources/application.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-spring:
- profiles:
- active: { DEPLOY_ACTIVE }
- servlet:
- multipart:
- enabled: true
- maxFileSize: 10MB
- maxRequestSize: 15MB
- task:
- execution:
- pool:
- core-size: { THREAD_CORE_SIZE }
- max-size: { THREAD_MAX_SIZE }
- session:
- store-type: jdbc
- jdbc:
- initialize-schema: never
- datasource:
- hikari:
- connection-timeout: { CONNECTION_TIMEOUT }
- maximum-pool-size: { MAXIMUM_POOL_SIZE }
-
-springdoc:
- swagger-ui:
- path: /funeat-api
- enabled: true
- tags-sorter: alpha
-
-logging:
- file:
- path: { LOG_DIR }
-
-server:
- tomcat:
- threads:
- max: { MAX_THREADS }
- max-connections: { MAX_CONNECTIONS }
- accept-count: { ACCEPT_COUNT }
-
-back-office:
- id: { BACK_OFFICE_ID }
- key: { BACK_OFFICE_KEY }
diff --git a/backend/src/main/resources/logback-spring-dev.xml b/backend/src/main/resources/logback-spring-dev.xml
deleted file mode 100644
index b86839da..00000000
--- a/backend/src/main/resources/logback-spring-dev.xml
+++ /dev/null
@@ -1,121 +0,0 @@
-
-
-
-
-
-
-
- ${dev_slack_webhook_uri}
-
- ${log_pattern}
-
- open-macbook
- :face_with_symbols_on_mouth:
- true
-
-
-
-
-
- WARN
-
-
-
-
-
- INFO
- ACCEPT
- DENY
-
- ${log_dir}/info.log
-
-
- ${log_dir}/info/info.%d{yyyy-MM-dd}_%i.log
-
- ${dev_file_size}
- ${dev_file_max_history}
-
-
-
- ${log_pattern}
-
- true
-
-
-
-
-
- WARN
- ACCEPT
- DENY
-
- ${log_dir}/warn.log
-
-
- ${log_dir}/warn/%d{yyyy-MM-dd}_%i.log
-
- ${dev_file_size}
- ${dev_file_max_history}
-
-
-
- ${log_pattern}
-
- true
-
-
-
-
-
- ERROR
- ACCEPT
- DENY
-
- ${log_dir}/error.log
-
-
- ${log_dir}/error/%d{yyyy-MM-dd}_%i.log
-
- ${dev_file_size}
- ${dev_file_max_history}
-
-
-
- ${log_pattern}
-
- true
-
-
-
-
- ${log_dir}/query_log.log
-
-
- ${log_dir}/query/%d{yyyy-MM-dd}_%i.log
-
- 10kb
- 1
-
-
-
- ${log_pattern}
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/backend/src/main/resources/logback-spring-prod.xml b/backend/src/main/resources/logback-spring-prod.xml
deleted file mode 100644
index 101ba8d7..00000000
--- a/backend/src/main/resources/logback-spring-prod.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-
-
-
-
- ${prod_slack_webhook_uri}
-
- ${log_pattern}
-
- open-macbook
- :face_with_symbols_on_mouth:
- true
-
-
-
-
-
- ERROR
-
-
-
-
-
- INFO
- ACCEPT
- DENY
-
- ${log_dir}/info.log
-
-
- ${log_dir}/info/info.%d{yyyy-MM-dd}_%i.log
-
- ${prod_file_size}
- ${prod_file_max_history}
-
-
-
- ${log_pattern}
-
- true
-
-
-
-
-
- WARN
- ACCEPT
- DENY
-
- ${log_dir}/warn.log
-
-
- ${log_dir}/warn/%d{yyyy-MM-dd}_%i.log
-
- ${prod_file_size}
- ${prod_file_max_history}
-
-
-
- ${log_pattern}
-
- true
-
-
-
-
-
- ERROR
- ACCEPT
- DENY
-
- ${log_dir}/error.log
-
-
- ${log_dir}/error/%d{yyyy-MM-dd}_%i.log
-
- ${prod_file_size}
- ${prod_file_max_history}
-
-
-
- ${log_pattern}
-
- true
-
-
-
-
-
-
-
-
-
-
-
diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml
deleted file mode 100644
index ccea7008..00000000
--- a/backend/src/main/resources/logback-spring.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/backend/src/main/resources/logback-variables.yml b/backend/src/main/resources/logback-variables.yml
deleted file mode 100644
index 82334293..00000000
--- a/backend/src/main/resources/logback-variables.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-log_pattern: { LOG_PATTERN }
-dev_slack_webhook_uri: { DEV_SLACK_WEBHOOK_URI }
-dev_file_size: { DEV_FILE_SIZE }
-dev_file_max_history: { DEV_FILE_MAX_HISTORY }
-prod_slack_webhook_uri: { PROD_SLACK_WEBHOOK_URI }
-prod_file_size: { PROD_FILE_SIZE }
-prod_file_max_history: { PROD_FILE_MAX_HISTORY }
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 00000000..46b9b6a4
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,48 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+ id ("org.springframework.boot") version "3.2.1"
+ id ("io.spring.dependency-management") version "1.1.4"
+ id ("java")
+ kotlin("jvm") version "1.9.20"
+ kotlin("plugin.spring") version "1.9.20"
+ kotlin("plugin.jpa") version "1.9.20"
+}
+
+group = "com.funeat"
+version = "0.0.1-SNAPSHOT"
+java.sourceCompatibility = JavaVersion.VERSION_21
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation("org.springframework.boot:spring-boot-starter-validation")
+ implementation("org.springframework.boot:spring-boot-starter-data-jpa")
+ implementation("org.springframework.boot:spring-boot-starter-web")
+ runtimeOnly("com.mysql:mysql-connector-j")
+ testImplementation("org.springframework.boot:spring-boot-starter-test")
+ testImplementation("io.rest-assured:rest-assured:5.3.2")
+ testRuntimeOnly("com.h2database:h2")
+
+ implementation("org.springdoc:springdoc-openapi-ui:1.7.0")
+ implementation("com.github.maricn:logback-slack-appender:1.4.0")
+
+ implementation("org.springframework.boot:spring-boot-starter-actuator")
+ runtimeOnly("io.micrometer:micrometer-registry-prometheus")
+
+ implementation("com.amazonaws:aws-java-sdk-s3:1.12.547")
+
+ implementation("org.springframework.session:spring-session-jdbc")
+}
+
+tasks.withType {
+ kotlinOptions {
+ jvmTarget = "21"
+ }
+}
+
+tasks.withType {
+ useJUnitPlatform()
+}
diff --git a/frontend/.babelrc.json b/frontend/.babelrc.json
deleted file mode 100644
index 2c055d07..00000000
--- a/frontend/.babelrc.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "sourceType": "unambiguous",
- "presets": [
- [
- "@babel/preset-env",
- {
- "targets": {
- "chrome": 100
- }
- }
- ],
- "@babel/preset-typescript",
- "@babel/preset-react"
- ],
- "plugins": ["babel-plugin-styled-components"]
-}
diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js
deleted file mode 100644
index 963fcc6c..00000000
--- a/frontend/.eslintrc.js
+++ /dev/null
@@ -1,113 +0,0 @@
-module.exports = {
- env: {
- browser: true,
- es2021: true,
- },
- extends: [
- 'eslint:recommended',
- 'plugin:@typescript-eslint/recommended',
- 'plugin:react/recommended',
- 'plugin:storybook/recommended',
- 'plugin:import/recommended',
- ],
- ignorePatterns: ['*.js'],
- overrides: [
- {
- env: {
- node: true,
- },
- files: ['.eslintrc.{js,cjs}'],
- parserOptions: {
- sourceType: 'script',
- },
- },
- {
- env: {
- jest: true,
- },
- files: ['__tests__/**/*.{ts,tsx}'],
- parserOptions: {
- ecmaVersion: 'latest',
- sourceType: 'module',
- project: './tsconfig.json',
- tsconfigRootDir: __dirname,
- },
- },
- ],
- parser: '@typescript-eslint/parser',
- parserOptions: {
- ecmaVersion: 'latest',
- sourceType: 'module',
- project: './tsconfig.json',
- tsconfigRootDir: __dirname,
- },
- plugins: ['@typescript-eslint', 'react', 'import'],
- rules: {
- 'react/react-in-jsx-scope': 'off',
- '@typescript-eslint/no-var-requires': 0,
- '@typescript-eslint/consistent-type-imports': [
- 'error',
- {
- prefer: 'type-imports',
- disallowTypeAnnotations: false,
- },
- ],
- 'react/jsx-key': [
- 'error',
- {
- warnOnDuplicates: true,
- },
- ],
- 'react/self-closing-comp': [
- 'error',
- {
- component: true,
- html: true,
- },
- ],
- 'import/order': [
- 'error',
- {
- groups: ['builtin', 'external', 'internal', ['parent', 'sibling', 'index'], 'object', 'unknown'],
- pathGroups: [
- {
- pattern: '@storybook/**',
- group: 'external',
- },
- {
- pattern: '@fun-eat/**',
- group: 'external',
- },
- {
- pattern: '@tanstack/**',
- group: 'external',
- },
- {
- pattern: '@*/**',
- group: 'unknown',
- },
- {
- pattern: '@*',
- group: 'unknown',
- },
- ],
- pathGroupsExcludedImportTypes: ['unknown'],
- alphabetize: {
- order: 'asc',
- caseInsensitive: true,
- },
- 'newlines-between': 'always',
- },
- ],
- 'import/no-unresolved': 'off',
- '@typescript-eslint/no-empty-function': 'off',
- '@typescript-eslint/ban-types': 'off',
- 'import/export': 'off',
- },
- settings: {
- 'import/resolver': {
- typescript: {},
- webpack: {},
- },
- },
-};
diff --git a/frontend/.gitignore b/frontend/.gitignore
deleted file mode 100644
index 8a2a134c..00000000
--- a/frontend/.gitignore
+++ /dev/null
@@ -1,9 +0,0 @@
-node_modules
-dist
-.DS_Store
-.AppleDouble
-.LSOverride
-.env
-coverage
-test-results
-junit.xml
diff --git a/frontend/.nvmrc b/frontend/.nvmrc
deleted file mode 100644
index 3876fd49..00000000
--- a/frontend/.nvmrc
+++ /dev/null
@@ -1 +0,0 @@
-18.16.1
diff --git a/frontend/.prettierrc b/frontend/.prettierrc
deleted file mode 100644
index 8c2d4fe2..00000000
--- a/frontend/.prettierrc
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "printWidth": 120,
- "singleQuote": true,
- "endOfLine": "auto",
- "semi": true,
- "tabWidth": 2
-}
diff --git a/frontend/.storybook/main.ts b/frontend/.storybook/main.ts
deleted file mode 100644
index 8791c62d..00000000
--- a/frontend/.storybook/main.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import type { StorybookConfig } from '@storybook/react-webpack5';
-import path from 'path';
-
-const config: StorybookConfig = {
- stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
- addons: [
- '@storybook/addon-links',
- '@storybook/addon-essentials',
- '@storybook/addon-interactions',
- 'msw-storybook-addon',
- '@storybook/addon-onboarding',
- ],
- framework: {
- name: '@storybook/react-webpack5',
- options: {},
- },
- core: {
- builder: {
- name: '@storybook/builder-webpack5',
- options: {
- fsCache: true,
- lazyCompilation: true,
- },
- },
- },
- webpackFinal: async (config) => {
- if (config.resolve) {
- config.resolve.alias = {
- ...config.resolve.alias,
- '@': path.resolve(__dirname, '../src'),
- '@apis': path.resolve(__dirname, '../src/apis'),
- '@assets': path.resolve(__dirname, '../src/assets'),
- '@components': path.resolve(__dirname, '../src/components'),
- '@constants': path.resolve(__dirname, '../src/constants'),
- '@hooks': path.resolve(__dirname, '../src/hooks'),
- '@mocks': path.resolve(__dirname, '../src/mocks'),
- '@pages': path.resolve(__dirname, '../src/pages'),
- '@router': path.resolve(__dirname, '../src/router'),
- '@styles': path.resolve(__dirname, '../src/styles'),
- '@utils': path.resolve(__dirname, '../src/utils'),
- };
- }
- const imageRule = config.module?.rules?.find((rule) => {
- const test = (rule as { test: RegExp }).test;
-
- if (!test) return false;
-
- return test.test('.svg');
- }) as { [key: string]: any };
-
- imageRule.exclude = /\.svg$/;
-
- config.module?.rules?.push({
- test: /\.svg$/,
- use: ['@svgr/webpack'],
- });
-
- return config;
- },
- docs: {
- autodocs: true,
- },
- staticDirs: ['../public'],
-};
-export default config;
diff --git a/frontend/.storybook/preview-body.html b/frontend/.storybook/preview-body.html
deleted file mode 100644
index a37b26cb..00000000
--- a/frontend/.storybook/preview-body.html
+++ /dev/null
@@ -1,129 +0,0 @@
-
-
-
-
-
-
-
diff --git a/frontend/.storybook/preview.tsx b/frontend/.storybook/preview.tsx
deleted file mode 100644
index 944ee1c0..00000000
--- a/frontend/.storybook/preview.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import React from 'react';
-import { FunEatProvider } from '@fun-eat/design-system';
-import type { Preview } from '@storybook/react';
-import { initialize, mswDecorator } from 'msw-storybook-addon';
-import {
- loginHandlers,
- productHandlers,
- reviewHandlers,
- rankingHandlers,
- memberHandlers,
- recipeHandlers,
- searchHandlers,
-} from '../src/mocks/handlers';
-import { BrowserRouter } from 'react-router-dom';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-
-initialize({
- serviceWorker: {
- url: '/mockServiceWorker.js',
- },
-});
-
-const queryClient = new QueryClient();
-
-export const decorators = [
- (Story) => (
-
-
-
-
-
-
-
- ),
- mswDecorator,
-];
-
-const preview: Preview = {
- parameters: {
- actions: { argTypesRegex: '^on[A-Z].*' },
- controls: {
- matchers: {
- color: /(background|color)$/i,
- date: /Date$/,
- },
- },
- msw: {
- handlers: [
- ...productHandlers,
- ...reviewHandlers,
- ...loginHandlers,
- ...rankingHandlers,
- ...memberHandlers,
- ...recipeHandlers,
- ...searchHandlers,
- ],
- },
- },
-};
-
-export default preview;
diff --git a/frontend/.stylelintrc.js b/frontend/.stylelintrc.js
deleted file mode 100644
index 914c886f..00000000
--- a/frontend/.stylelintrc.js
+++ /dev/null
@@ -1,15 +0,0 @@
-const { propertyOrdering, selectorOrdering } = require('stylelint-semantic-groups');
-
-propertyOrdering[0] = propertyOrdering[0].map((rule) => {
- rule.emptyLineBefore = 'never';
- return rule;
-});
-
-module.exports = {
- plugins: ['stylelint-order'],
- customSyntax: 'postcss-styled-syntax',
- rules: {
- 'order/order': selectorOrdering,
- 'order/properties-order': propertyOrdering,
- },
-};
diff --git a/frontend/__tests__/hooks/useImageUploader.test.ts b/frontend/__tests__/hooks/useImageUploader.test.ts
deleted file mode 100644
index 3de2da05..00000000
--- a/frontend/__tests__/hooks/useImageUploader.test.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { useImageUploader } from '@/hooks/common';
-import { renderHook, act } from '@testing-library/react';
-
-const originalCreateObjectUrl = URL.createObjectURL;
-const originalRevokeObjectUrl = URL.revokeObjectURL;
-
-beforeAll(() => {
- URL.createObjectURL = jest.fn(() => 'mocked url');
- URL.revokeObjectURL = jest.fn();
-});
-
-afterAll(() => {
- URL.createObjectURL = originalCreateObjectUrl;
- URL.revokeObjectURL = originalRevokeObjectUrl;
-});
-
-it('uploadImage를 사용하여 이미지 파일을 업로드할 수 있다.', () => {
- const { result } = renderHook(() => useImageUploader());
-
- const file = new File(['dummy content'], 'example.png', { type: 'image/png' });
-
- act(() => {
- result.current.uploadImage(file);
- });
-
- expect(result.current.imageFile).toBe(file);
- expect(result.current.previewImage).toBe('mocked url');
- expect(URL.createObjectURL).toHaveBeenCalledWith(file);
-});
-
-it('이미지 파일이 아니면 "이미지 파일만 업로드 가능합니다." 메시지를 보여주는 alert 창이 뜬다.', () => {
- const { result } = renderHook(() => useImageUploader());
-
- const file = new File(['dummy content'], 'example.txt', { type: 'text/plain' });
-
- global.alert = jest.fn();
-
- act(() => {
- result.current.uploadImage(file);
- });
-
- expect(global.alert).toHaveBeenCalledWith('이미지 파일만 업로드 가능합니다.');
-});
-
-it('deleteImage를 사용하여 이미지 파일을 삭제할 수 있다.', () => {
- const { result } = renderHook(() => useImageUploader());
-
- const file = new File(['dummy content'], 'example.png', { type: 'image/png' });
-
- act(() => {
- result.current.uploadImage(file);
- });
-
- act(() => {
- result.current.deleteImage();
- });
-
- expect(result.current.imageFile).toBeNull();
- expect(result.current.previewImage).toBe('');
- expect(URL.revokeObjectURL).toHaveBeenCalledWith('mocked url');
-});
diff --git a/frontend/__tests__/hooks/useStarRating.test.ts b/frontend/__tests__/hooks/useStarRating.test.ts
deleted file mode 100644
index 66ed60cb..00000000
--- a/frontend/__tests__/hooks/useStarRating.test.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useStarRatingHover } from '@/hooks/review';
-import { renderHook, act } from '@testing-library/react';
-
-it('handleMouseEnter를 사용하여 마우스 호버된 별점 값을 저장할 수 있다.', () => {
- const { result } = renderHook(() => useStarRatingHover());
-
- expect(result.current.hovering).toBe(0);
-
- act(() => {
- result.current.handleMouseEnter(3);
- });
-
- expect(result.current.hovering).toBe(3);
-});
-
-it('handleMouseLeave를 사용하여 마우스 호버된 별점을 초기화 할 수 있다.', () => {
- const { result } = renderHook(() => useStarRatingHover());
-
- expect(result.current.hovering).toBe(0);
-
- act(() => {
- result.current.handleMouseEnter(3);
- });
-
- expect(result.current.hovering).toBe(3);
-
- act(() => {
- result.current.handleMouseLeave();
- });
-
- expect(result.current.hovering).toBe(0);
-});
diff --git a/frontend/__tests__/hooks/useTabMenu.test.ts b/frontend/__tests__/hooks/useTabMenu.test.ts
deleted file mode 100644
index d8ca5317..00000000
--- a/frontend/__tests__/hooks/useTabMenu.test.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { useTabMenu } from '@/hooks/common';
-import { renderHook, act } from '@testing-library/react';
-
-it('선택된 탭 초기 상태는 0번 인덱스이다.', () => {
- const { result } = renderHook(() => useTabMenu());
-
- expect(result.current.selectedTabMenu).toBe(0);
- expect(result.current.isFirstTabMenu).toBe(true);
-});
-
-it('handleTabMenuClick를 사용하여 선택한 탭 인덱스를 저장할 수 있다. ', () => {
- const { result } = renderHook(() => useTabMenu());
-
- act(() => {
- result.current.handleTabMenuClick(1);
- });
-
- expect(result.current.selectedTabMenu).toBe(1);
-});
-
-it('initTabMenu를 사용하여 선택된 탭을 맨 처음 탭으로 초기화할 수 있다.', () => {
- const { result } = renderHook(() => useTabMenu());
-
- act(() => {
- result.current.handleTabMenuClick(1);
- result.current.initTabMenu();
- });
-
- expect(result.current.selectedTabMenu).toBe(0);
-});
diff --git a/frontend/jest.config.js b/frontend/jest.config.js
deleted file mode 100644
index 82a71339..00000000
--- a/frontend/jest.config.js
+++ /dev/null
@@ -1,20 +0,0 @@
-module.exports = {
- testEnvironment: 'jsdom',
- transform: {
- '^.+\\.(js|ts|tsx)?$': 'ts-jest',
- },
- moduleNameMapper: {
- '^@/(.*)$': '/src/$1',
- },
- testMatch: ['/__tests__/**/*.test.(js|jsx|ts|tsx)'],
- reporters: [
- 'default',
- [
- 'jest-junit',
- {
- outputDirectory: '/test-results',
- outputName: 'results.xml',
- },
- ],
- ],
-};
diff --git a/frontend/package.json b/frontend/package.json
deleted file mode 100644
index 358f1fe4..00000000
--- a/frontend/package.json
+++ /dev/null
@@ -1,84 +0,0 @@
-{
- "name": "fun-eat",
- "version": "0.0.0",
- "main": "index.js",
- "license": "MIT",
- "scripts": {
- "start": "webpack serve --open --config webpack.dev.js",
- "build": "webpack --config webpack.prod.js",
- "build-dev": "webpack --config webpack.dev.js",
- "storybook": "storybook dev -p 6006",
- "lint:styled": "stylelint './src/**/*.tsx' --fix",
- "test": "jest",
- "test:coverage": "jest --watchAll --coverage"
- },
- "dependencies": {
- "@fun-eat/design-system": "^0.3.18",
- "@tanstack/react-query": "^4.32.6",
- "@tanstack/react-query-devtools": "^4.32.6",
- "browser-image-compression": "^2.0.2",
- "dayjs": "^1.11.9",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-ga4": "^2.1.0",
- "react-router-dom": "^6.14.2",
- "styled-components": "^6.0.2"
- },
- "devDependencies": {
- "@babel/preset-env": "^7.22.9",
- "@babel/preset-react": "^7.22.5",
- "@babel/preset-typescript": "^7.22.5",
- "@storybook/addon-essentials": "^7.0.27",
- "@storybook/addon-interactions": "^7.0.27",
- "@storybook/addon-links": "^7.0.27",
- "@storybook/addon-onboarding": "^1.0.8",
- "@storybook/blocks": "^7.0.27",
- "@storybook/react": "^7.0.27",
- "@storybook/react-webpack5": "^7.0.27",
- "@storybook/testing-library": "^0.0.14-next.2",
- "@svgr/webpack": "^8.0.1",
- "@testing-library/jest-dom": "^5.17.0",
- "@testing-library/react": "^14.0.0",
- "@testing-library/user-event": "^14.4.3",
- "@types/jest": "^29.5.3",
- "@types/react": "^18.2.14",
- "@types/react-dom": "^18.2.6",
- "@types/styled-components": "^5.1.26",
- "@typescript-eslint/eslint-plugin": "^5.60.1",
- "@typescript-eslint/parser": "^5.60.1",
- "babel-plugin-styled-components": "^2.1.4",
- "copy-webpack-plugin": "^11.0.0",
- "dotenv-webpack": "^8.0.1",
- "eslint": "^8.44.0",
- "eslint-import-resolver-typescript": "^3.5.5",
- "eslint-import-resolver-webpack": "^0.13.2",
- "eslint-plugin-import": "^2.27.5",
- "eslint-plugin-react": "^7.32.2",
- "eslint-plugin-storybook": "^0.6.12",
- "html-webpack-plugin": "^5.5.3",
- "jest": "^29.6.2",
- "jest-environment-jsdom": "^29.6.2",
- "jest-junit": "^16.0.0",
- "msw": "^1.2.3",
- "msw-storybook-addon": "^1.8.0",
- "postcss": "^8.4.29",
- "postcss-styled-syntax": "^0.4.0",
- "prettier": "^2.8.8",
- "storybook": "^7.1.1",
- "stylelint": "^15.10.3",
- "stylelint-order": "^6.0.3",
- "stylelint-semantic-groups": "^1.2.0",
- "ts-jest": "^29.1.1",
- "ts-loader": "^9.4.4",
- "typescript": "^5.1.6",
- "webpack": "^5.88.1",
- "webpack-cli": "^5.1.4",
- "webpack-dev-server": "^4.15.1"
- },
- "msw": {
- "workerDirectory": "public"
- },
- "resolutions": {
- "jackspeak": "2.1.1"
- }
-}
diff --git a/frontend/public/assets/apple-icon-180x180.png b/frontend/public/assets/apple-icon-180x180.png
deleted file mode 100644
index 986c9be8..00000000
Binary files a/frontend/public/assets/apple-icon-180x180.png and /dev/null differ
diff --git a/frontend/public/assets/favicon-16x16.png b/frontend/public/assets/favicon-16x16.png
deleted file mode 100644
index a277e183..00000000
Binary files a/frontend/public/assets/favicon-16x16.png and /dev/null differ
diff --git a/frontend/public/assets/favicon-32x32.png b/frontend/public/assets/favicon-32x32.png
deleted file mode 100644
index 89b5dff5..00000000
Binary files a/frontend/public/assets/favicon-32x32.png and /dev/null differ
diff --git a/frontend/public/assets/favicon.ico b/frontend/public/assets/favicon.ico
deleted file mode 100644
index 48fea60f..00000000
Binary files a/frontend/public/assets/favicon.ico and /dev/null differ
diff --git a/frontend/public/assets/og-image.png b/frontend/public/assets/og-image.png
deleted file mode 100644
index 3d4b525e..00000000
Binary files a/frontend/public/assets/og-image.png and /dev/null differ
diff --git a/frontend/public/index.html b/frontend/public/index.html
deleted file mode 100644
index 36782ea8..00000000
--- a/frontend/public/index.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
- 펀잇
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json
deleted file mode 100644
index 7e44fe2f..00000000
--- a/frontend/public/manifest.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "name": "펀잇",
- "short_name": "펀잇",
- "description": "궁금해? 맛있을걸? 먹어봐 🥄",
- "display": "standalone",
- "icons": [
- {
- "src": "/assets/favicon-16x16.png",
- "sizes": "16x16",
- "type": "image/png"
- },
- {
- "src": "/assets/favicon-32x32.png",
- "sizes": "32x32",
- "type": "image/png"
- },
- {
- "src": "/assets/apple-icon-180x180.png",
- "sizes": "180x180",
- "type": "image/png"
- }
- ]
-}
diff --git a/frontend/public/mockServiceWorker.js b/frontend/public/mockServiceWorker.js
deleted file mode 100644
index 51d85eee..00000000
--- a/frontend/public/mockServiceWorker.js
+++ /dev/null
@@ -1,303 +0,0 @@
-/* eslint-disable */
-/* tslint:disable */
-
-/**
- * Mock Service Worker (1.3.2).
- * @see https://github.com/mswjs/msw
- * - Please do NOT modify this file.
- * - Please do NOT serve this file on production.
- */
-
-const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70'
-const activeClientIds = new Set()
-
-self.addEventListener('install', function () {
- self.skipWaiting()
-})
-
-self.addEventListener('activate', function (event) {
- event.waitUntil(self.clients.claim())
-})
-
-self.addEventListener('message', async function (event) {
- const clientId = event.source.id
-
- if (!clientId || !self.clients) {
- return
- }
-
- const client = await self.clients.get(clientId)
-
- if (!client) {
- return
- }
-
- const allClients = await self.clients.matchAll({
- type: 'window',
- })
-
- switch (event.data) {
- case 'KEEPALIVE_REQUEST': {
- sendToClient(client, {
- type: 'KEEPALIVE_RESPONSE',
- })
- break
- }
-
- case 'INTEGRITY_CHECK_REQUEST': {
- sendToClient(client, {
- type: 'INTEGRITY_CHECK_RESPONSE',
- payload: INTEGRITY_CHECKSUM,
- })
- break
- }
-
- case 'MOCK_ACTIVATE': {
- activeClientIds.add(clientId)
-
- sendToClient(client, {
- type: 'MOCKING_ENABLED',
- payload: true,
- })
- break
- }
-
- case 'MOCK_DEACTIVATE': {
- activeClientIds.delete(clientId)
- break
- }
-
- case 'CLIENT_CLOSED': {
- activeClientIds.delete(clientId)
-
- const remainingClients = allClients.filter((client) => {
- return client.id !== clientId
- })
-
- // Unregister itself when there are no more clients
- if (remainingClients.length === 0) {
- self.registration.unregister()
- }
-
- break
- }
- }
-})
-
-self.addEventListener('fetch', function (event) {
- const { request } = event
- const accept = request.headers.get('accept') || ''
-
- // Bypass server-sent events.
- if (accept.includes('text/event-stream')) {
- return
- }
-
- // Bypass navigation requests.
- if (request.mode === 'navigate') {
- return
- }
-
- // Opening the DevTools triggers the "only-if-cached" request
- // that cannot be handled by the worker. Bypass such requests.
- if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
- return
- }
-
- // Bypass all requests when there are no active clients.
- // Prevents the self-unregistered worked from handling requests
- // after it's been deleted (still remains active until the next reload).
- if (activeClientIds.size === 0) {
- return
- }
-
- // Generate unique request ID.
- const requestId = Math.random().toString(16).slice(2)
-
- event.respondWith(
- handleRequest(event, requestId).catch((error) => {
- if (error.name === 'NetworkError') {
- console.warn(
- '[MSW] Successfully emulated a network error for the "%s %s" request.',
- request.method,
- request.url,
- )
- return
- }
-
- // At this point, any exception indicates an issue with the original request/response.
- console.error(
- `\
-[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`,
- request.method,
- request.url,
- `${error.name}: ${error.message}`,
- )
- }),
- )
-})
-
-async function handleRequest(event, requestId) {
- const client = await resolveMainClient(event)
- const response = await getResponse(event, client, requestId)
-
- // Send back the response clone for the "response:*" life-cycle events.
- // Ensure MSW is active and ready to handle the message, otherwise
- // this message will pend indefinitely.
- if (client && activeClientIds.has(client.id)) {
- ;(async function () {
- const clonedResponse = response.clone()
- sendToClient(client, {
- type: 'RESPONSE',
- payload: {
- requestId,
- type: clonedResponse.type,
- ok: clonedResponse.ok,
- status: clonedResponse.status,
- statusText: clonedResponse.statusText,
- body:
- clonedResponse.body === null ? null : await clonedResponse.text(),
- headers: Object.fromEntries(clonedResponse.headers.entries()),
- redirected: clonedResponse.redirected,
- },
- })
- })()
- }
-
- return response
-}
-
-// Resolve the main client for the given event.
-// Client that issues a request doesn't necessarily equal the client
-// that registered the worker. It's with the latter the worker should
-// communicate with during the response resolving phase.
-async function resolveMainClient(event) {
- const client = await self.clients.get(event.clientId)
-
- if (client?.frameType === 'top-level') {
- return client
- }
-
- const allClients = await self.clients.matchAll({
- type: 'window',
- })
-
- return allClients
- .filter((client) => {
- // Get only those clients that are currently visible.
- return client.visibilityState === 'visible'
- })
- .find((client) => {
- // Find the client ID that's recorded in the
- // set of clients that have registered the worker.
- return activeClientIds.has(client.id)
- })
-}
-
-async function getResponse(event, client, requestId) {
- const { request } = event
- const clonedRequest = request.clone()
-
- function passthrough() {
- // Clone the request because it might've been already used
- // (i.e. its body has been read and sent to the client).
- const headers = Object.fromEntries(clonedRequest.headers.entries())
-
- // Remove MSW-specific request headers so the bypassed requests
- // comply with the server's CORS preflight check.
- // Operate with the headers as an object because request "Headers"
- // are immutable.
- delete headers['x-msw-bypass']
-
- return fetch(clonedRequest, { headers })
- }
-
- // Bypass mocking when the client is not active.
- if (!client) {
- return passthrough()
- }
-
- // Bypass initial page load requests (i.e. static assets).
- // The absence of the immediate/parent client in the map of the active clients
- // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
- // and is not ready to handle requests.
- if (!activeClientIds.has(client.id)) {
- return passthrough()
- }
-
- // Bypass requests with the explicit bypass header.
- // Such requests can be issued by "ctx.fetch()".
- if (request.headers.get('x-msw-bypass') === 'true') {
- return passthrough()
- }
-
- // Notify the client that a request has been intercepted.
- const clientMessage = await sendToClient(client, {
- type: 'REQUEST',
- payload: {
- id: requestId,
- url: request.url,
- method: request.method,
- headers: Object.fromEntries(request.headers.entries()),
- cache: request.cache,
- mode: request.mode,
- credentials: request.credentials,
- destination: request.destination,
- integrity: request.integrity,
- redirect: request.redirect,
- referrer: request.referrer,
- referrerPolicy: request.referrerPolicy,
- body: await request.text(),
- bodyUsed: request.bodyUsed,
- keepalive: request.keepalive,
- },
- })
-
- switch (clientMessage.type) {
- case 'MOCK_RESPONSE': {
- return respondWithMock(clientMessage.data)
- }
-
- case 'MOCK_NOT_FOUND': {
- return passthrough()
- }
-
- case 'NETWORK_ERROR': {
- const { name, message } = clientMessage.data
- const networkError = new Error(message)
- networkError.name = name
-
- // Rejecting a "respondWith" promise emulates a network error.
- throw networkError
- }
- }
-
- return passthrough()
-}
-
-function sendToClient(client, message) {
- return new Promise((resolve, reject) => {
- const channel = new MessageChannel()
-
- channel.port1.onmessage = (event) => {
- if (event.data && event.data.error) {
- return reject(event.data.error)
- }
-
- resolve(event.data)
- }
-
- client.postMessage(message, [channel.port2])
- })
-}
-
-function sleep(timeMs) {
- return new Promise((resolve) => {
- setTimeout(resolve, timeMs)
- })
-}
-
-async function respondWithMock(response) {
- await sleep(response.delay)
- return new Response(response.body, response)
-}
diff --git a/frontend/public/robots.txt b/frontend/public/robots.txt
deleted file mode 100644
index 7b8e4fcf..00000000
--- a/frontend/public/robots.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-User-agent: *
-Disallow: /404
-Allow: /
-
-# Host
-Host: https://funeat.site/
-
-# Sitemaps
-Sitemap: https://funeat.site/sitemap.xml
\ No newline at end of file
diff --git a/frontend/public/sitemap.xml b/frontend/public/sitemap.xml
deleted file mode 100644
index 9c261be7..00000000
--- a/frontend/public/sitemap.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
- https://funeat.site
- 2023-09-25T05:39:39+00:00
-
-
- https://funeat.site/products/food
- 2023-09-25T05:39:39+00:00
-
-
- https://funeat.site/products/store
- 2023-09-25T05:39:39+00:00
-
-
- https://funeat.site/recipes
- 2023-09-25T05:39:39+00:00
-
-
\ No newline at end of file
diff --git a/frontend/src/apis/ApiClient.ts b/frontend/src/apis/ApiClient.ts
deleted file mode 100644
index 26fe6284..00000000
--- a/frontend/src/apis/ApiClient.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import { fetchApi } from './fetch';
-
-interface RequestOptions {
- params?: string;
- queries?: string;
- credentials?: boolean;
-}
-
-export class ApiClient {
- #path: string;
-
- #headers: HeadersInit;
-
- constructor(path: string, headers: HeadersInit = {}) {
- this.#path = path;
- this.#headers = headers;
- }
-
- getUrl(params = '', queries = '') {
- return '/api' + this.#path + params + queries;
- }
-
- get({ params, queries, credentials = false }: RequestOptions) {
- return fetchApi(this.getUrl(params, queries), {
- method: 'GET',
- headers: this.#headers,
- credentials: credentials ? 'include' : 'omit',
- });
- }
-
- post({ params, queries, credentials = false }: RequestOptions, headers?: HeadersInit, body?: B) {
- return fetchApi(this.getUrl(params, queries), {
- method: 'POST',
- headers: headers,
- body: body ? JSON.stringify(body) : null,
- credentials: credentials ? 'include' : 'omit',
- });
- }
-
- postData({ params, queries, credentials = false }: RequestOptions, body: FormData) {
- return fetchApi(this.getUrl(params, queries), {
- method: 'POST',
- headers: this.#headers,
- body: body,
- credentials: credentials ? 'include' : 'omit',
- });
- }
-
- patch({ params, queries, credentials = false }: RequestOptions, headers: HeadersInit, body?: B) {
- return fetchApi(this.getUrl(params, queries), {
- method: 'PATCH',
- headers: headers,
- body: body ? JSON.stringify(body) : null,
- credentials: credentials ? 'include' : 'omit',
- });
- }
-
- put({ params, queries, credentials = false }: RequestOptions, headers?: HeadersInit, body?: B) {
- return fetchApi(this.getUrl(params, queries), {
- method: 'PUT',
- headers: headers,
- body: body ? JSON.stringify(body) : null,
- credentials: credentials ? 'include' : 'omit',
- });
- }
-
- putData({ params, queries, credentials = false }: RequestOptions, body: FormData) {
- return fetchApi(this.getUrl(params, queries), {
- method: 'PUT',
- headers: this.#headers,
- body: body,
- credentials: credentials ? 'include' : 'omit',
- });
- }
-
- delete({ params, queries, credentials = false }: RequestOptions, body?: B) {
- return fetchApi(this.getUrl(params, queries), {
- method: 'DELETE',
- headers: this.#headers,
- body: body ? JSON.stringify(body) : null,
- credentials: credentials ? 'include' : 'omit',
- });
- }
-}
diff --git a/frontend/src/apis/fetch.ts b/frontend/src/apis/fetch.ts
deleted file mode 100644
index 6c760b43..00000000
--- a/frontend/src/apis/fetch.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { ErrorResponse } from '@/types/response';
-
-export const fetchApi = async (url: string, options: RequestInit) => {
- if (!navigator.onLine) {
- throw new Error('네트워크 오프라인이 감지되었습니다');
- }
-
- const response = await fetch(url, options);
-
- if (!response.ok) {
- const errorData: ErrorResponse = await response.json();
- throw new Error(errorData.message);
- }
-
- return response;
-};
diff --git a/frontend/src/apis/index.ts b/frontend/src/apis/index.ts
deleted file mode 100644
index c258c161..00000000
--- a/frontend/src/apis/index.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { ApiClient } from './ApiClient';
-
-export const categoryApi = new ApiClient('/categories');
-export const productApi = new ApiClient('/products');
-export const tagApi = new ApiClient('/tags');
-export const rankApi = new ApiClient('/ranks');
-export const loginApi = new ApiClient('/login');
-export const memberApi = new ApiClient('/members');
-export const recipeApi = new ApiClient('/recipes');
-export const searchApi = new ApiClient('/search');
-export const logoutApi = new ApiClient('/logout');
-export const reviewApi = new ApiClient('/reviews');
-export const bannerApi = new ApiClient('/banners');
diff --git a/frontend/src/assets/characters.svg b/frontend/src/assets/characters.svg
deleted file mode 100644
index 6580162c..00000000
--- a/frontend/src/assets/characters.svg
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
\ No newline at end of file
diff --git a/frontend/src/assets/logo.svg b/frontend/src/assets/logo.svg
deleted file mode 100644
index 26cb9858..00000000
--- a/frontend/src/assets/logo.svg
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
diff --git a/frontend/src/assets/plate.svg b/frontend/src/assets/plate.svg
deleted file mode 100644
index 1a5f2f61..00000000
--- a/frontend/src/assets/plate.svg
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
\ No newline at end of file
diff --git a/frontend/src/assets/samgakgimbab.svg b/frontend/src/assets/samgakgimbab.svg
deleted file mode 100644
index a5e9872f..00000000
--- a/frontend/src/assets/samgakgimbab.svg
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
\ No newline at end of file
diff --git a/frontend/src/components/Common/Banner/Banner.tsx b/frontend/src/components/Common/Banner/Banner.tsx
deleted file mode 100644
index 89af08c6..00000000
--- a/frontend/src/components/Common/Banner/Banner.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Link } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import { useBannerQuery } from '@/hooks/queries/banner';
-
-const Banner = () => {
- const { data: banners } = useBannerQuery();
- const { link, image } = banners[Math.floor(Math.random() * banners.length)];
-
- if (!link) {
- return ;
- }
-
- return (
-
-
-
- );
-};
-
-export default Banner;
-
-const BannerImage = styled.img`
- width: 100%;
- height: auto;
-`;
diff --git a/frontend/src/components/Common/Carousel/Carousel.stories.tsx b/frontend/src/components/Common/Carousel/Carousel.stories.tsx
deleted file mode 100644
index 8d7eccf6..00000000
--- a/frontend/src/components/Common/Carousel/Carousel.stories.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import Carousel from './Carousel';
-
-import { RecipeItem } from '@/components/Recipe';
-import mockRecipe from '@/mocks/data/recipes.json';
-
-const meta: Meta = {
- title: 'common/Carousel',
- component: Carousel,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- args: {
- carouselList: [
- {
- id: 0,
- children: 1
,
- },
- {
- id: 1,
- children: 2
,
- },
- {
- id: 2,
- children: 3
,
- },
- ],
- },
-};
-
-export const RecipeRanking: Story = {
- args: {
- carouselList: [
- {
- id: 0,
- children: ,
- },
- {
- id: 1,
- children: ,
- },
- {
- id: 2,
- children: ,
- },
- ],
- },
-};
diff --git a/frontend/src/components/Common/Carousel/Carousel.tsx b/frontend/src/components/Common/Carousel/Carousel.tsx
deleted file mode 100644
index 038b5cef..00000000
--- a/frontend/src/components/Common/Carousel/Carousel.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { useEffect, useState } from 'react';
-import styled from 'styled-components';
-
-import type { CarouselChildren } from '@/types/common';
-
-interface CarouselProps {
- carouselList: CarouselChildren[];
-}
-
-const Carousel = ({ carouselList }: CarouselProps) => {
- const extendedCarouselList = [...carouselList, carouselList[0]];
- const [currentIndex, setCurrentIndex] = useState(0);
-
- const CAROUSEL_WIDTH = window.innerWidth;
-
- const showNextSlide = () => {
- setCurrentIndex((prev) => (prev === carouselList.length ? 0 : prev + 1));
- };
-
- useEffect(() => {
- const timer = setInterval(showNextSlide, 2000);
-
- return () => clearInterval(timer);
- }, [currentIndex]);
-
- return (
-
-
- {extendedCarouselList.map(({ id, children }, index) => (
-
- {children}
-
- ))}
-
-
- );
-};
-
-export default Carousel;
-
-const CarouselContainer = styled.div`
- display: flex;
- width: 100%;
- border: 1px solid ${({ theme }) => theme.colors.gray2};
- border-radius: 10px;
- overflow: hidden;
-`;
-
-const CarouselWrapper = styled.ul`
- display: flex;
-`;
-
-const CarouselItem = styled.li`
- height: fit-content;
-`;
diff --git a/frontend/src/components/Common/CategoryFoodList/CategoryFoodList.stories.tsx b/frontend/src/components/Common/CategoryFoodList/CategoryFoodList.stories.tsx
deleted file mode 100644
index 25de7b81..00000000
--- a/frontend/src/components/Common/CategoryFoodList/CategoryFoodList.stories.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import CategoryFoodList from './CategoryFoodList';
-
-import CategoryProvider from '@/contexts/CategoryContext';
-
-const meta: Meta = {
- title: 'common/CategoryFoodList',
- component: CategoryFoodList,
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/CategoryFoodList/CategoryFoodList.tsx b/frontend/src/components/Common/CategoryFoodList/CategoryFoodList.tsx
deleted file mode 100644
index 921584b8..00000000
--- a/frontend/src/components/Common/CategoryFoodList/CategoryFoodList.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import styled from 'styled-components';
-
-import CategoryItem from '../CategoryItem/CategoryItem';
-
-import { CATEGORY_TYPE } from '@/constants';
-import { useCategoryFoodQuery } from '@/hooks/queries/product';
-
-const categoryType = CATEGORY_TYPE.FOOD;
-
-const CategoryFoodList = () => {
- const { data: categories } = useCategoryFoodQuery(categoryType);
-
- return (
-
- {categories.map(({ id, name, image }) => (
-
- ))}
-
- );
-};
-
-export default CategoryFoodList;
-
-const CategoryFoodListWrapper = styled.div`
- display: flex;
- gap: 16px;
-`;
diff --git a/frontend/src/components/Common/CategoryFoodTab/CategoryFoodTab.stories.tsx b/frontend/src/components/Common/CategoryFoodTab/CategoryFoodTab.stories.tsx
deleted file mode 100644
index 7cbb6e1f..00000000
--- a/frontend/src/components/Common/CategoryFoodTab/CategoryFoodTab.stories.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import CategoryFoodTab from './CategoryFoodTab';
-
-import CategoryProvider from '@/contexts/CategoryContext';
-
-const meta: Meta = {
- title: 'common/CategoryFoodTab',
- component: CategoryFoodTab,
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/CategoryFoodTab/CategoryFoodTab.tsx b/frontend/src/components/Common/CategoryFoodTab/CategoryFoodTab.tsx
deleted file mode 100644
index 56817526..00000000
--- a/frontend/src/components/Common/CategoryFoodTab/CategoryFoodTab.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import { Button, theme } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import { CATEGORY_TYPE } from '@/constants';
-import { useGA } from '@/hooks/common';
-import { useCategoryActionContext, useCategoryValueContext } from '@/hooks/context';
-import { useCategoryFoodQuery } from '@/hooks/queries/product/useCategoryQuery';
-import { getTargetCategoryName } from '@/utils/category';
-
-const categoryType = CATEGORY_TYPE.FOOD;
-
-const CategoryFoodTab = () => {
- const { data: categories } = useCategoryFoodQuery(categoryType);
-
- const { categoryIds } = useCategoryValueContext();
- const { selectCategory } = useCategoryActionContext();
-
- const currentCategoryId = categoryIds[categoryType];
-
- const { gaEvent } = useGA();
-
- const handleCategoryButtonClick = (menuId: number) => {
- selectCategory(categoryType, menuId);
- gaEvent({
- category: 'button',
- action: `${getTargetCategoryName(categories, menuId)} 카테고리 버튼 클릭`,
- label: '카테고리',
- });
- };
-
- return (
-
- {categories.map(({ id, name }) => {
- const isSelected = id === currentCategoryId;
- return (
-
- handleCategoryButtonClick(id)}
- aria-pressed={isSelected}
- >
- {name}
-
-
- );
- })}
-
- );
-};
-
-export default CategoryFoodTab;
-
-const CategoryMenuContainer = styled.ul`
- display: flex;
- gap: 8px;
- white-space: nowrap;
- overflow-x: auto;
-
- &::-webkit-scrollbar {
- display: none;
- }
-`;
-
-const CategoryButton = styled(Button)<{ isSelected: boolean }>`
- padding: 6px 12px;
- ${({ isSelected }) =>
- isSelected &&
- `
- background: ${theme.colors.gray5};
- color: ${theme.textColors.white};
- `}
-`;
diff --git a/frontend/src/components/Common/CategoryItem/CategoryItem.stories.tsx b/frontend/src/components/Common/CategoryItem/CategoryItem.stories.tsx
deleted file mode 100644
index 3dc17ddf..00000000
--- a/frontend/src/components/Common/CategoryItem/CategoryItem.stories.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import CategoryItem from './CategoryItem';
-
-import CategoryProvider from '@/contexts/CategoryContext';
-
-const meta: Meta = {
- title: 'common/CategoryItem',
- component: CategoryItem,
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
- args: {
- categoryId: 1,
- name: '즉석 식품',
- image: 'https://tqklhszfkvzk6518638.cdn.ntruss.com/product/8801771029052.jpg',
- categoryType: 'food',
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/CategoryItem/CategoryItem.tsx b/frontend/src/components/Common/CategoryItem/CategoryItem.tsx
deleted file mode 100644
index 051c9707..00000000
--- a/frontend/src/components/Common/CategoryItem/CategoryItem.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Button } from '@fun-eat/design-system';
-import { useNavigate } from 'react-router-dom';
-import styled from 'styled-components';
-
-import { PATH } from '@/constants/path';
-import { useGA } from '@/hooks/common';
-import { useCategoryActionContext } from '@/hooks/context';
-
-interface CategoryItemProps {
- categoryId: number;
- name: string;
- image: string;
- categoryType: 'food' | 'store';
-}
-
-const CategoryItem = ({ categoryId, name, image, categoryType }: CategoryItemProps) => {
- const navigate = useNavigate();
- const { selectCategory } = useCategoryActionContext();
-
- const { gaEvent } = useGA();
-
- const handleCategoryItemClick = (categoryId: number) => {
- selectCategory(categoryType, categoryId);
- navigate(PATH.PRODUCT_LIST + '/' + categoryType);
-
- gaEvent({
- category: 'button',
- action: `${name} 카테고리 링크 클릭`,
- label: '카테고리',
- });
- };
-
- return (
-
- );
-};
-
-export default CategoryItem;
-
-const ImageWrapper = styled.div`
- display: flex;
- justify-content: center;
- align-items: center;
- width: 60px;
- height: 60px;
- border-radius: 10px;
- background: ${({ theme }) => theme.colors.white};
-
- & > img {
- width: 100%;
- height: auto;
- object-fit: cover;
- }
-`;
-
-const CategoryName = styled.p`
- margin-top: 10px;
- font-weight: 600;
- font-size: ${({ theme }) => theme.fontSizes.xs};
-`;
diff --git a/frontend/src/components/Common/CategoryStoreList/CategoryStoreList.stories.tsx b/frontend/src/components/Common/CategoryStoreList/CategoryStoreList.stories.tsx
deleted file mode 100644
index d26be6f4..00000000
--- a/frontend/src/components/Common/CategoryStoreList/CategoryStoreList.stories.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import CategoryStoreList from './CategoryStoreList';
-
-import CategoryProvider from '@/contexts/CategoryContext';
-
-const meta: Meta = {
- title: 'common/CategoryStoreList',
- component: CategoryStoreList,
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/CategoryStoreList/CategoryStoreList.tsx b/frontend/src/components/Common/CategoryStoreList/CategoryStoreList.tsx
deleted file mode 100644
index 6bf2c36a..00000000
--- a/frontend/src/components/Common/CategoryStoreList/CategoryStoreList.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import styled from 'styled-components';
-
-import CategoryItem from '../CategoryItem/CategoryItem';
-
-import { CATEGORY_TYPE } from '@/constants';
-import { useCategoryStoreQuery } from '@/hooks/queries/product';
-
-const categoryType = CATEGORY_TYPE.STORE;
-
-const CategoryStoreList = () => {
- const { data: categories } = useCategoryStoreQuery(categoryType);
-
- return (
-
- {categories.map(({ id, name, image }) => (
-
- ))}
-
- );
-};
-
-export default CategoryStoreList;
-
-const CategoryStoreListWrapper = styled.div`
- display: flex;
- gap: 16px;
-`;
diff --git a/frontend/src/components/Common/CategoryStoreTab/CategoryStoreTab.stories.tsx b/frontend/src/components/Common/CategoryStoreTab/CategoryStoreTab.stories.tsx
deleted file mode 100644
index 7fca5880..00000000
--- a/frontend/src/components/Common/CategoryStoreTab/CategoryStoreTab.stories.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import CategoryStoreTab from './CategoryStoreTab';
-
-import CategoryProvider from '@/contexts/CategoryContext';
-
-const meta: Meta = {
- title: 'common/CategoryStoreTab',
- component: CategoryStoreTab,
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/CategoryStoreTab/CategoryStoreTab.tsx b/frontend/src/components/Common/CategoryStoreTab/CategoryStoreTab.tsx
deleted file mode 100644
index b75abb7b..00000000
--- a/frontend/src/components/Common/CategoryStoreTab/CategoryStoreTab.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { Button, theme } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import { CATEGORY_TYPE } from '@/constants';
-import { useGA } from '@/hooks/common';
-import { useCategoryActionContext, useCategoryValueContext } from '@/hooks/context';
-import { useCategoryStoreQuery } from '@/hooks/queries/product/useCategoryQuery';
-import { getTargetCategoryName } from '@/utils/category';
-
-const categoryType = CATEGORY_TYPE.STORE;
-
-const CategoryStoreTab = () => {
- const { data: categories } = useCategoryStoreQuery(categoryType);
-
- const { categoryIds } = useCategoryValueContext();
- const { selectCategory } = useCategoryActionContext();
- const currentCategoryId = categoryIds[categoryType];
-
- const { gaEvent } = useGA();
-
- const handleCategoryButtonClick = (menuId: number) => {
- selectCategory(categoryType, menuId);
- gaEvent({
- category: 'button',
- action: `${getTargetCategoryName(categories, menuId)} 카테고리 버튼 클릭`,
- label: '카테고리',
- });
- };
-
- return (
-
- {categories.map(({ id, name }) => {
- const isSelected = id === currentCategoryId;
- return (
-
- handleCategoryButtonClick(id)}
- aria-pressed={isSelected}
- >
- {name}
-
-
- );
- })}
-
- );
-};
-
-export default CategoryStoreTab;
-
-const CategoryMenuContainer = styled.ul`
- display: flex;
- gap: 8px;
- white-space: nowrap;
- overflow-x: auto;
-
- &::-webkit-scrollbar {
- display: none;
- }
-`;
-
-const CategoryButton = styled(Button)<{ isSelected: boolean }>`
- padding: 6px 12px;
- ${({ isSelected }) =>
- isSelected &&
- `
- background: ${theme.colors.primary};
- color: ${theme.textColors.default};
- `}
-`;
diff --git a/frontend/src/components/Common/ErrorBoundary/ErrorBoundary.tsx b/frontend/src/components/Common/ErrorBoundary/ErrorBoundary.tsx
deleted file mode 100644
index d8491896..00000000
--- a/frontend/src/components/Common/ErrorBoundary/ErrorBoundary.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { Button } from '@fun-eat/design-system';
-import type { ComponentType, PropsWithChildren } from 'react';
-import { Component } from 'react';
-
-export interface FallbackProps {
- message: string;
-}
-
-interface ErrorBoundaryProps {
- handleReset?: () => void;
- fallback: ComponentType;
-}
-
-interface ErrorBoundaryState {
- error: Error | null;
-}
-
-class ErrorBoundary extends Component, ErrorBoundaryState> {
- state: ErrorBoundaryState = {
- error: null,
- };
-
- static getDerivedStateFromError(error: Error): ErrorBoundaryState {
- return { error };
- }
-
- resetError = () => {
- if (this.props.handleReset) {
- this.props.handleReset();
- }
-
- this.setState({ error: null });
- };
-
- render() {
- const { fallback: FallbackComponent } = this.props;
-
- if (this.state.error) {
- return (
- <>
-
-
- >
- );
- }
-
- return this.props.children;
- }
-}
-
-export default ErrorBoundary;
diff --git a/frontend/src/components/Common/ErrorComponent/ErrorComponent.stories.tsx b/frontend/src/components/Common/ErrorComponent/ErrorComponent.stories.tsx
deleted file mode 100644
index ee51fcea..00000000
--- a/frontend/src/components/Common/ErrorComponent/ErrorComponent.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ErrorComponent from './ErrorComponent';
-
-const meta: Meta = {
- title: 'common/ErrorComponent',
- component: ErrorComponent,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/ErrorComponent/ErrorComponent.tsx b/frontend/src/components/Common/ErrorComponent/ErrorComponent.tsx
deleted file mode 100644
index bc66e1f1..00000000
--- a/frontend/src/components/Common/ErrorComponent/ErrorComponent.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-const ErrorComponent = () => {
- return 에러가 발생했습니다.
;
-};
-
-export default ErrorComponent;
diff --git a/frontend/src/components/Common/Header/Header.stories.tsx b/frontend/src/components/Common/Header/Header.stories.tsx
deleted file mode 100644
index b88b4258..00000000
--- a/frontend/src/components/Common/Header/Header.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import Header from './Header';
-
-const meta: Meta = {
- title: 'common/Header',
- component: Header,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/Header/Header.tsx b/frontend/src/components/Common/Header/Header.tsx
deleted file mode 100644
index ae2431ed..00000000
--- a/frontend/src/components/Common/Header/Header.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Link } from '@fun-eat/design-system';
-import { Link as RouterLink } from 'react-router-dom';
-import styled from 'styled-components';
-
-import SvgIcon from '../Svg/SvgIcon';
-
-import Logo from '@/assets/logo.svg';
-import { PATH } from '@/constants/path';
-
-interface HeaderProps {
- hasSearch?: boolean;
-}
-
-const Header = ({ hasSearch = true }: HeaderProps) => {
- if (hasSearch) {
- return (
-
-
-
-
-
-
-
-
- );
- }
-
- return (
-
-
-
-
-
- );
-};
-
-export default Header;
-
-const HeaderWithSearchContainer = styled.header`
- display: flex;
- justify-content: space-between;
- align-items: center;
- width: calc(100% - 40px);
- height: 60px;
- margin: 0 auto;
-`;
-
-const HeaderContainer = styled.header`
- display: flex;
- justify-content: center;
- align-items: center;
- width: 100%;
- height: 60px;
-`;
diff --git a/frontend/src/components/Common/ImageUploader/ImageUploader.stories.tsx b/frontend/src/components/Common/ImageUploader/ImageUploader.stories.tsx
deleted file mode 100644
index 02de4489..00000000
--- a/frontend/src/components/Common/ImageUploader/ImageUploader.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ImageUploader from './ImageUploader';
-
-const meta: Meta = {
- title: 'common/ImageUploader',
- component: ImageUploader,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/ImageUploader/ImageUploader.tsx b/frontend/src/components/Common/ImageUploader/ImageUploader.tsx
deleted file mode 100644
index 9c915081..00000000
--- a/frontend/src/components/Common/ImageUploader/ImageUploader.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { Button } from '@fun-eat/design-system';
-import type { ChangeEventHandler } from 'react';
-import styled from 'styled-components';
-
-import { IMAGE_MAX_SIZE } from '@/constants';
-import { useEnterKeyDown } from '@/hooks/common';
-import { useToastActionContext } from '@/hooks/context';
-
-interface ReviewImageUploaderProps {
- previewImage: string;
- uploadImage: (imageFile: File) => void;
- deleteImage: () => void;
-}
-
-const ImageUploader = ({ previewImage, uploadImage, deleteImage }: ReviewImageUploaderProps) => {
- const { inputRef, handleKeydown } = useEnterKeyDown();
- const { toast } = useToastActionContext();
-
- const handleImageUpload: ChangeEventHandler = (event) => {
- if (!event.target.files) {
- return;
- }
-
- const imageFile = event.target.files[0];
-
- if (imageFile.size > IMAGE_MAX_SIZE) {
- toast.error('이미지 크기가 너무 커요. 5MB 이하의 이미지를 골라주세요.');
- event.target.value = '';
- return;
- }
-
- uploadImage(imageFile);
- };
-
- return (
- <>
- {previewImage ? (
-
-
-
-
- ) : (
-
- +
-
-
- )}
- >
- );
-};
-
-export default ImageUploader;
-
-const ImageUploadLabel = styled.label`
- display: flex;
- justify-content: center;
- align-items: center;
- width: 92px;
- height: 95px;
- border: 1px solid ${({ theme }) => theme.borderColors.disabled};
- border-radius: ${({ theme }) => theme.borderRadius.xs};
- background: ${({ theme }) => theme.colors.gray1};
- cursor: pointer;
-
- & > input {
- display: none;
- }
-`;
-
-const PreviewImageWrapper = styled.div`
- display: flex;
- flex-direction: column;
- gap: 20px;
- align-items: center;
-`;
diff --git a/frontend/src/components/Common/Input/Input.stories.tsx b/frontend/src/components/Common/Input/Input.stories.tsx
deleted file mode 100644
index 48e8856a..00000000
--- a/frontend/src/components/Common/Input/Input.stories.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import Input from './Input';
-import SvgIcon from '../Svg/SvgIcon';
-
-const meta: Meta = {
- title: 'common/Input',
- component: Input,
- argTypes: {
- rightIcon: {
- control: { type: 'boolean' },
- mapping: { false: '', true: },
- },
- },
- args: {
- customWidth: '300px',
- isError: false,
- rightIcon: false,
- errorMessage: '',
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
-
-export const WithPlaceholder: Story = {
- args: {
- placeholder: '상품 이름을 검색하세요.',
- },
-};
-
-export const WithIcon: Story = {
- args: {
- placeholder: '상품 이름을 검색하세요.',
- rightIcon: true,
- },
-};
-
-export const Error: Story = {
- args: {
- isError: true,
- errorMessage: '10글자 이내로 입력해주세요.',
- },
-};
-
-export const Disabled: Story = {
- render: () => ,
-};
diff --git a/frontend/src/components/Common/Input/Input.tsx b/frontend/src/components/Common/Input/Input.tsx
deleted file mode 100644
index c3b3b40f..00000000
--- a/frontend/src/components/Common/Input/Input.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import { Text, theme } from '@fun-eat/design-system';
-import type { ComponentPropsWithRef, ForwardedRef, ReactNode } from 'react';
-import { forwardRef } from 'react';
-import styled from 'styled-components';
-
-interface InputProps extends ComponentPropsWithRef<'input'> {
- /**
- * Input 컴포넌트의 너비값입니다.
- */
- customWidth?: string;
- /**
- * Input 컴포넌트의 최소 너비값입니다.
- */
- minWidth?: string;
- /**
- * Input value에 에러가 있는지 여부입니다.
- */
- isError?: boolean;
- /**
- * Input 컴포넌트 오른쪽에 위치할 아이콘입니다.
- */
- rightIcon?: ReactNode;
- /**
- * isError가 true일 때 보여줄 에러 메시지입니다.
- */
- errorMessage?: string;
-}
-
-const Input = forwardRef(
- (
- { customWidth = '300px', minWidth, isError = false, rightIcon, errorMessage, ...props }: InputProps,
- ref: ForwardedRef
- ) => {
- return (
- <>
-
-
- {rightIcon && {rightIcon}}
-
- {isError && {errorMessage}}
- >
- );
- }
-);
-
-Input.displayName = 'Input';
-
-export default Input;
-
-type InputContainerStyleProps = Pick;
-type CustomInputStyleProps = Pick;
-
-const InputContainer = styled.div`
- position: relative;
- min-width: ${({ minWidth }) => minWidth ?? 0};
- max-width: ${({ customWidth }) => customWidth};
- text-align: center;
-`;
-
-const CustomInput = styled.input`
- width: 100%;
- height: 40px;
- padding: 10px 0 10px 12px;
- color: ${({ isError }) => (isError ? theme.colors.error : theme.textColors.default)};
- border: 1px solid ${({ isError }) => (isError ? theme.colors.error : theme.borderColors.default)};
- border-radius: 5px;
-
- &:focus {
- border: 2px solid ${({ isError }) => (isError ? theme.colors.error : theme.borderColors.strong)};
- outline: none;
- }
-
- &:disabled {
- border: 1px solid ${({ theme }) => theme.borderColors.disabled};
- background: ${({ theme }) => theme.colors.gray1};
- }
-
- &::placeholder {
- color: ${theme.textColors.disabled};
- font-size: ${theme.fontSizes.sm};
- }
-`;
-
-const IconWrapper = styled.div`
- position: absolute;
- top: 0;
- right: 0;
- display: flex;
- align-items: center;
- height: 100%;
- margin-right: 8px;
-`;
-
-const ErrorMessage = styled(Text)`
- color: ${theme.colors.error};
- font-size: ${theme.fontSizes.xs};
-`;
diff --git a/frontend/src/components/Common/Loading/Loading.stories.tsx b/frontend/src/components/Common/Loading/Loading.stories.tsx
deleted file mode 100644
index 3b866175..00000000
--- a/frontend/src/components/Common/Loading/Loading.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import Loading from './Loading';
-
-const meta: Meta = {
- title: 'common/Loading',
- component: Loading,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/Loading/Loading.tsx b/frontend/src/components/Common/Loading/Loading.tsx
deleted file mode 100644
index 7c58614a..00000000
--- a/frontend/src/components/Common/Loading/Loading.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { Text } from '@fun-eat/design-system';
-import styled, { keyframes } from 'styled-components';
-
-import PlateImage from '@/assets/plate.svg';
-
-const DEFAULT_DESCRIPTION = '잠시만 기다려주세요 🥄';
-
-interface LoadingProps {
- customHeight?: string;
- description?: string;
-}
-
-const Loading = ({ customHeight = '100%', description = DEFAULT_DESCRIPTION }: LoadingProps) => {
- return (
-
-
-
-
- {description}
-
- );
-};
-
-export default Loading;
-
-type LoadingContainerStyleProps = Pick;
-
-const LoadingContainer = styled.div`
- display: flex;
- flex-direction: column;
- row-gap: 36px;
- justify-content: center;
- align-items: center;
- height: ${({ customHeight }) => customHeight};
-`;
-
-const rotate = keyframes`
- 0% {
- transform: rotate(0deg);
- }
-
- 100% {
- transform: rotate(360deg);
- }
-`;
-
-const PlateImageWrapper = styled.div`
- animation: ${rotate} 1.5s ease-in-out infinite;
-`;
diff --git a/frontend/src/components/Common/MarkedText/MarkedText.tsx b/frontend/src/components/Common/MarkedText/MarkedText.tsx
deleted file mode 100644
index 341b3c92..00000000
--- a/frontend/src/components/Common/MarkedText/MarkedText.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Fragment } from 'react';
-import styled from 'styled-components';
-
-interface MarkedTextProps {
- text: string;
- mark: string;
-}
-
-const MarkedText = ({ text, mark }: MarkedTextProps) => {
- const textFragments = text.split(new RegExp(`(${mark})`, 'gi'));
-
- return (
- <>
- {textFragments.map((fragment, index) => (
-
- {fragment.toLowerCase() === mark.toLowerCase() ? {fragment} : <>{fragment}>}
-
- ))}
- >
- );
-};
-
-export default MarkedText;
-
-const Mark = styled.mark`
- font-weight: ${({ theme }) => theme.fontWeights.bold};
- background-color: ${({ theme }) => theme.backgroundColors.default};
-`;
diff --git a/frontend/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.stories.tsx b/frontend/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.stories.tsx
deleted file mode 100644
index 739bde3e..00000000
--- a/frontend/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.stories.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import NavigableSectionTitle from './NavigableSectionTitle';
-
-const meta: Meta = {
- title: 'common/NavigableSectionTitle',
- component: NavigableSectionTitle,
- args: {
- title: '내가 작성한 리뷰 (12개)',
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.tsx b/frontend/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.tsx
deleted file mode 100644
index a24ba0ed..00000000
--- a/frontend/src/components/Common/NavigableSectionTitle/NavigableSectionTitle.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Heading, Link, theme } from '@fun-eat/design-system';
-import { Link as RouterLink } from 'react-router-dom';
-import styled from 'styled-components';
-
-import { SvgIcon } from '@/components/Common';
-
-interface NavigableSectionTitleProps {
- title: string;
- routeDestination: string;
-}
-
-const NavigableSectionTitle = ({ title, routeDestination }: NavigableSectionTitleProps) => {
- return (
-
-
- {title}
-
-
-
-
-
- );
-};
-
-export default NavigableSectionTitle;
-
-const NavigableSectionTitleContainer = styled.div`
- display: flex;
- justify-content: space-between;
- align-items: center;
-`;
-
-const ArrowIcon = styled(SvgIcon)`
- transform: translateY(3px) rotate(180deg);
-`;
diff --git a/frontend/src/components/Common/NavigationBar/NavigationBar.stories.tsx b/frontend/src/components/Common/NavigationBar/NavigationBar.stories.tsx
deleted file mode 100644
index a9242715..00000000
--- a/frontend/src/components/Common/NavigationBar/NavigationBar.stories.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import NavigationBar from './NavigationBar';
-
-const meta: Meta = {
- title: 'common/NavigationBar',
- component: NavigationBar,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- render: () => (
-
-
-
- ),
-};
diff --git a/frontend/src/components/Common/NavigationBar/NavigationBar.tsx b/frontend/src/components/Common/NavigationBar/NavigationBar.tsx
deleted file mode 100644
index 8cbfbee3..00000000
--- a/frontend/src/components/Common/NavigationBar/NavigationBar.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { Link, Text, theme } from '@fun-eat/design-system';
-import { Link as RouterLink, useLocation } from 'react-router-dom';
-import styled from 'styled-components';
-
-import SvgIcon from '../Svg/SvgIcon';
-
-import { NAVIGATION_MENU } from '@/constants';
-
-const NavigationBar = () => {
- const location = useLocation();
-
- return (
-
-
- {NAVIGATION_MENU.map(({ variant, name, path }) => {
- const currentPath = location.pathname.split('/')[1];
- const isSelected = currentPath === path.split('/')[1];
-
- return (
-
-
-
-
- {name}
-
-
-
- );
- })}
-
-
- );
-};
-
-export default NavigationBar;
-
-const NavigationBarContainer = styled.nav`
- width: 100%;
- height: 60px;
-`;
-
-const NavigationBarList = styled.ul`
- display: flex;
- justify-content: space-around;
- align-items: center;
- padding-top: 12px;
- border: 1px solid ${({ theme }) => theme.borderColors.disabled};
- border-bottom: none;
- border-top-left-radius: 20px;
- border-top-right-radius: 20px;
-`;
-
-const NavigationItem = styled.li`
- height: 50px;
-`;
-
-const NavigationLink = styled(Link)`
- display: flex;
- flex-direction: column;
- gap: 4px;
- justify-content: flex-end;
- align-items: center;
-`;
diff --git a/frontend/src/components/Common/RegisterButton/RegisterButton.tsx b/frontend/src/components/Common/RegisterButton/RegisterButton.tsx
deleted file mode 100644
index 1601e560..00000000
--- a/frontend/src/components/Common/RegisterButton/RegisterButton.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { Button } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import { useMemberQuery } from '@/hooks/queries/members';
-
-interface RegisterButtonProps {
- activeLabel: string;
- disabledLabel: string;
- onClick: () => void;
-}
-
-const RegisterButton = ({ activeLabel, disabledLabel, onClick }: RegisterButtonProps) => {
- const { data: member } = useMemberQuery();
-
- return (
-
- {member ? activeLabel : disabledLabel}
-
- );
-};
-
-export default RegisterButton;
-
-const RegisterButtonContainer = styled(Button)`
- cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
-`;
diff --git a/frontend/src/components/Common/ScrollButton/ScrollButton.stories.tsx b/frontend/src/components/Common/ScrollButton/ScrollButton.stories.tsx
deleted file mode 100644
index 50b796d2..00000000
--- a/frontend/src/components/Common/ScrollButton/ScrollButton.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ScrollButton from './ScrollButton';
-
-const meta: Meta = {
- title: 'common/ScrollButton',
- component: ScrollButton,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/ScrollButton/ScrollButton.tsx b/frontend/src/components/Common/ScrollButton/ScrollButton.tsx
deleted file mode 100644
index 6bfcc509..00000000
--- a/frontend/src/components/Common/ScrollButton/ScrollButton.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { Button } from '@fun-eat/design-system';
-import type { RefObject } from 'react';
-import { styled } from 'styled-components';
-
-import SvgIcon from '../Svg/SvgIcon';
-
-import { useScroll } from '@/hooks/common';
-
-interface ScrollButtonProps {
- targetRef: RefObject;
- isRecipePage?: boolean;
-}
-
-const ScrollButton = ({ targetRef, isRecipePage = false }: ScrollButtonProps) => {
- const { scrollToTop } = useScroll();
-
- const handleScroll = () => {
- if (targetRef) {
- scrollToTop(targetRef);
- }
- };
-
- return (
-
-
-
- );
-};
-
-export default ScrollButton;
-
-const ScrollButtonWrapper = styled(Button)>`
- position: fixed;
- bottom: ${({ isRecipePage }) => (isRecipePage ? '210px' : '90px')};
- right: 20px;
- border-radius: 50%;
- box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 4px;
-
- @media screen and (min-width: 600px) {
- left: calc(50% + 234px);
- }
-
- &:hover {
- transform: scale(1.1);
- transition: all 200ms ease-in-out;
- }
-
- svg {
- rotate: 90deg;
- }
-`;
diff --git a/frontend/src/components/Common/SectionTitle/SectionTitle.stories.tsx b/frontend/src/components/Common/SectionTitle/SectionTitle.stories.tsx
deleted file mode 100644
index 05883aea..00000000
--- a/frontend/src/components/Common/SectionTitle/SectionTitle.stories.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import SectionTitle from './SectionTitle';
-
-const meta: Meta = {
- title: 'common/SectionTitle',
- component: SectionTitle,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- args: {
- name: '사이다',
- },
-};
-
-export const Bookmarked: Story = {
- args: {
- name: '사이다',
- },
-};
diff --git a/frontend/src/components/Common/SectionTitle/SectionTitle.tsx b/frontend/src/components/Common/SectionTitle/SectionTitle.tsx
deleted file mode 100644
index c9d649dd..00000000
--- a/frontend/src/components/Common/SectionTitle/SectionTitle.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Button, Heading, Link, theme } from '@fun-eat/design-system';
-import { Link as RouterLink } from 'react-router-dom';
-import styled from 'styled-components';
-
-import { SvgIcon } from '@/components/Common';
-import { useRoutePage } from '@/hooks/common';
-
-interface SectionTitleProps {
- name: string;
- link?: string;
-}
-
-const SectionTitle = ({ name, link }: SectionTitleProps) => {
- const { routeBack } = useRoutePage();
-
- return (
-
-
-
- {link ? (
-
- {name}
-
- ) : (
- {name}
- )}
- {link && }
-
-
- );
-};
-
-export default SectionTitle;
-
-const SectionTitleContainer = styled.div`
- display: flex;
- justify-content: space-between;
- align-items: center;
-`;
-
-const SectionTitleWrapper = styled.div`
- display: flex;
- align-items: center;
-
- svg {
- padding-top: 2px;
- }
-`;
-
-const ProductName = styled(Heading)`
- margin: 0 5px 0 16px;
-`;
diff --git a/frontend/src/components/Common/Skeleton/Skeleton.stories.tsx b/frontend/src/components/Common/Skeleton/Skeleton.stories.tsx
deleted file mode 100644
index e8952c31..00000000
--- a/frontend/src/components/Common/Skeleton/Skeleton.stories.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import Skeleton from './Skeleton';
-
-const meta: Meta = {
- title: 'common/Skeleton',
- component: Skeleton,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- args: {
- width: 100,
- height: 100,
- },
-};
diff --git a/frontend/src/components/Common/Skeleton/Skeleton.tsx b/frontend/src/components/Common/Skeleton/Skeleton.tsx
deleted file mode 100644
index 857d0307..00000000
--- a/frontend/src/components/Common/Skeleton/Skeleton.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import type { ComponentPropsWithoutRef } from 'react';
-import styled from 'styled-components';
-
-interface SkeletonProps extends ComponentPropsWithoutRef<'div'> {
- width?: string | number;
- height?: string | number;
-}
-
-const Skeleton = ({ width, height }: SkeletonProps) => {
- return ;
-};
-
-export default Skeleton;
-
-export const SkeletonContainer = styled.div`
- position: absolute;
- width: ${({ width }) => (typeof width === 'number' ? width + 'px' : width)};
- height: ${({ height }) => (typeof height === 'number' ? height + 'px' : height)};
- border-radius: 8px;
- background: linear-gradient(-90deg, #dddddd, #f7f7f7, #dddddd, #f7f7f7);
- background-size: 400%;
- overflow: hidden;
- animation: skeleton-gradient 5s infinite ease-out;
-
- @keyframes skeleton-gradient {
- 0% {
- background-position: 0% 50%;
- }
- 50% {
- background-position: 100% 50%;
- }
- 100% {
- background-position: 0% 50%;
- }
- }
-`;
diff --git a/frontend/src/components/Common/SortButton/SortButton.stories.tsx b/frontend/src/components/Common/SortButton/SortButton.stories.tsx
deleted file mode 100644
index 004845ef..00000000
--- a/frontend/src/components/Common/SortButton/SortButton.stories.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import SortButton from './SortButton';
-
-import { PRODUCT_SORT_OPTIONS } from '@/constants';
-
-const meta: Meta = {
- title: 'common/SortButton',
- component: SortButton,
- args: {
- option: PRODUCT_SORT_OPTIONS[0],
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/SortButton/SortButton.tsx b/frontend/src/components/Common/SortButton/SortButton.tsx
deleted file mode 100644
index 54d7acc1..00000000
--- a/frontend/src/components/Common/SortButton/SortButton.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Button, useTheme } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import SvgIcon from '../Svg/SvgIcon';
-
-import type { SortOption } from '@/types/common';
-
-interface SortButtonProps {
- option: SortOption;
- onClick: () => void;
-}
-
-const SortButton = ({ option, onClick }: SortButtonProps) => {
- const theme = useTheme();
-
- return (
-
-
- {option.label}
-
- );
-};
-
-export default SortButton;
-
-const SortButtonContainer = styled(Button)`
- display: flex;
- justify-content: flex-end;
- align-items: center;
- padding: 0;
- column-gap: 4px;
-`;
diff --git a/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx b/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx
deleted file mode 100644
index 5e7c2f93..00000000
--- a/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { BottomSheet, useBottomSheet } from '@fun-eat/design-system';
-import type { Meta, StoryObj } from '@storybook/react';
-import { useEffect } from 'react';
-
-import SortOptionList from './SortOptionList';
-
-import { PRODUCT_SORT_OPTIONS } from '@/constants';
-import { useSortOption } from '@/hooks/common';
-
-const meta: Meta = {
- title: 'common/SortOptionList',
- component: SortOptionList,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- render: () => {
- const { isOpen, isClosing, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet();
- const { selectedOption, selectSortOption } = useSortOption(PRODUCT_SORT_OPTIONS[0]);
-
- useEffect(() => {
- handleOpenBottomSheet();
- }, []);
-
- return (
-
-
-
- );
- },
-};
diff --git a/frontend/src/components/Common/SortOptionList/SortOptionList.tsx b/frontend/src/components/Common/SortOptionList/SortOptionList.tsx
deleted file mode 100644
index fb62c72c..00000000
--- a/frontend/src/components/Common/SortOptionList/SortOptionList.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import { Button } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import type { SortOption } from '@/types/common';
-
-interface SortOptionListProps {
- options: readonly SortOption[];
- selectedOption: SortOption;
- selectSortOption: (selectedOptionLabel: SortOption) => void;
- close: () => void;
-}
-
-const SortOptionList = ({ options, selectedOption, selectSortOption, close }: SortOptionListProps) => {
- const handleSelectedOption = (sortOption: SortOption) => {
- selectSortOption(sortOption);
- close();
- };
-
- return (
-
- {options.map((sortOption) => {
- const isSelected = sortOption.label === selectedOption.label;
- return (
-
- handleSelectedOption(sortOption)}
- >
- {sortOption.label}
-
-
- );
- })}
-
- );
-};
-
-export default SortOptionList;
-
-const SortOptionListContainer = styled.ul`
- padding: 20px;
-
- & > li {
- height: 60px;
- line-height: 60px;
- border-bottom: 1px solid ${({ theme }) => theme.dividerColors.disabled};
- }
-
- & > li:last-of-type {
- border: none;
- }
-`;
-
-const SortOptionButton = styled(Button)`
- padding: 10px 0;
- text-align: left;
- border: none;
- outline: transparent;
-
- &:hover {
- color: ${({ theme }) => theme.textColors.default};
- font-weight: ${({ theme }) => theme.fontWeights.bold};
- transition: all 200ms ease-in;
- }
-`;
diff --git a/frontend/src/components/Common/Svg/SvgIcon.stories.tsx b/frontend/src/components/Common/Svg/SvgIcon.stories.tsx
deleted file mode 100644
index 87c7f7e0..00000000
--- a/frontend/src/components/Common/Svg/SvgIcon.stories.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import { theme } from '@fun-eat/design-system';
-import type { Meta, StoryObj } from '@storybook/react';
-
-import SvgIcon, { SVG_ICON_VARIANTS } from './SvgIcon';
-
-const meta: Meta = {
- title: 'common/SvgIcon',
- component: SvgIcon,
- argTypes: {
- color: {
- control: {
- type: 'color',
- },
- },
- },
- args: {
- variant: 'recipe',
- color: theme.colors.gray4,
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Playground: Story = {};
-
-export const SvgIcons: Story = {
- render: () => {
- return (
- <>
- {SVG_ICON_VARIANTS.map((variant) => (
-
- ))}
- >
- );
- },
-};
diff --git a/frontend/src/components/Common/Svg/SvgIcon.tsx b/frontend/src/components/Common/Svg/SvgIcon.tsx
deleted file mode 100644
index e31256ae..00000000
--- a/frontend/src/components/Common/Svg/SvgIcon.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { theme } from '@fun-eat/design-system';
-import type { ComponentPropsWithoutRef, CSSProperties } from 'react';
-
-export const SVG_ICON_VARIANTS = [
- 'recipe',
- 'list',
- 'member',
- 'search',
- 'arrow',
- 'bookmark',
- 'bookmarkFilled',
- 'review',
- 'star',
- 'favorite',
- 'favoriteFilled',
- 'home',
- 'sort',
- 'kakao',
- 'close',
- 'triangle',
- 'plus',
- 'pencil',
- 'camera',
- 'link',
- 'plane',
- 'info',
- 'trashcan',
-] as const;
-export type SvgIconVariant = (typeof SVG_ICON_VARIANTS)[number];
-
-interface SvgIconProps extends ComponentPropsWithoutRef<'svg'> {
- /**
- * SvgSprite 컴포넌트의 symbol id입니다.
- */
- variant: SvgIconVariant;
- /**
- * SvgIcon의 색상입니다. (기본값 gray4)
- */
- color?: CSSProperties['color'];
- /**
- * SvgIcon의 너비입니다. (기본값 24)
- */
- width?: number;
- /**
- * SvgIcon의 높이입니다. (기본값 24)
- */
- height?: number;
-}
-
-const SvgIcon = ({ variant, width = 24, height = 24, color = theme.colors.gray4, ...props }: SvgIconProps) => {
- return (
-
- );
-};
-
-export default SvgIcon;
diff --git a/frontend/src/components/Common/Svg/SvgSprite.tsx b/frontend/src/components/Common/Svg/SvgSprite.tsx
deleted file mode 100644
index e0fa06dd..00000000
--- a/frontend/src/components/Common/Svg/SvgSprite.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-const SvgSprite = () => {
- return (
-
- );
-};
-
-export default SvgSprite;
diff --git a/frontend/src/components/Common/TabMenu/TabMenu.stories.tsx b/frontend/src/components/Common/TabMenu/TabMenu.stories.tsx
deleted file mode 100644
index 4ba72445..00000000
--- a/frontend/src/components/Common/TabMenu/TabMenu.stories.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import TabMenu from './TabMenu';
-
-const meta: Meta = {
- title: 'common/TabMenu',
- component: TabMenu,
- args: {
- tabMenus: ['리뷰 1,200', '꿀조합'],
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- render: ({ ...args }) => (
-
-
-
- ),
-};
diff --git a/frontend/src/components/Common/TabMenu/TabMenu.tsx b/frontend/src/components/Common/TabMenu/TabMenu.tsx
deleted file mode 100644
index 548b2f36..00000000
--- a/frontend/src/components/Common/TabMenu/TabMenu.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import { Button } from '@fun-eat/design-system';
-import type { ForwardedRef, MouseEventHandler } from 'react';
-import { forwardRef } from 'react';
-import styled from 'styled-components';
-
-interface TabMenuProps {
- tabMenus: readonly string[];
- selectedTabMenu: number;
- handleTabMenuSelect: (index: number) => void;
-}
-
-const TabMenu = (
- { tabMenus, selectedTabMenu, handleTabMenuSelect }: TabMenuProps,
- ref: ForwardedRef
-) => {
- const handleTabMenuClick: MouseEventHandler = (event) => {
- const { index } = event.currentTarget.dataset;
-
- if (index) {
- handleTabMenuSelect(Number(index));
- }
- };
-
- return (
-
- {tabMenus.map((menu, index) => {
- const isSelected = selectedTabMenu === index;
- return (
-
-
- {menu}
-
-
- );
- })}
-
- );
-};
-
-export default forwardRef(TabMenu);
-
-const TabMenuContainer = styled.ul`
- display: flex;
-`;
-
-const TabMenuItem = styled.li<{ isSelected: boolean }>`
- flex-grow: 1;
- width: 50%;
- height: 45px;
- border-bottom: 2px solid
- ${({ isSelected, theme }) => (isSelected ? theme.borderColors.strong : theme.borderColors.disabled)};
-`;
-
-const TabMenuButton = styled(Button)`
- padding: 0;
- line-height: 45px;
-`;
diff --git a/frontend/src/components/Common/TagList/TagList.stories.tsx b/frontend/src/components/Common/TagList/TagList.stories.tsx
deleted file mode 100644
index 4ac76785..00000000
--- a/frontend/src/components/Common/TagList/TagList.stories.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import TagList from './TagList';
-
-import productDetails from '@/mocks/data/productDetails.json';
-
-const meta: Meta = {
- title: 'common/TagList',
- component: TagList,
- args: {
- tags: productDetails[0].tags,
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Common/TagList/TagList.tsx b/frontend/src/components/Common/TagList/TagList.tsx
deleted file mode 100644
index b36cd70a..00000000
--- a/frontend/src/components/Common/TagList/TagList.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Badge } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import type { Tag } from '@/types/common';
-import { convertTagColor } from '@/utils/convertTagColor';
-
-interface TagListProps {
- tags: Tag[];
-}
-
-const TagList = ({ tags }: TagListProps) => {
- return (
-
- {tags.map((tag) => {
- const tagColor = convertTagColor(tag.tagType);
- return (
-
-
- {tag.name}
-
-
- );
- })}
-
- );
-};
-
-export default TagList;
-
-const TagListContainer = styled.ul`
- display: flex;
- margin: 12px 0;
- column-gap: 8px;
-`;
-
-const TagBadge = styled(Badge)`
- font-weight: bold;
-`;
diff --git a/frontend/src/components/Common/Toast/Toast.stories.tsx b/frontend/src/components/Common/Toast/Toast.stories.tsx
deleted file mode 100644
index 383c4375..00000000
--- a/frontend/src/components/Common/Toast/Toast.stories.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import Toast from './Toast';
-
-import ToastProvider from '@/contexts/ToastContext';
-import { useToastActionContext } from '@/hooks/context';
-
-const meta: Meta = {
- title: 'common/Toast',
- component: Toast,
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- render: () => {
- const { toast } = useToastActionContext();
- const handleClick = () => {
- toast.success('성공');
- };
- return (
-
-
-
- );
- },
-};
-
-export const Error: Story = {
- render: () => {
- const { toast } = useToastActionContext();
- const handleClick = () => {
- toast.error('실패');
- };
- return (
-
-
-
- );
- },
-};
diff --git a/frontend/src/components/Common/Toast/Toast.tsx b/frontend/src/components/Common/Toast/Toast.tsx
deleted file mode 100644
index c9d9dc46..00000000
--- a/frontend/src/components/Common/Toast/Toast.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Text, useTheme } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import { useToast } from '@/hooks/common';
-import { fadeOut, slideIn } from '@/styles/animations';
-
-interface ToastProps {
- id: number;
- message: string;
- isError?: boolean;
-}
-
-const Toast = ({ id, message, isError = false }: ToastProps) => {
- const theme = useTheme();
- const isShown = useToast(id);
-
- return (
-
- {message}
-
- );
-};
-
-export default Toast;
-
-type ToastStyleProps = Pick & { isAnimating?: boolean };
-
-const ToastWrapper = styled.div`
- position: relative;
- width: calc(100% - 20px);
- height: 55px;
- max-width: 560px;
- border-radius: 10px;
- background: ${({ isError, theme }) => (isError ? theme.colors.error : theme.colors.black)};
- animation: ${({ isAnimating }) => (isAnimating ? slideIn : fadeOut)} 0.3s ease-in-out forwards;
-`;
-
-const Message = styled(Text)`
- margin-left: 20px;
- line-height: 55px;
-`;
diff --git a/frontend/src/components/Common/index.ts b/frontend/src/components/Common/index.ts
deleted file mode 100644
index 070263f5..00000000
--- a/frontend/src/components/Common/index.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-export { default as CategoryFoodTab } from './CategoryFoodTab/CategoryFoodTab';
-export { default as CategoryStoreTab } from './CategoryStoreTab/CategoryStoreTab';
-export { default as Header } from './Header/Header';
-export { default as NavigationBar } from './NavigationBar/NavigationBar';
-export { default as SortButton } from './SortButton/SortButton';
-export { default as SortOptionList } from './SortOptionList/SortOptionList';
-export { default as SvgSprite } from './Svg/SvgSprite';
-export { default as SvgIcon } from './Svg/SvgIcon';
-export { default as TabMenu } from './TabMenu/TabMenu';
-export { default as TagList } from './TagList/TagList';
-export { default as SectionTitle } from './SectionTitle/SectionTitle';
-export { default as ScrollButton } from './ScrollButton/ScrollButton';
-export { default as Input } from './Input/Input';
-export { default as ImageUploader } from './ImageUploader/ImageUploader';
-export { default as ErrorBoundary } from './ErrorBoundary/ErrorBoundary';
-export { default as ErrorComponent } from './ErrorComponent/ErrorComponent';
-export { default as Loading } from './Loading/Loading';
-export { default as MarkedText } from './MarkedText/MarkedText';
-export { default as NavigableSectionTitle } from './NavigableSectionTitle/NavigableSectionTitle';
-export { default as Carousel } from './Carousel/Carousel';
-export { default as RegisterButton } from './RegisterButton/RegisterButton';
-export { default as Toast } from './Toast/Toast';
-export { default as CategoryItem } from './CategoryItem/CategoryItem';
-export { default as CategoryFoodList } from './CategoryFoodList/CategoryFoodList';
-export { default as CategoryStoreList } from './CategoryStoreList/CategoryStoreList';
-export { default as Skeleton } from './Skeleton/Skeleton';
-export { default as Banner } from './Banner/Banner';
diff --git a/frontend/src/components/Layout/AuthLayout.tsx b/frontend/src/components/Layout/AuthLayout.tsx
deleted file mode 100644
index 0cc0570c..00000000
--- a/frontend/src/components/Layout/AuthLayout.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Navigate } from 'react-router-dom';
-
-import { PATH } from '@/constants/path';
-import { useMemberQuery } from '@/hooks/queries/members';
-
-interface AuthLayoutProps {
- children: JSX.Element;
-}
-
-const AuthLayout = ({ children }: AuthLayoutProps) => {
- const { data: member } = useMemberQuery();
-
- if (!member) {
- return ;
- }
-
- return children;
-};
-
-export default AuthLayout;
diff --git a/frontend/src/components/Layout/DefaultLayout.tsx b/frontend/src/components/Layout/DefaultLayout.tsx
deleted file mode 100644
index 39f1f8cf..00000000
--- a/frontend/src/components/Layout/DefaultLayout.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import type { PropsWithChildren } from 'react';
-import styled from 'styled-components';
-
-import Header from '../Common/Header/Header';
-import NavigationBar from '../Common/NavigationBar/NavigationBar';
-
-const DefaultLayout = ({ children }: PropsWithChildren) => {
- return (
-
-
- {children}
-
-
- );
-};
-
-export default DefaultLayout;
-
-const DefaultLayoutContainer = styled.div`
- height: 100%;
- max-width: 600px;
- margin: 0 auto;
-`;
-
-const MainWrapper = styled.main`
- position: relative;
- height: calc(100% - 120px);
- overflow-x: hidden;
- overflow-y: auto;
-`;
diff --git a/frontend/src/components/Layout/HeaderOnlyLayout.tsx b/frontend/src/components/Layout/HeaderOnlyLayout.tsx
deleted file mode 100644
index ec3ad899..00000000
--- a/frontend/src/components/Layout/HeaderOnlyLayout.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import type { PropsWithChildren } from 'react';
-import styled from 'styled-components';
-
-import Header from '../Common/Header/Header';
-
-const HeaderOnlyLayout = ({ children }: PropsWithChildren) => {
- return (
-
-
- {children}
-
- );
-};
-
-export default HeaderOnlyLayout;
-
-const HeaderOnlyLayoutContainer = styled.div`
- height: 100%;
- max-width: 600px;
- margin: 0 auto;
-`;
-
-const MainWrapper = styled.main`
- position: relative;
- height: calc(100% - 60px);
- padding: 20px;
- overflow-x: hidden;
- overflow-y: auto;
-`;
diff --git a/frontend/src/components/Layout/MinimalLayout.tsx b/frontend/src/components/Layout/MinimalLayout.tsx
deleted file mode 100644
index 1fdfc36f..00000000
--- a/frontend/src/components/Layout/MinimalLayout.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import type { PropsWithChildren } from 'react';
-import styled from 'styled-components';
-
-const MinimalLayout = ({ children }: PropsWithChildren) => {
- return (
-
- {children}
-
- );
-};
-
-export default MinimalLayout;
-
-const MinimalLayoutContainer = styled.div`
- height: 100%;
- max-width: 600px;
- margin: 0 auto;
-`;
-
-const MainWrapper = styled.main`
- position: relative;
- height: 100%;
- padding: 20px;
- overflow-x: hidden;
- overflow-y: auto;
-`;
diff --git a/frontend/src/components/Layout/SimpleHeaderLayout.tsx b/frontend/src/components/Layout/SimpleHeaderLayout.tsx
deleted file mode 100644
index 4f17b93a..00000000
--- a/frontend/src/components/Layout/SimpleHeaderLayout.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import type { PropsWithChildren } from 'react';
-import styled from 'styled-components';
-
-import Header from '../Common/Header/Header';
-import NavigationBar from '../Common/NavigationBar/NavigationBar';
-
-const SimpleHeaderLayout = ({ children }: PropsWithChildren) => {
- return (
-
-
- {children}
-
-
- );
-};
-
-export default SimpleHeaderLayout;
-
-const SimpleHeaderLayoutContainer = styled.div`
- height: 100%;
- max-width: 600px;
- margin: 0 auto;
-`;
-
-const MainWrapper = styled.main`
- position: relative;
- height: calc(100% - 120px);
- padding: 20px 20px 0;
- overflow-x: hidden;
- overflow-y: auto;
-`;
diff --git a/frontend/src/components/Layout/index.ts b/frontend/src/components/Layout/index.ts
deleted file mode 100644
index 69ec1a40..00000000
--- a/frontend/src/components/Layout/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export { default as DefaultLayout } from './DefaultLayout';
-export { default as MinimalLayout } from './MinimalLayout';
-export { default as HeaderOnlyLayout } from './HeaderOnlyLayout';
-export { default as AuthLayout } from './AuthLayout';
-export { default as SimpleHeaderLayout } from './SimpleHeaderLayout';
diff --git a/frontend/src/components/Members/MemberModifyInput/MemberModifyInput.tsx b/frontend/src/components/Members/MemberModifyInput/MemberModifyInput.tsx
deleted file mode 100644
index 39a78e6c..00000000
--- a/frontend/src/components/Members/MemberModifyInput/MemberModifyInput.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Heading, Spacing, Text, useTheme } from '@fun-eat/design-system';
-import type { ChangeEventHandler } from 'react';
-import styled from 'styled-components';
-
-import { Input } from '@/components/Common';
-
-const MIN_LENGTH = 1;
-const MAX_LENGTH = 10;
-
-interface MemberModifyInputProps {
- nickname: string;
- modifyNickname: ChangeEventHandler;
-}
-
-const MemberModifyInput = ({ nickname, modifyNickname }: MemberModifyInputProps) => {
- const theme = useTheme();
-
- return (
-
-
- 닉네임
-
-
- {nickname.length}자 / {MAX_LENGTH}자
-
-
-
-
- );
-};
-
-export default MemberModifyInput;
-
-const MemberModifyInputContainer = styled.div`
- position: relative;
-`;
-
-const NicknameStatusText = styled(Text)`
- position: absolute;
- top: 0;
- right: 0;
-`;
diff --git a/frontend/src/components/Members/MemberRecipeList/MemberRecipeList.stories.tsx b/frontend/src/components/Members/MemberRecipeList/MemberRecipeList.stories.tsx
deleted file mode 100644
index 9b28324e..00000000
--- a/frontend/src/components/Members/MemberRecipeList/MemberRecipeList.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import MemberRecipeList from './MemberRecipeList';
-
-const meta: Meta = {
- title: 'members/ MemberRecipeList',
- component: MemberRecipeList,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Members/MemberRecipeList/MemberRecipeList.tsx b/frontend/src/components/Members/MemberRecipeList/MemberRecipeList.tsx
deleted file mode 100644
index 740daddf..00000000
--- a/frontend/src/components/Members/MemberRecipeList/MemberRecipeList.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import { Link, Spacing, Text, theme } from '@fun-eat/design-system';
-import { useRef } from 'react';
-import { Link as RouterLink } from 'react-router-dom';
-import styled from 'styled-components';
-
-import { RecipeItem } from '@/components/Recipe';
-import { PATH } from '@/constants/path';
-import { useIntersectionObserver } from '@/hooks/common';
-import { useInfiniteMemberRecipeQuery } from '@/hooks/queries/members';
-import useDisplaySlice from '@/utils/displaySlice';
-
-interface MemberRecipeListProps {
- isPreview?: boolean;
-}
-
-const MemberRecipeList = ({ isPreview = false }: MemberRecipeListProps) => {
- const scrollRef = useRef(null);
-
- const { fetchNextPage, hasNextPage, data } = useInfiniteMemberRecipeQuery();
- const memberRecipes = data?.pages.flatMap((page) => page.recipes);
- const recipeToDisplay = useDisplaySlice(isPreview, memberRecipes);
-
- useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage);
-
- const totalRecipeCount = data?.pages[0].page.totalDataCount;
-
- if (totalRecipeCount === 0) {
- return (
-
-
- 앗, 작성한 꿀조합이 없네요 🥲
-
-
-
- 꿀조합 작성하러 가기
-
-
- );
- }
-
- return (
-
- {!isPreview && (
-
- 총 {totalRecipeCount}개의 꿀조합을 남겼어요!
-
- )}
-
-
- {recipeToDisplay?.map((recipe) => (
-
-
-
-
-
- ))}
-
-
-
- );
-};
-
-export default MemberRecipeList;
-
-const MemberRecipeListContainer = styled.section`
- display: flex;
- flex-direction: column;
-`;
-
-const MemberRecipeListWrapper = styled.ul`
- display: flex;
- flex-direction: column;
- gap: 20px;
-`;
-
-const TotalRecipeCount = styled(Text)`
- text-align: right;
-`;
-
-const ErrorContainer = styled.div`
- display: flex;
- flex-direction: column;
- align-items: center;
- margin-top: 20px;
-`;
-
-const RecipeLink = styled(Link)`
- padding: 12px 12px;
- border: 1px solid ${({ theme }) => theme.colors.gray4};
- border-radius: 8px;
-`;
diff --git a/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.stories.tsx b/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.stories.tsx
deleted file mode 100644
index a631341d..00000000
--- a/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.stories.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import MemberReviewItem from './MemberReviewItem';
-
-import ToastProvider from '@/contexts/ToastContext';
-
-const meta: Meta = {
- title: 'members/MemberReviewItem',
- component: MemberReviewItem,
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
- args: {
- review: {
- reviewId: 1,
- productId: 5,
- productName: '구운감자슬림명란마요',
- content:
- '할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데요 이것보다 긴 리뷰도 잘려 보인답니다',
- rating: 4.0,
- favoriteCount: 1256,
- categoryType: 'food',
- },
- isPreview: true,
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.tsx b/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.tsx
deleted file mode 100644
index 1d450385..00000000
--- a/frontend/src/components/Members/MemberReviewItem/MemberReviewItem.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import { useTheme, Spacing, Text, Button } from '@fun-eat/design-system';
-import type { MouseEventHandler } from 'react';
-import styled from 'styled-components';
-
-import { SvgIcon } from '@/components/Common';
-import { useToastActionContext } from '@/hooks/context';
-import { useDeleteReview } from '@/hooks/queries/members';
-import type { MemberReview } from '@/types/review';
-
-interface MemberReviewItemProps {
- review: MemberReview;
- isPreview: boolean;
-}
-
-const MemberReviewItem = ({ review, isPreview }: MemberReviewItemProps) => {
- const theme = useTheme();
-
- const { mutate } = useDeleteReview();
-
- const { toast } = useToastActionContext();
-
- const { reviewId, productName, content, rating, favoriteCount } = review;
-
- const handleReviewDelete: MouseEventHandler = (e) => {
- e.preventDefault();
-
- const result = window.confirm('리뷰를 삭제하시겠습니까?');
- if (!result) {
- return;
- }
-
- mutate(reviewId, {
- onSuccess: () => {
- toast.success('리뷰를 삭제했습니다.');
- },
- onError: (error) => {
- if (error instanceof Error) {
- toast.error(error.message);
- return;
- }
-
- toast.error('리뷰 좋아요를 다시 시도해주세요.');
- },
- });
- };
-
- return (
-
-
-
- {productName}
-
- {!isPreview && (
-
- )}
-
-
- {content}
-
-
-
-
-
-
- {favoriteCount}
-
-
-
-
-
- {rating.toFixed(1)}
-
-
-
-
- );
-};
-
-export default MemberReviewItem;
-
-const ReviewRankingItemContainer = styled.div`
- display: flex;
- flex-direction: column;
- gap: 4px;
- padding: 12px 0;
- border-bottom: ${({ theme }) => `1px solid ${theme.borderColors.disabled}`};
-`;
-
-const ProductNameIconWrapper = styled.div`
- display: flex;
- justify-content: space-between;
-`;
-
-const ReviewText = styled(Text)`
- display: -webkit-inline-box;
- text-overflow: ellipsis;
- overflow: hidden;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
-`;
-
-const FavoriteStarWrapper = styled.div`
- display: flex;
- gap: 4px;
-`;
-
-const FavoriteIconWrapper = styled.div`
- display: flex;
- gap: 4px;
- align-items: center;
-`;
-
-const RatingIconWrapper = styled.div`
- display: flex;
- gap: 2px;
- align-items: center;
-
- & > svg {
- padding-bottom: 2px;
- }
-`;
diff --git a/frontend/src/components/Members/MemberReviewList/MemberReviewList.stories.tsx b/frontend/src/components/Members/MemberReviewList/MemberReviewList.stories.tsx
deleted file mode 100644
index 97c95ac9..00000000
--- a/frontend/src/components/Members/MemberReviewList/MemberReviewList.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import MemberReviewList from './MemberReviewList';
-
-const meta: Meta = {
- title: 'members/MemberReviewList',
- component: MemberReviewList,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx b/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx
deleted file mode 100644
index b622d9f6..00000000
--- a/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import { Link, Spacing, Text, theme } from '@fun-eat/design-system';
-import { useRef } from 'react';
-import { Link as RouterLink } from 'react-router-dom';
-import styled from 'styled-components';
-
-import MemberReviewItem from '../MemberReviewItem/MemberReviewItem';
-
-import { PATH } from '@/constants/path';
-import { useIntersectionObserver } from '@/hooks/common';
-import { useInfiniteMemberReviewQuery } from '@/hooks/queries/members';
-import useDisplaySlice from '@/utils/displaySlice';
-
-interface MemberReviewListProps {
- isPreview?: boolean;
-}
-
-const MemberReviewList = ({ isPreview = false }: MemberReviewListProps) => {
- const scrollRef = useRef(null);
- const { fetchNextPage, hasNextPage, data } = useInfiniteMemberReviewQuery();
- const memberReviews = data.pages.flatMap((page) => page.reviews);
- const reviewsToDisplay = useDisplaySlice(isPreview, memberReviews);
-
- useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage);
-
- const totalReviewCount = data.pages[0].page.totalDataCount;
-
- if (totalReviewCount === 0) {
- return (
-
-
- 앗, 작성한 리뷰가 없네요 🥲
-
-
-
- 리뷰 작성하러 가기
-
-
- );
- }
-
- return (
-
- {!isPreview && (
-
- 총 {totalReviewCount}개의 리뷰를 남겼어요!
-
- )}
-
-
- {reviewsToDisplay.map((review) => (
-
-
-
-
-
- ))}
-
-
-
- );
-};
-
-export default MemberReviewList;
-
-const MemberReviewListContainer = styled.section`
- display: flex;
- flex-direction: column;
-`;
-
-const MemberReviewListWrapper = styled.ul`
- display: flex;
- flex-direction: column;
- gap: 20px;
-`;
-
-const TotalReviewCount = styled(Text)`
- text-align: right;
-`;
-
-const ErrorContainer = styled.div`
- display: flex;
- flex-direction: column;
- align-items: center;
- margin-top: 20px;
-`;
-
-const ReviewLink = styled(Link)`
- padding: 12px 12px;
- border: 1px solid ${({ theme }) => theme.colors.gray4};
- border-radius: 8px;
-`;
diff --git a/frontend/src/components/Members/MembersInfo/MembersInfo.tsx b/frontend/src/components/Members/MembersInfo/MembersInfo.tsx
deleted file mode 100644
index 1b2e9483..00000000
--- a/frontend/src/components/Members/MembersInfo/MembersInfo.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import { Button, Heading, Link, theme } from '@fun-eat/design-system';
-import { Link as RouterLink } from 'react-router-dom';
-import styled from 'styled-components';
-
-import { SvgIcon } from '@/components/Common';
-import { PATH } from '@/constants/path';
-import { useLogoutMutation, useMemberQuery } from '@/hooks/queries/members';
-
-const MembersInfo = () => {
- const { data: member } = useMemberQuery();
- const { mutate } = useLogoutMutation();
-
- if (!member) {
- return null;
- }
-
- const { nickname, profileImage } = member;
-
- const handleLogout = () => {
- mutate();
- };
-
- return (
-
-
-
-
- {nickname} 님
-
-
-
-
-
-
-
- );
-};
-
-export default MembersInfo;
-
-const MembersInfoContainer = styled.div`
- display: flex;
- justify-content: space-between;
- align-items: center;
-`;
-
-const MemberInfoWrapper = styled.div`
- display: flex;
- align-items: center;
-`;
-
-const MemberModifyLink = styled(Link)`
- margin-left: 5px;
- transform: translateY(1px);
-`;
-
-const MembersImage = styled.img`
- margin-right: 16px;
- border: 2px solid ${({ theme }) => theme.colors.primary};
- border-radius: 50%;
- object-fit: cover;
-`;
diff --git a/frontend/src/components/Members/MembersInfo/MyPageInfo.stories.tsx b/frontend/src/components/Members/MembersInfo/MyPageInfo.stories.tsx
deleted file mode 100644
index a44e59f4..00000000
--- a/frontend/src/components/Members/MembersInfo/MyPageInfo.stories.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import MembersInfo from './MembersInfo';
-
-import mockMember from '@/mocks/data/members.json';
-
-const meta: Meta = {
- title: 'members/MembersInfo',
- component: MembersInfo,
- args: {
- member: mockMember,
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Members/index.ts b/frontend/src/components/Members/index.ts
deleted file mode 100644
index 4e31460e..00000000
--- a/frontend/src/components/Members/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export { default as MembersInfo } from './MembersInfo/MembersInfo';
-export { default as MemberReviewList } from './MemberReviewList/MemberReviewList';
-export { default as MemberRecipeList } from './MemberRecipeList/MemberRecipeList';
-export { default as MemberModifyInput } from './MemberModifyInput/MemberModifyInput';
-export { default as MemberReviewItem } from './MemberReviewItem/MemberReviewItem';
diff --git a/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.stories.tsx b/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.stories.tsx
deleted file mode 100644
index 32226fec..00000000
--- a/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.stories.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ProductDetailItem from './ProductDetailItem';
-
-import productDetail from '@/mocks/data/productDetail.json';
-
-const meta: Meta = {
- title: 'product/ProductDetailItem',
- component: ProductDetailItem,
- args: {
- productDetail: productDetail,
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- render: ({ ...args }) => (
-
- ),
-};
diff --git a/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.tsx b/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.tsx
deleted file mode 100644
index b91b97e2..00000000
--- a/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import { Text, useTheme } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import PreviewImage from '@/assets/characters.svg';
-import PBPreviewImage from '@/assets/samgakgimbab.svg';
-import { SvgIcon, TagList } from '@/components/Common';
-import { CATEGORY_TYPE } from '@/constants';
-import type { ProductDetail } from '@/types/product';
-
-interface ProductDetailItemProps {
- category: string;
- productDetail: ProductDetail;
-}
-
-const ProductDetailItem = ({ category, productDetail }: ProductDetailItemProps) => {
- const { name, price, image, content, averageRating, tags } = productDetail;
-
- const theme = useTheme();
-
- return (
-
-
- {image ? (
-
- ) : category === CATEGORY_TYPE.FOOD ? (
-
- ) : (
-
- )}
-
-
-
-
- 가격
- {price.toLocaleString('ko-KR')}원
-
-
- 상품 설명
- {content}
-
-
- 평균 평점
-
-
- {averageRating.toFixed(1)}
-
-
-
-
- );
-};
-
-export default ProductDetailItem;
-
-const ProductDetailContainer = styled.div`
- display: flex;
- flex-direction: column;
- row-gap: 30px;
- g & > img,
- svg {
- align-self: center;
- }
-`;
-
-const ImageWrapper = styled.div`
- display: flex;
- justify-content: center;
- align-items: center;
-`;
-
-const DetailInfoWrapper = styled.div`
- & > div + div {
- margin-top: 10px;
- }
-`;
-
-const DescriptionWrapper = styled.div`
- display: flex;
- column-gap: 20px;
-
- & > p:first-of-type {
- flex-shrink: 0;
- width: 60px;
- }
-`;
-
-const ProductContent = styled(Text)`
- white-space: pre-wrap;
-`;
-
-const RatingIconWrapper = styled.div`
- display: flex;
- align-items: center;
- margin-left: -4px;
- column-gap: 4px;
-
- & > svg {
- padding-bottom: 2px;
- }
-`;
diff --git a/frontend/src/components/Product/ProductItem/ProductItem.stories.tsx b/frontend/src/components/Product/ProductItem/ProductItem.stories.tsx
deleted file mode 100644
index 6afb4a23..00000000
--- a/frontend/src/components/Product/ProductItem/ProductItem.stories.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ProductItem from './ProductItem';
-
-import mockProducts from '@/mocks/data/products.json';
-
-const meta: Meta = {
- title: 'product/ProductItem',
- component: ProductItem,
- args: {
- product: mockProducts.products[0],
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Product/ProductItem/ProductItem.tsx b/frontend/src/components/Product/ProductItem/ProductItem.tsx
deleted file mode 100644
index 43dc773e..00000000
--- a/frontend/src/components/Product/ProductItem/ProductItem.tsx
+++ /dev/null
@@ -1,112 +0,0 @@
-import { Text, useTheme } from '@fun-eat/design-system';
-import { memo, useState } from 'react';
-import { useParams } from 'react-router-dom';
-import styled from 'styled-components';
-
-import PreviewImage from '@/assets/characters.svg';
-import PBPreviewImage from '@/assets/samgakgimbab.svg';
-import { Skeleton, SvgIcon } from '@/components/Common';
-import { CATEGORY_TYPE } from '@/constants';
-import type { Product } from '@/types/product';
-
-interface ProductItemProps {
- product: Product;
-}
-
-const ProductItem = ({ product }: ProductItemProps) => {
- const theme = useTheme();
- const { category } = useParams();
- const { name, price, image, averageRating, reviewCount } = product;
- const [isImageLoading, setIsImageLoading] = useState(true);
-
- return (
-
- {image ? (
- <>
- setIsImageLoading(false)}
- />
- {isImageLoading && }
- >
- ) : category === CATEGORY_TYPE.FOOD ? (
-
- ) : (
-
- )}
-
-
- {name}
-
-
- {price.toLocaleString('ko-KR')}원
-
-
-
-
-
- {averageRating.toFixed(1)}
-
-
-
-
-
- {reviewCount}
-
-
-
-
-
- );
-};
-
-export default memo(ProductItem);
-
-const ProductItemContainer = styled.div`
- position: relative;
- display: flex;
- align-items: center;
- padding: 12px 0;
-`;
-
-const ProductImage = styled.img`
- object-fit: cover;
-`;
-
-const ProductInfoWrapper = styled.div`
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- height: 100%;
- margin-left: 30px;
-`;
-
-const ProductReviewWrapper = styled.div`
- display: flex;
- margin-left: -2px;
- column-gap: 20px;
-`;
-
-const RatingIconWrapper = styled.div`
- display: flex;
- align-items: center;
- column-gap: 4px;
-
- & > svg {
- padding-bottom: 2px;
- }
-`;
-
-const ReviewIconWrapper = styled.div`
- display: flex;
- align-items: center;
- column-gap: 4px;
-
- & > svg {
- padding-top: 2px;
- }
-`;
diff --git a/frontend/src/components/Product/ProductList/ProductList.stories.tsx b/frontend/src/components/Product/ProductList/ProductList.stories.tsx
deleted file mode 100644
index de89073b..00000000
--- a/frontend/src/components/Product/ProductList/ProductList.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ProductList from './ProductList';
-
-const meta: Meta = {
- title: 'product/ProductList',
- component: ProductList,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Product/ProductList/ProductList.tsx b/frontend/src/components/Product/ProductList/ProductList.tsx
deleted file mode 100644
index 2bdaf30e..00000000
--- a/frontend/src/components/Product/ProductList/ProductList.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import { Link } from '@fun-eat/design-system';
-import { useRef } from 'react';
-import { Link as RouterLink } from 'react-router-dom';
-import styled from 'styled-components';
-
-import ProductItem from '../ProductItem/ProductItem';
-
-import { PATH } from '@/constants/path';
-import { useIntersectionObserver } from '@/hooks/common';
-import { useCategoryValueContext } from '@/hooks/context';
-import { useInfiniteProductsQuery } from '@/hooks/queries/product';
-import type { CategoryVariant, SortOption } from '@/types/common';
-
-interface ProductListProps {
- category: CategoryVariant;
- selectedOption?: SortOption;
-}
-
-const ProductList = ({ category, selectedOption }: ProductListProps) => {
- const scrollRef = useRef(null);
- const { categoryIds } = useCategoryValueContext();
-
- const { fetchNextPage, hasNextPage, data } = useInfiniteProductsQuery(
- categoryIds[category],
- selectedOption?.value ?? 'reviewCount,desc'
- );
-
- useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage);
-
- const productList = data.pages.flatMap((page) => page.products);
-
- return (
- <>
-
- {productList.map((product) => (
-
-
-
-
-
- ))}
-
-
- >
- );
-};
-
-export default ProductList;
-
-const ProductListContainer = styled.ul`
- display: flex;
- flex-direction: column;
-
- & > li {
- border-bottom: 1px solid ${({ theme }) => theme.borderColors.disabled};
- }
-`;
diff --git a/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.stories.tsx b/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.stories.tsx
deleted file mode 100644
index 4dc5590b..00000000
--- a/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.stories.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ProductOverviewItem from './ProductOverviewItem';
-
-const meta: Meta = {
- title: 'product/ProductOverviewItem',
- component: ProductOverviewItem,
- args: {
- image: 'https://t3.ftcdn.net/jpg/06/06/91/70/240_F_606917032_4ujrrMV8nspZDX8nTgGrTpJ69N9JNxOL.jpg',
- name: '소금빵',
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
-
-export const Ranking: Story = {
- args: {
- rank: 1,
- },
-};
diff --git a/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.tsx b/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.tsx
deleted file mode 100644
index ef281107..00000000
--- a/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import { Text } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import PreviewImage from '@/assets/characters.svg';
-
-interface ProductOverviewItemProps {
- name: string;
- image: string | null;
- rank?: number;
-}
-
-const ProductOverviewItem = ({ name, image, rank }: ProductOverviewItemProps) => {
- return (
-
-
- {rank ?? ''}
-
- {image !== null ? (
-
- ) : (
-
- )}
-
- {name}
-
-
- );
-};
-
-export default ProductOverviewItem;
-
-const ProductOverviewContainer = styled.div>`
- display: flex;
- gap: 15px;
- align-items: center;
- height: 50px;
- padding: 0 15px;
- border-radius: ${({ theme }) => theme.borderRadius.xs};
- background: ${({ theme, rank }) => (rank ? theme.colors.gray1 : theme.colors.white)};
-`;
-
-const ProductOverviewImage = styled.img`
- width: 45px;
- height: 45px;
- border-radius: 50%;
-`;
-
-const ProductPreviewImage = styled(PreviewImage)`
- width: 45px;
- height: 45px;
- border-radius: 50%;
- background-color: ${({ theme }) => theme.colors.white};
-`;
-
-const ProductOverviewText = styled(Text)`
- white-space: nowrap;
- text-overflow: ellipsis;
- word-break: break-all;
- overflow: hidden;
-`;
diff --git a/frontend/src/components/Product/ProductRecipeList/ProductRecipeList.tsx b/frontend/src/components/Product/ProductRecipeList/ProductRecipeList.tsx
deleted file mode 100644
index 7298f3c8..00000000
--- a/frontend/src/components/Product/ProductRecipeList/ProductRecipeList.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { Link, Text } from '@fun-eat/design-system';
-import { useRef } from 'react';
-import { Link as RouterLink } from 'react-router-dom';
-import styled from 'styled-components';
-
-import { RecipeItem } from '@/components/Recipe';
-import { PATH } from '@/constants/path';
-import { useIntersectionObserver } from '@/hooks/common';
-import { useInfiniteProductRecipesQuery } from '@/hooks/queries/product';
-import type { SortOption } from '@/types/common';
-
-interface ProductRecipeListProps {
- productId: number;
- productName: string;
- selectedOption: SortOption;
-}
-
-const ProductRecipeList = ({ productId, productName, selectedOption }: ProductRecipeListProps) => {
- const scrollRef = useRef(null);
- const { fetchNextPage, hasNextPage, data } = useInfiniteProductRecipesQuery(productId, selectedOption.value);
- useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage);
-
- const recipes = data.pages.flatMap((page) => page.recipes);
-
- if (recipes.length === 0) {
- return (
-
-
- {productName}을/를 {'\n'}사용한 꿀조합을 만들어보세요 🍯
-
-
- 꿀조합 작성하러 가기
-
-
- );
- }
-
- return (
- <>
-
- {recipes.map((recipe) => (
-
-
-
-
-
- ))}
-
-
- >
- );
-};
-
-export default ProductRecipeList;
-
-const ProductRecipeListContainer = styled.ul`
- & > li + li {
- margin-top: 40px;
- }
-`;
-
-const ErrorContainer = styled.div`
- display: flex;
- flex-direction: column;
- align-items: center;
-`;
-
-const ErrorDescription = styled(Text)`
- padding: 20px 0;
- white-space: pre-wrap;
-`;
-
-const RecipeLink = styled(Link)`
- padding: 16px 24px;
- border: 1px solid ${({ theme }) => theme.colors.gray4};
- border-radius: 8px;
-`;
diff --git a/frontend/src/components/Product/ProductTitle/ProductTitle.stories.tsx b/frontend/src/components/Product/ProductTitle/ProductTitle.stories.tsx
deleted file mode 100644
index fbd2b1ed..00000000
--- a/frontend/src/components/Product/ProductTitle/ProductTitle.stories.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ProductTitle from './ProductTitle';
-
-const meta: Meta = {
- title: 'common/ProductTitle',
- component: ProductTitle,
- args: {
- content: '상품 목록',
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Product/ProductTitle/ProductTitle.tsx b/frontend/src/components/Product/ProductTitle/ProductTitle.tsx
deleted file mode 100644
index 1c10cf2f..00000000
--- a/frontend/src/components/Product/ProductTitle/ProductTitle.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Heading, Link, theme } from '@fun-eat/design-system';
-import { Link as RouterLink } from 'react-router-dom';
-import styled from 'styled-components';
-
-import SvgIcon from '../../Common/Svg/SvgIcon';
-
-import { PATH } from '@/constants/path';
-
-interface ProductTitleProps {
- content: string;
- routeDestination: string;
-}
-
-const ProductTitle = ({ content, routeDestination }: ProductTitleProps) => {
- return (
-
-
- {content}
-
-
-
-
-
-
- );
-};
-
-export default ProductTitle;
-
-const ProductTitleContainer = styled.div`
- position: relative;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- height: 30px;
-`;
-
-const ProductTitleLink = styled(Link)`
- display: flex;
- gap: 20px;
- align-items: center;
- margin-left: 36%;
-`;
-
-const HeadingTitle = styled(Heading)`
- font-size: 2.4rem;
-`;
-
-const DropDownIcon = styled(SvgIcon)`
- rotate: 270deg;
-`;
diff --git a/frontend/src/components/Product/index.ts b/frontend/src/components/Product/index.ts
deleted file mode 100644
index 25a06f28..00000000
--- a/frontend/src/components/Product/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export { default as ProductDetailItem } from './ProductDetailItem/ProductDetailItem';
-export { default as ProductItem } from './ProductItem/ProductItem';
-export { default as ProductList } from './ProductList/ProductList';
-export { default as ProductOverviewItem } from './ProductOverviewItem/ProductOverviewItem';
-export { default as ProductRecipeList } from './ProductRecipeList/ProductRecipeList';
-export { default as ProductTitle } from './ProductTitle/ProductTitle';
diff --git a/frontend/src/components/Rank/ProductRankingList/ProductRankingList.stories.tsx b/frontend/src/components/Rank/ProductRankingList/ProductRankingList.stories.tsx
deleted file mode 100644
index 68b955e5..00000000
--- a/frontend/src/components/Rank/ProductRankingList/ProductRankingList.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ProductRankingList from './ProductRankingList';
-
-const meta: Meta = {
- title: 'product/ProductRankingList',
- component: ProductRankingList,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Rank/ProductRankingList/ProductRankingList.tsx b/frontend/src/components/Rank/ProductRankingList/ProductRankingList.tsx
deleted file mode 100644
index 309f5219..00000000
--- a/frontend/src/components/Rank/ProductRankingList/ProductRankingList.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Link, Spacing } from '@fun-eat/design-system';
-import { Link as RouterLink } from 'react-router-dom';
-
-import { ProductOverviewItem } from '@/components/Product';
-import { PATH } from '@/constants/path';
-import { useGA } from '@/hooks/common';
-import { useProductRankingQuery } from '@/hooks/queries/rank';
-import displaySlice from '@/utils/displaySlice';
-
-interface ProductRankingListProps {
- isHomePage?: boolean;
-}
-
-const ProductRankingList = ({ isHomePage = false }: ProductRankingListProps) => {
- const { data: productRankings } = useProductRankingQuery();
- const { gaEvent } = useGA();
- const productsToDisplay = displaySlice(isHomePage, productRankings.products, 3);
-
- const handleProductRankingLinkClick = () => {
- gaEvent({ category: 'link', action: '상품 랭킹 링크 클릭', label: '랭킹' });
- };
-
- return (
-
- {productsToDisplay.map(({ id, name, image, categoryType }, index) => (
- -
-
-
-
-
-
- ))}
-
- );
-};
-
-export default ProductRankingList;
diff --git a/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.stories.tsx b/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.stories.tsx
deleted file mode 100644
index c20fa9fa..00000000
--- a/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.stories.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import RecipeRankingItem from './RecipeRankingItem';
-
-import mockRecipeRankingList from '@/mocks/data/recipeRankingList.json';
-
-const meta: Meta = {
- title: 'recipe/RecipeRankingItem',
- component: RecipeRankingItem,
- args: {
- rank: 1,
- recipe: mockRecipeRankingList.recipes[0],
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.tsx b/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.tsx
deleted file mode 100644
index b887ad10..00000000
--- a/frontend/src/components/Rank/RecipeRankingItem/RecipeRankingItem.tsx
+++ /dev/null
@@ -1,122 +0,0 @@
-import { Spacing, Text, useTheme } from '@fun-eat/design-system';
-import { useState } from 'react';
-import styled from 'styled-components';
-
-import RecipePreviewImage from '@/assets/plate.svg';
-import { Skeleton, SvgIcon } from '@/components/Common';
-import type { RecipeRanking } from '@/types/ranking';
-import { getRelativeDate } from '@/utils/date';
-
-interface RecipeRankingItemProps {
- rank: number;
- recipe: RecipeRanking;
-}
-
-const RecipeRankingItem = ({ rank, recipe }: RecipeRankingItemProps) => {
- const theme = useTheme();
- const {
- image,
- title,
- author: { nickname, profileImage },
- favoriteCount,
- createdAt,
- } = recipe;
- const [isImageLoading, setIsImageLoading] = useState(true);
-
- return (
-
-
-
-
-
- {image !== null ? (
- <>
- setIsImageLoading(false)}
- />
- {isImageLoading && }
- >
- ) : (
-
- )}
-
-
- {title}
-
-
-
- {favoriteCount}
-
-
-
- {getRelativeDate(createdAt)}
-
-
-
-
-
-
-
- {nickname} 님
-
-
-
-
- );
-};
-
-export default RecipeRankingItem;
-
-const RecipeRankingItemContainer = styled.div`
- width: calc(100% - 50px);
- max-width: 560px;
- margin: 12px 0;
- padding: 0 5px;
-`;
-
-const RecipeRankingWrapper = styled.div`
- display: flex;
- justify-content: space-between;
- width: 95%;
-`;
-
-const RankingRecipeWrapper = styled.div`
- display: flex;
- align-items: center;
-`;
-
-const RecipeImage = styled.img`
- border-radius: 5px;
- object-fit: cover;
-`;
-
-const TitleFavoriteWrapper = styled.div`
- display: flex;
- flex-direction: column;
- justify-content: space-around;
- height: 100%;
-`;
-
-const FavoriteWrapper = styled.div`
- display: flex;
- gap: 4px;
- align-items: center;
-`;
-
-const AuthorWrapper = styled.div`
- display: flex;
- flex-direction: column;
- justify-content: space-around;
- align-items: center;
- height: 100%;
-`;
-
-const AuthorImage = styled.img`
- border: 2px solid ${({ theme }) => theme.colors.primary};
- border-radius: 50%;
- object-fit: cover;
-`;
diff --git a/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.stories.tsx b/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.stories.tsx
deleted file mode 100644
index d13bad6f..00000000
--- a/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import RecipeRankingList from './RecipeRankingList';
-
-const meta: Meta = {
- title: 'recipe/RecipeRankingList',
- component: RecipeRankingList,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.tsx b/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.tsx
deleted file mode 100644
index 76397964..00000000
--- a/frontend/src/components/Rank/RecipeRankingList/RecipeRankingList.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Link, Text } from '@fun-eat/design-system';
-import { Link as RouterLink } from 'react-router-dom';
-
-import RecipeRankingItem from '../RecipeRankingItem/RecipeRankingItem';
-
-import { Carousel } from '@/components/Common';
-import { PATH } from '@/constants/path';
-import { useGA } from '@/hooks/common';
-import { useRecipeRankingQuery } from '@/hooks/queries/rank';
-
-const RecipeRankingList = () => {
- const { data: recipeResponse } = useRecipeRankingQuery();
- const { gaEvent } = useGA();
-
- if (recipeResponse.recipes.length === 0) return 아직 랭킹이 없어요!;
-
- const handleRecipeRankingLinkClick = () => {
- gaEvent({ category: 'link', action: '꿀조합 랭킹 링크 클릭', label: '랭킹' });
- };
-
- const carouselList = recipeResponse.recipes.map((recipe, index) => ({
- id: index,
- children: (
-
-
-
- ),
- }));
-
- return ;
-};
-
-export default RecipeRankingList;
diff --git a/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.stories.tsx b/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.stories.tsx
deleted file mode 100644
index 099f3473..00000000
--- a/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.stories.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ReviewRankingItem from './ReviewRankingItem';
-
-const meta: Meta = {
- title: 'review/ReviewRankingItem',
- component: ReviewRankingItem,
- args: {
- reviewRanking: {
- reviewId: 1,
- productId: 5,
- productName: '구운감자슬림명란마요',
- content:
- '할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데요 이것보다 긴 리뷰도 잘려 보인답니다',
- rating: 4.0,
- favoriteCount: 1256,
- categoryType: 'food',
- createdAt: '2021-08-01T00:00:00.000Z',
- },
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx b/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx
deleted file mode 100644
index 205cfb03..00000000
--- a/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import { Spacing, Text, useTheme } from '@fun-eat/design-system';
-import { memo } from 'react';
-import styled from 'styled-components';
-
-import { SvgIcon } from '@/components/Common';
-import type { ReviewRanking } from '@/types/ranking';
-import { getRelativeDate } from '@/utils/date';
-
-interface ReviewRankingItemProps {
- reviewRanking: ReviewRanking;
-}
-
-const ReviewRankingItem = ({ reviewRanking }: ReviewRankingItemProps) => {
- const theme = useTheme();
-
- const { productName, content, rating, favoriteCount, createdAt } = reviewRanking;
-
- return (
-
-
- {productName}
-
-
- {content}
-
-
-
-
-
-
- {favoriteCount}
-
-
-
-
-
- {rating.toFixed(1)}
-
-
-
- {getRelativeDate(createdAt)}
-
-
-
- );
-};
-
-export default memo(ReviewRankingItem);
-
-const ReviewRankingItemContainer = styled.div`
- display: flex;
- flex-direction: column;
- gap: 4px;
- padding: 12px;
- border: ${({ theme }) => `1px solid ${theme.borderColors.disabled}`};
- border-radius: ${({ theme }) => theme.borderRadius.sm};
-`;
-
-const ReviewText = styled(Text)`
- display: -webkit-inline-box;
- text-overflow: ellipsis;
- overflow: hidden;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
-`;
-
-const FavoriteStarWrapper = styled.div`
- display: flex;
- gap: 4px;
-`;
-
-const FavoriteIconWrapper = styled.div`
- display: flex;
- gap: 4px;
- align-items: center;
-`;
-
-const RatingIconWrapper = styled.div`
- display: flex;
- gap: 2px;
- align-items: center;
-
- & > svg {
- padding-bottom: 2px;
- }
-`;
-
-const ReviewDate = styled(Text)`
- margin-left: auto;
-`;
diff --git a/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.stories.tsx b/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.stories.tsx
deleted file mode 100644
index 671dd89e..00000000
--- a/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ReviewRankingList from './ReviewRankingList';
-
-const meta: Meta = {
- title: 'review/ReviewRankingList',
- component: ReviewRankingList,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.tsx b/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.tsx
deleted file mode 100644
index 99f10d5f..00000000
--- a/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { Link } from '@fun-eat/design-system';
-import { Link as RouterLink } from 'react-router-dom';
-import styled from 'styled-components';
-
-import ReviewRankingItem from '../ReviewRankingItem/ReviewRankingItem';
-
-import { PATH } from '@/constants/path';
-import { useGA } from '@/hooks/common';
-import { useReviewRankingQuery } from '@/hooks/queries/rank';
-import useDisplaySlice from '@/utils/displaySlice';
-
-interface ReviewRankingListProps {
- isHomePage?: boolean;
-}
-
-const ReviewRankingList = ({ isHomePage = false }: ReviewRankingListProps) => {
- const { data: reviewRankings } = useReviewRankingQuery();
- const { gaEvent } = useGA();
- const reviewsToDisplay = useDisplaySlice(isHomePage, reviewRankings.reviews);
-
- const handleReviewRankingLinkClick = () => {
- gaEvent({ category: 'link', action: '리뷰 랭킹 링크 클릭', label: '랭킹' });
- };
-
- return (
-
- {reviewsToDisplay.map((reviewRanking) => (
-
-
-
-
-
- ))}
-
- );
-};
-
-export default ReviewRankingList;
-
-const ReviewRankingListContainer = styled.ul`
- display: flex;
- flex-direction: column;
- gap: 20px;
-`;
diff --git a/frontend/src/components/Rank/index.ts b/frontend/src/components/Rank/index.ts
deleted file mode 100644
index 1b39a108..00000000
--- a/frontend/src/components/Rank/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export { default as ReviewRankingItem } from '../Rank/ReviewRankingItem/ReviewRankingItem';
-export { default as ReviewRankingList } from './ReviewRankingList/ReviewRankingList';
-export { default as ProductRankingList } from './ProductRankingList/ProductRankingList';
-export { default as RecipeRankingItem } from './RecipeRankingItem/RecipeRankingItem';
-export { default as RecipeRankingList } from './RecipeRankingList/RecipeRankingList';
diff --git a/frontend/src/components/Recipe/CommentForm/CommentForm.stories.tsx b/frontend/src/components/Recipe/CommentForm/CommentForm.stories.tsx
deleted file mode 100644
index e65b8722..00000000
--- a/frontend/src/components/Recipe/CommentForm/CommentForm.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import CommentForm from './CommentForm';
-
-const meta: Meta = {
- title: 'recipe/CommentForm',
- component: CommentForm,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Recipe/CommentForm/CommentForm.tsx b/frontend/src/components/Recipe/CommentForm/CommentForm.tsx
deleted file mode 100644
index 10465755..00000000
--- a/frontend/src/components/Recipe/CommentForm/CommentForm.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import { Button, Spacing, Text, Textarea, useTheme } from '@fun-eat/design-system';
-import type { ChangeEventHandler, FormEventHandler, RefObject } from 'react';
-import { useState } from 'react';
-import styled from 'styled-components';
-
-import { SvgIcon } from '@/components/Common';
-import { useScroll } from '@/hooks/common';
-import { useToastActionContext } from '@/hooks/context';
-import { useRecipeCommentMutation } from '@/hooks/queries/recipe';
-
-interface CommentFormProps {
- recipeId: number;
- scrollTargetRef: RefObject;
-}
-
-const MAX_COMMENT_LENGTH = 200;
-
-const CommentForm = ({ recipeId, scrollTargetRef }: CommentFormProps) => {
- const [commentValue, setCommentValue] = useState('');
- const { mutate } = useRecipeCommentMutation(recipeId);
-
- const theme = useTheme();
- const { toast } = useToastActionContext();
-
- const { scrollToPosition } = useScroll();
-
- const handleCommentInput: ChangeEventHandler = (e) => {
- setCommentValue(e.target.value);
- };
-
- const handleSubmitComment: FormEventHandler = (e) => {
- e.preventDefault();
-
- mutate(
- { comment: commentValue },
- {
- onSuccess: () => {
- setCommentValue('');
- scrollToPosition(scrollTargetRef);
- toast.success('댓글이 등록되었습니다.');
- },
- onError: (error) => {
- if (error instanceof Error) {
- toast.error(error.message);
- return;
- }
-
- toast.error('댓글을 등록하는데 오류가 발생했습니다.');
- },
- }
- );
- };
-
- return (
-
-
-
-
- {commentValue.length}자 / {MAX_COMMENT_LENGTH}자
-
-
- );
-};
-
-export default CommentForm;
-
-const CommentFormContainer = styled.div`
- position: fixed;
- bottom: 0;
- width: calc(100% - 40px);
- max-width: 540px;
- padding: 16px 0;
- background: ${({ theme }) => theme.backgroundColors.default};
-`;
-
-const Form = styled.form`
- display: flex;
- gap: 4px;
- justify-content: space-around;
- align-items: center;
-`;
-
-const CommentTextarea = styled(Textarea)`
- height: 50px;
- padding: 8px;
- font-size: 1.4rem;
-`;
-
-const SubmitButton = styled(Button)`
- cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
-`;
diff --git a/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx b/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx
deleted file mode 100644
index 70bf1f9a..00000000
--- a/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import CommentItem from './CommentItem';
-
-import comments from '@/mocks/data/comments.json';
-
-const meta: Meta = {
- title: 'recipe/CommentItem',
- component: CommentItem,
- args: {
- recipeComment: comments.comments[0],
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Recipe/CommentItem/CommentItem.tsx b/frontend/src/components/Recipe/CommentItem/CommentItem.tsx
deleted file mode 100644
index 847194b7..00000000
--- a/frontend/src/components/Recipe/CommentItem/CommentItem.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { Divider, Spacing, Text, useTheme } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import type { Comment } from '@/types/recipe';
-import { getFormattedDate } from '@/utils/date';
-
-interface CommentItemProps {
- recipeComment: Comment;
-}
-
-const CommentItem = ({ recipeComment }: CommentItemProps) => {
- const theme = useTheme();
- const { author, comment, createdAt } = recipeComment;
-
- return (
- <>
-
-
-
-
- {author.nickname} 님
-
-
- {getFormattedDate(createdAt)}
-
-
-
- {comment}
-
-
- >
- );
-};
-
-export default CommentItem;
-
-const AuthorWrapper = styled.div`
- display: flex;
- gap: 12px;
- align-items: center;
-`;
-
-const AuthorProfileImage = styled.img`
- border: 1px solid ${({ theme }) => theme.colors.primary};
- border-radius: 50%;
-`;
-
-const CommentContent = styled(Text)`
- margin: 16px 0;
-`;
diff --git a/frontend/src/components/Recipe/CommentList/CommentList.stories.tsx b/frontend/src/components/Recipe/CommentList/CommentList.stories.tsx
deleted file mode 100644
index ebad218d..00000000
--- a/frontend/src/components/Recipe/CommentList/CommentList.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import CommentList from './CommentList';
-
-const meta: Meta = {
- title: 'recipe/CommentList',
- component: CommentList,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Recipe/CommentList/CommentList.tsx b/frontend/src/components/Recipe/CommentList/CommentList.tsx
deleted file mode 100644
index d44f33c3..00000000
--- a/frontend/src/components/Recipe/CommentList/CommentList.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Heading, Spacing, Text, theme } from '@fun-eat/design-system';
-import { useRef } from 'react';
-
-import CommentItem from '../CommentItem/CommentItem';
-
-import { useIntersectionObserver } from '@/hooks/common';
-import { useInfiniteRecipeCommentQuery } from '@/hooks/queries/recipe';
-
-interface CommentListProps {
- recipeId: number;
-}
-
-const CommentList = ({ recipeId }: CommentListProps) => {
- const scrollRef = useRef(null);
-
- const { fetchNextPage, hasNextPage, data } = useInfiniteRecipeCommentQuery(Number(recipeId));
- useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage);
-
- const [{ totalElements }] = data.pages.flatMap((page) => page);
- const comments = data.pages.flatMap((page) => page.comments);
-
- return (
- <>
-
- 댓글 ({totalElements}개)
-
-
- {totalElements === 0 && 꿀조합의 첫번째 댓글을 달아보세요!}
- {comments.map((comment) => (
-
- ))}
-
- >
- );
-};
-
-export default CommentList;
diff --git a/frontend/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.stories.tsx b/frontend/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.stories.tsx
deleted file mode 100644
index eefc6267..00000000
--- a/frontend/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.stories.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { RecipeDetailTextarea } from '..';
-
-import RecipeFormProvider from '@/contexts/RecipeFormContext';
-
-const meta: Meta = {
- title: 'recipe/RecipeDetailTextarea',
- component: RecipeDetailTextarea,
- args: {
- recipeDetail: '',
- },
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.tsx b/frontend/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.tsx
deleted file mode 100644
index 79d5cdd4..00000000
--- a/frontend/src/components/Recipe/RecipeDetailTextarea/RecipeDetailTextarea.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { Heading, Spacing, Textarea, Text, useTheme } from '@fun-eat/design-system';
-import type { ChangeEventHandler } from 'react';
-import styled from 'styled-components';
-
-import { useRecipeFormActionContext } from '@/hooks/context';
-
-const MAX_LENGTH = 500;
-
-interface RecipeDetailTextareaProps {
- recipeDetail: string;
-}
-
-const RecipeDetailTextarea = ({ recipeDetail }: RecipeDetailTextareaProps) => {
- const theme = useTheme();
-
- const { handleRecipeFormValue } = useRecipeFormActionContext();
-
- const handleRecipeDetail: ChangeEventHandler = (e) => {
- handleRecipeFormValue({ target: 'content', value: e.currentTarget.value });
- };
-
- return (
- <>
-
- 자세한 설명을 남겨주세요.
- *
-
-
-
-
-
- 작성한 글자 수: {recipeDetail.length}자 / {MAX_LENGTH}자
-
- >
- );
-};
-
-export default RecipeDetailTextarea;
-
-const RequiredMark = styled.sup`
- color: ${({ theme }) => theme.colors.error};
-`;
diff --git a/frontend/src/components/Recipe/RecipeFavorite/RecipeFavorite.tsx b/frontend/src/components/Recipe/RecipeFavorite/RecipeFavorite.tsx
deleted file mode 100644
index 703e209c..00000000
--- a/frontend/src/components/Recipe/RecipeFavorite/RecipeFavorite.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import { theme, Button, Text } from '@fun-eat/design-system';
-import { useState } from 'react';
-import styled from 'styled-components';
-
-import { SvgIcon } from '@/components/Common';
-import { useTimeout } from '@/hooks/common';
-import { useToastActionContext } from '@/hooks/context';
-import { useRecipeFavoriteMutation } from '@/hooks/queries/recipe';
-
-interface RecipeFavoriteProps {
- favorite: boolean;
- favoriteCount: number;
- recipeId: number;
-}
-
-const RecipeFavorite = ({ recipeId, favorite, favoriteCount }: RecipeFavoriteProps) => {
- const [isFavorite, setIsFavorite] = useState(favorite);
- const [currentFavoriteCount, setCurrentFavoriteCount] = useState(favoriteCount);
- const { toast } = useToastActionContext();
-
- const { mutate } = useRecipeFavoriteMutation(Number(recipeId));
-
- const handleToggleFavorite = async () => {
- mutate(
- { favorite: !isFavorite },
- {
- onSuccess: () => {
- setIsFavorite((prev) => !prev);
- setCurrentFavoriteCount((prev) => (isFavorite ? prev - 1 : prev + 1));
- },
- onError: () => {
- toast.error('꿀조합 좋아요를 다시 시도해주세요.');
- },
- }
- );
- };
-
- const [debouncedToggleFavorite] = useTimeout(handleToggleFavorite, 200);
-
- return (
-
-
-
- {currentFavoriteCount}
-
-
- );
-};
-
-export default RecipeFavorite;
-
-const FavoriteButton = styled(Button)`
- display: flex;
- gap: 8px;
- align-items: center;
-`;
diff --git a/frontend/src/components/Recipe/RecipeItem/RecipeItem.stories.tsx b/frontend/src/components/Recipe/RecipeItem/RecipeItem.stories.tsx
deleted file mode 100644
index 351a3a0d..00000000
--- a/frontend/src/components/Recipe/RecipeItem/RecipeItem.stories.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import RecipeItem from './RecipeItem';
-
-import mockRecipe from '@/mocks/data/recipes.json';
-
-const meta: Meta = {
- title: 'recipe/RecipeItem',
- component: RecipeItem,
- args: {
- recipe: mockRecipe.recipes[0],
- },
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Recipe/RecipeItem/RecipeItem.tsx b/frontend/src/components/Recipe/RecipeItem/RecipeItem.tsx
deleted file mode 100644
index 5846bd87..00000000
--- a/frontend/src/components/Recipe/RecipeItem/RecipeItem.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import { Heading, Text, useTheme } from '@fun-eat/design-system';
-import { Fragment, memo, useState } from 'react';
-import styled from 'styled-components';
-
-import PreviewImage from '@/assets/plate.svg';
-import { Skeleton, SvgIcon } from '@/components/Common';
-import type { MemberRecipe, Recipe } from '@/types/recipe';
-import { getFormattedDate } from '@/utils/date';
-
-interface RecipeItemProps {
- recipe: Recipe | MemberRecipe;
- isMemberPage?: boolean;
-}
-
-const RecipeItem = ({ recipe, isMemberPage = false }: RecipeItemProps) => {
- const { image, title, createdAt, favoriteCount, products } = recipe;
- const author = 'author' in recipe ? recipe.author : null;
- const [isImageLoading, setIsImageLoading] = useState(true);
- const theme = useTheme();
-
- return (
- <>
- {!isMemberPage && (
-
- {image !== null ? (
- <>
- setIsImageLoading(false)} />
- {isImageLoading && }
- >
- ) : (
-
- )}
- {author && }
-
- )}
-
-
- {author && `${author.nickname} 님 | `}
- {getFormattedDate(createdAt)}
-
-
- {title}
-
-
- {products.map(({ id, name }) => (
-
- #{name}
-
- ))}
-
-
-
-
- {favoriteCount}
-
-
-
- >
- );
-};
-
-export default memo(RecipeItem);
-
-const ImageWrapper = styled.div`
- position: relative;
- display: flex;
- justify-content: center;
- width: 100%;
- height: 160px;
-`;
-
-const RecipeImage = styled.img`
- width: 100%;
- height: 100%;
- border-radius: 8px;
- object-fit: cover;
-`;
-
-const ProfileImage = styled.img`
- position: absolute;
- bottom: -20px;
- right: 16px;
- width: 60px;
- height: 60px;
- border: 2px solid ${({ theme }) => theme.colors.primary};
- border-radius: 50%;
- background-color: ${({ theme }) => theme.backgroundColors.default};
-`;
-
-const RecipeInfoWrapper = styled.div`
- position: relative;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- height: 100px;
- margin-top: 10px;
-`;
-
-const FavoriteWrapper = styled.div`
- position: absolute;
- top: 50%;
- bottom: 50%;
- right: 0;
- display: flex;
- gap: 4px;
- align-items: center;
- transform: translateY(-50%);
-`;
-
-const RecipeProductText = styled(Text)`
- white-space: nowrap;
- text-overflow: ellipsis;
- overflow: hidden;
-`;
diff --git a/frontend/src/components/Recipe/RecipeList/RecipeList.stories.tsx b/frontend/src/components/Recipe/RecipeList/RecipeList.stories.tsx
deleted file mode 100644
index bad0e66d..00000000
--- a/frontend/src/components/Recipe/RecipeList/RecipeList.stories.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import RecipeList from './RecipeList';
-
-const meta: Meta = {
- title: 'recipe/RecipeList',
- component: RecipeList,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Recipe/RecipeList/RecipeList.tsx b/frontend/src/components/Recipe/RecipeList/RecipeList.tsx
deleted file mode 100644
index ae1bb190..00000000
--- a/frontend/src/components/Recipe/RecipeList/RecipeList.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { Link, Text } from '@fun-eat/design-system';
-import { useRef } from 'react';
-import { Link as RouterLink } from 'react-router-dom';
-import styled from 'styled-components';
-
-import RecipeItem from '../RecipeItem/RecipeItem';
-
-import { useIntersectionObserver } from '@/hooks/common';
-import { useInfiniteRecipesQuery } from '@/hooks/queries/recipe';
-import type { SortOption } from '@/types/common';
-
-interface RecipeListProps {
- selectedOption: SortOption;
-}
-
-const RecipeList = ({ selectedOption }: RecipeListProps) => {
- const scrollRef = useRef(null);
- const { fetchNextPage, hasNextPage, data } = useInfiniteRecipesQuery(selectedOption.value);
- useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage);
-
- const recipes = data.pages.flatMap((page) => page.recipes);
-
- if (recipes.length === 0) {
- return 꿀조합을 작성해보세요;
- }
-
- return (
- <>
-
- {recipes.map((recipe) => (
-
-
-
-
-
- ))}
-
-
- >
- );
-};
-
-export default RecipeList;
-
-const RecipeListContainer = styled.ul`
- & > li + li {
- margin-top: 40px;
- }
-`;
diff --git a/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.stories.tsx b/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.stories.tsx
deleted file mode 100644
index cef7d75c..00000000
--- a/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.stories.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import RecipeNameInput from './RecipeNameInput';
-
-import RecipeFormProvider from '@/contexts/RecipeFormContext';
-
-const meta: Meta = {
- title: 'recipe/RecipeNameInput',
- component: RecipeNameInput,
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.tsx b/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.tsx
deleted file mode 100644
index 3ab0d8bc..00000000
--- a/frontend/src/components/Recipe/RecipeNameInput/RecipeNameInput.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { Heading, Spacing, Text, useTheme } from '@fun-eat/design-system';
-import type { ChangeEventHandler } from 'react';
-import styled from 'styled-components';
-
-import { Input } from '@/components/Common';
-import { useRecipeFormActionContext } from '@/hooks/context';
-
-const MIN_LENGTH = 1;
-const MAX_LENGTH = 15;
-interface RecipeNameInputProps {
- recipeName: string;
-}
-
-const RecipeNameInput = ({ recipeName }: RecipeNameInputProps) => {
- const { handleRecipeFormValue } = useRecipeFormActionContext();
- const theme = useTheme();
-
- const handleRecipeName: ChangeEventHandler = (e) => {
- handleRecipeFormValue({ target: 'title', value: e.currentTarget.value });
- };
-
- return (
-
-
- 꿀조합 이름
- *
-
-
- {recipeName.length}자 / {MAX_LENGTH}자
-
-
-
-
- );
-};
-
-export default RecipeNameInput;
-
-const RecipeNameInputContainer = styled.div`
- position: relative;
- width: 300px;
-`;
-
-const RequiredMark = styled.sup`
- color: ${({ theme }) => theme.colors.error};
-`;
-
-const RecipeNameStatusText = styled(Text)`
- position: absolute;
- top: 0;
- right: 0;
- line-height: 28px;
-`;
diff --git a/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.stories.tsx b/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.stories.tsx
deleted file mode 100644
index d8a4f8ef..00000000
--- a/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.stories.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import RecipeRegisterForm from './RecipeRegisterForm';
-
-import RecipeFormProvider from '@/contexts/RecipeFormContext';
-
-const meta: Meta = {
- title: 'recipe/RecipeRegisterForm',
- component: RecipeRegisterForm,
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.tsx b/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.tsx
deleted file mode 100644
index e92d894c..00000000
--- a/frontend/src/components/Recipe/RecipeRegisterForm/RecipeRegisterForm.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-import { Button, Divider, Heading, Spacing, Text, useTheme } from '@fun-eat/design-system';
-import type { FormEventHandler } from 'react';
-import styled from 'styled-components';
-
-import RecipeDetailTextarea from '../RecipeDetailTextarea/RecipeDetailTextarea';
-import RecipeNameInput from '../RecipeNameInput/RecipeNameInput';
-import RecipeUsedProducts from '../RecipeUsedProducts/RecipeUsedProducts';
-
-import { ImageUploader, SvgIcon } from '@/components/Common';
-import { useImageUploader, useFormData } from '@/hooks/common';
-import { useRecipeFormValueContext, useRecipeFormActionContext, useToastActionContext } from '@/hooks/context';
-import { useRecipeRegisterFormMutation } from '@/hooks/queries/recipe';
-import type { RecipeRequest } from '@/types/recipe';
-
-interface RecipeRegisterFormProps {
- closeRecipeDialog: () => void;
-}
-
-const RecipeRegisterForm = ({ closeRecipeDialog }: RecipeRegisterFormProps) => {
- const theme = useTheme();
-
- const { previewImage, imageFile, uploadImage, deleteImage } = useImageUploader();
-
- const recipeFormValue = useRecipeFormValueContext();
- const { resetRecipeFormValue } = useRecipeFormActionContext();
- const { toast } = useToastActionContext();
-
- const formData = useFormData({
- imageKey: 'images',
- imageFile: imageFile === null ? imageFile : [imageFile],
- formContentKey: 'recipeRequest',
- formContent: recipeFormValue,
- });
-
- const { mutate, isLoading } = useRecipeRegisterFormMutation();
-
- const isValid =
- recipeFormValue.title.length > 0 && recipeFormValue.content.length > 0 && recipeFormValue.productIds.length > 0;
-
- const resetAndCloseForm = () => {
- deleteImage();
- resetRecipeFormValue();
- closeRecipeDialog();
- };
-
- const handleRecipeFormSubmit: FormEventHandler = async (event) => {
- event.preventDefault();
-
- mutate(formData, {
- onSuccess: () => {
- resetAndCloseForm();
- toast.success('🍯 꿀조합이 등록 됐어요');
- },
- onError: (error) => {
- resetAndCloseForm();
- if (error instanceof Error) {
- toast.error(error.message);
- return;
- }
-
- toast.error('꿀조합 등록을 다시 시도해주세요');
- },
- });
- };
-
- return (
-
- 나만의 꿀조합 만들기🍯
-
-
-
-
-
-
-
- );
-};
-
-export default RecipeRegisterForm;
-
-const RecipeRegisterFormContainer = styled.div`
- position: relative;
- height: 100%;
- padding: 0 24px;
-`;
-
-const RecipeHeading = styled(Heading)`
- height: 80px;
- font-size: 2.4rem;
- line-height: 80px;
- text-align: center;
-`;
-
-const CloseButton = styled(Button)`
- position: absolute;
- top: 24px;
- right: 32px;
-`;
-
-const FormButton = styled(Button)`
- color: ${({ theme, disabled }) => (disabled ? theme.colors.white : theme.colors.black)};
- background: ${({ theme, disabled }) => (disabled ? theme.colors.gray3 : theme.colors.primary)};
- cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
-`;
diff --git a/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.stories.tsx b/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.stories.tsx
deleted file mode 100644
index efa66657..00000000
--- a/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.stories.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import RecipeUsedProducts from './RecipeUsedProducts';
-
-import RecipeFormProvider from '@/contexts/RecipeFormContext';
-
-const meta: Meta = {
- title: 'recipe/RecipeUsedProducts',
- component: RecipeUsedProducts,
- args: {
- usedProducts: [],
- },
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.tsx b/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.tsx
deleted file mode 100644
index 8ee6faec..00000000
--- a/frontend/src/components/Recipe/RecipeUsedProducts/RecipeUsedProducts.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-import { Badge, Button, Heading, Text, useTheme } from '@fun-eat/design-system';
-import { useQueryErrorResetBoundary } from '@tanstack/react-query';
-import { Suspense, useState } from 'react';
-import styled from 'styled-components';
-
-import SearchedProductList from './SearchedProductList';
-
-import { ErrorBoundary, ErrorComponent, Input, Loading, SvgIcon } from '@/components/Common';
-import { useDebounce } from '@/hooks/common';
-import { useRecipeFormActionContext } from '@/hooks/context';
-import { useSearch } from '@/hooks/search';
-import type { RecipeProduct } from '@/types/recipe';
-
-const MAX_USED_PRODUCTS_COUNT = 6;
-
-const RecipeUsedProducts = () => {
- const theme = useTheme();
- const { reset } = useQueryErrorResetBoundary();
-
- const { searchQuery, handleSearchQuery, isAutocompleteOpen, handleAutocompleteClose, resetSearchQuery } = useSearch();
- const [debouncedSearchQuery, setDebouncedSearchQuery] = useState(searchQuery || '');
- useDebounce(
- () => {
- setDebouncedSearchQuery(searchQuery);
- },
- 200,
- [searchQuery]
- );
-
- const [usedProducts, setUsedProducts] = useState([]);
- const { handleRecipeFormValue } = useRecipeFormActionContext();
-
- const removeUsedProducts = (id: number) => {
- setUsedProducts((prev) => prev.filter((usedProduct) => usedProduct.id !== id));
- handleRecipeFormValue({ target: 'productIds', value: id, action: 'remove' });
- };
-
- const addUsedProducts = (id: number, name: string) => {
- setUsedProducts((prev) => {
- if (prev.some((product) => product.id === id)) return prev;
- return [...prev, { id: id, name: name }];
- });
- handleRecipeFormValue({ target: 'productIds', value: id, action: 'add' });
-
- handleAutocompleteClose();
- resetSearchQuery();
- };
-
- return (
- <>
-
- 사용한 상품
- *
-
- {usedProducts.length ? (
-
- {usedProducts.map(({ id, name }) => (
-
-
- {name}
- removeUsedProducts(id)}>
-
-
-
-
- ))}
-
- ) : (
-
- 사용한 상품은 6개까지 업로드 할 수 있어요 😉
-
- )}
-
- }
- value={usedProducts.length === MAX_USED_PRODUCTS_COUNT ? '' : searchQuery}
- onChange={handleSearchQuery}
- disabled={usedProducts.length === MAX_USED_PRODUCTS_COUNT}
- />
- {usedProducts.length < MAX_USED_PRODUCTS_COUNT && debouncedSearchQuery && isAutocompleteOpen && (
-
- }>
-
-
-
- )}
-
- >
- );
-};
-
-export default RecipeUsedProducts;
-
-const RequiredMark = styled.sup`
- color: ${({ theme }) => theme.colors.error};
-`;
-
-const BadgeWrapper = styled.ul`
- display: flex;
- flex-direction: column;
- flex-wrap: wrap;
- align-content: flex-start;
- height: 48px;
- column-gap: 8px;
- overflow-x: auto;
-`;
-
-const ProductUploadLimitMessage = styled(Text)`
- display: flex;
- align-items: center;
- height: 48px;
-`;
-
-const SearchInputWrapper = styled.div`
- height: 100px;
-`;
-
-const RemoveButton = styled(Button)`
- margin-left: 4px;
-`;
diff --git a/frontend/src/components/Recipe/RecipeUsedProducts/SearchedProductList.tsx b/frontend/src/components/Recipe/RecipeUsedProducts/SearchedProductList.tsx
deleted file mode 100644
index d705d0a3..00000000
--- a/frontend/src/components/Recipe/RecipeUsedProducts/SearchedProductList.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import { Button, Text } from '@fun-eat/design-system';
-import type { MouseEventHandler } from 'react';
-import { useRef } from 'react';
-import styled from 'styled-components';
-
-import { MarkedText } from '@/components/Common';
-import { useIntersectionObserver } from '@/hooks/common';
-import { useInfiniteProductSearchAutocompleteQuery } from '@/hooks/queries/search';
-
-interface SearchedProductListProps {
- searchQuery: string;
- addUsedProducts: (id: number, name: string) => void;
- handleAutocompleteClose: MouseEventHandler;
-}
-
-const SearchedProductList = ({ searchQuery, addUsedProducts, handleAutocompleteClose }: SearchedProductListProps) => {
- const { data: searchResponse, fetchNextPage, hasNextPage } = useInfiniteProductSearchAutocompleteQuery(searchQuery);
- const scrollRef = useRef(null);
- useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage);
-
- if (!searchResponse) {
- return null;
- }
-
- const products = searchResponse.pages
- .flatMap((page) => page.products)
- .map((product) => ({
- id: product.id,
- name: product.name,
- }));
-
- if (products.length === 0) {
- return 검색어에 해당 하는 상품이 없습니다.;
- }
-
- return (
-
-
-
- {products.map(({ id, name }) => (
-
-
-
- ))}
-
-
-
- );
-};
-
-export default SearchedProductList;
-
-const Backdrop = styled.div`
- position: fixed;
- top: 0;
- left: 0;
- bottom: 0;
- right: 0;
- backround: rgba(0, 0, 0, 0.24);
-`;
-
-const SearchedProductListContainer = styled.div`
- max-height: 150px;
- background-color: ${({ theme }) => theme.backgroundColors.default};
- overflow-y: auto;
-`;
-
-const SearchedProductListWrapper = styled.ul`
- position: relative;
- width: 300px;
- height: 100%;
- max-height: 150px;
- border: 1px solid ${({ theme }) => theme.borderColors.default};
- border-top: none;
- border-radius: 0 0 5px 5px;
- background: ${({ theme }) => theme.backgroundColors.default};
- overflow: auto;
-
- &::-webkit-scrollbar: horizontal {
- display: none;
- }
-
- & > li {
- height: 36px;
- padding: 0 10px;
- line-height: 36px;
- }
-`;
-
-const ErrorText = styled(Text)`
- height: 36px;
- padding: 0 10px;
- line-height: 36px;
-`;
diff --git a/frontend/src/components/Recipe/index.ts b/frontend/src/components/Recipe/index.ts
deleted file mode 100644
index b7976120..00000000
--- a/frontend/src/components/Recipe/index.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export { default as RecipeNameInput } from './RecipeNameInput/RecipeNameInput';
-export { default as RecipeDetailTextarea } from './RecipeDetailTextarea/RecipeDetailTextarea';
-export { default as RecipeUsedProducts } from './RecipeUsedProducts/RecipeUsedProducts';
-export { default as RecipeItem } from './RecipeItem/RecipeItem';
-export { default as RecipeList } from './RecipeList/RecipeList';
-export { default as RecipeRegisterForm } from './RecipeRegisterForm/RecipeRegisterForm';
-export { default as RecipeFavorite } from './RecipeFavorite/RecipeFavorite';
-export { default as CommentItem } from './CommentItem/CommentItem';
-export { default as CommentForm } from './CommentForm/CommentForm';
-export { default as CommentList } from './CommentList/CommentList';
diff --git a/frontend/src/components/Review/BestReviewItem/BestReviewItem.stories.tsx b/frontend/src/components/Review/BestReviewItem/BestReviewItem.stories.tsx
deleted file mode 100644
index bc8c75e7..00000000
--- a/frontend/src/components/Review/BestReviewItem/BestReviewItem.stories.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import BestReviewItem from './BestReviewItem';
-
-const meta: Meta = {
- title: 'review/BestReviewItem',
- component: BestReviewItem,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- args: {
- productId: 2,
- },
-};
diff --git a/frontend/src/components/Review/BestReviewItem/BestReviewItem.tsx b/frontend/src/components/Review/BestReviewItem/BestReviewItem.tsx
deleted file mode 100644
index 5e66dcbb..00000000
--- a/frontend/src/components/Review/BestReviewItem/BestReviewItem.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import { Spacing, Text, useTheme } from '@fun-eat/design-system';
-import styled from 'styled-components';
-
-import { SvgIcon } from '@/components/Common';
-import { useBestReviewQuery } from '@/hooks/queries/rank';
-
-interface BestReviewItemProps {
- productId: number;
-}
-
-const BestReviewItem = ({ productId }: BestReviewItemProps) => {
- const { data: bestReview } = useBestReviewQuery(productId);
-
- if (!bestReview) {
- return null;
- }
-
- const { profileImage, userName, rating, favoriteCount, content } = bestReview;
-
- const theme = useTheme();
-
- return (
- <>
-
- ⭐️ 베스트 리뷰 ⭐️
-
-
- {Object.keys(bestReview).length !== 0 && (
-
-
-
-
-
-
- {userName} 님
-
- {Array.from({ length: 5 }, (_, index) => (
-
- ))}
-
-
-
-
-
- {favoriteCount}
-
-
-
-
-
- {content}
-
-
- )}
- >
- );
-};
-
-export default BestReviewItem;
-
-const BestReviewItemContainer = styled.div`
- padding: 10px;
- border: 1px solid ${({ theme }) => theme.borderColors.disabled};
- border-radius: 5px;
-`;
-
-const ReviewRateFavoriteWrapper = styled.div`
- display: flex;
- justify-content: space-between;
- align-items: flex-end;
-`;
-
-const ReviewerInfoWrapper = styled.div`
- display: flex;
- align-items: center;
- column-gap: 10px;
-`;
-
-const ReviewerImage = styled.img`
- border: 2px solid ${({ theme }) => theme.colors.primary};
- border-radius: 50%;
- object-fit: cover;
-`;
-
-const FavoriteWrapper = styled.div`
- display: flex;
- gap: 8px;
- align-items: center;
-`;
-
-const ReviewText = styled(Text)`
- display: -webkit-inline-box;
- text-overflow: ellipsis;
- overflow: hidden;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
-`;
diff --git a/frontend/src/components/Review/RebuyCheckbox/RebuyCheckbox.tsx b/frontend/src/components/Review/RebuyCheckbox/RebuyCheckbox.tsx
deleted file mode 100644
index 9f1a6e95..00000000
--- a/frontend/src/components/Review/RebuyCheckbox/RebuyCheckbox.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Checkbox } from '@fun-eat/design-system';
-import type { ChangeEventHandler } from 'react';
-
-import { useEnterKeyDown } from '@/hooks/common';
-import { useReviewFormActionContext } from '@/hooks/context';
-
-const RebuyCheckbox = () => {
- const { handleReviewFormValue } = useReviewFormActionContext();
- const { inputRef, labelRef, handleKeydown } = useEnterKeyDown();
-
- const handleRebuy: ChangeEventHandler = (event) => {
- handleReviewFormValue({ target: 'rebuy', value: event.target.checked });
- };
-
- return (
-
-
- 재구매할 생각이 있으신가요?
-
-
- );
-};
-
-export default RebuyCheckbox;
diff --git a/frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx b/frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx
deleted file mode 100644
index 4611ed65..00000000
--- a/frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ReviewItem from './ReviewItem';
-
-import mockReviews from '@/mocks/data/reviews.json';
-
-const meta: Meta = {
- title: 'review/ReviewItem',
- component: ReviewItem,
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {
- render: () => (
-
-
-
- ),
-};
-
-export const RebuyAndFavorite: Story = {
- render: () => (
-
-
-
- ),
-};
-
-export const NoImageReview: Story = {
- render: () => (
-
-
-
- ),
-};
diff --git a/frontend/src/components/Review/ReviewItem/ReviewItem.tsx b/frontend/src/components/Review/ReviewItem/ReviewItem.tsx
deleted file mode 100644
index 14cd83d1..00000000
--- a/frontend/src/components/Review/ReviewItem/ReviewItem.tsx
+++ /dev/null
@@ -1,150 +0,0 @@
-import { Badge, Button, Text, useTheme } from '@fun-eat/design-system';
-import { memo, useState } from 'react';
-import styled from 'styled-components';
-
-import { SvgIcon, TagList } from '@/components/Common';
-import { useTimeout } from '@/hooks/common';
-import { useToastActionContext } from '@/hooks/context';
-import { useReviewFavoriteMutation } from '@/hooks/queries/review';
-import type { Review } from '@/types/review';
-import { getRelativeDate } from '@/utils/date';
-
-interface ReviewItemProps {
- productId: number;
- review: Review;
-}
-
-const ReviewItem = ({ productId, review }: ReviewItemProps) => {
- const { id, userName, profileImage, image, rating, tags, content, createdAt, rebuy, favoriteCount, favorite } =
- review;
- const [isFavorite, setIsFavorite] = useState(favorite);
- const [currentFavoriteCount, setCurrentFavoriteCount] = useState(favoriteCount);
-
- const { toast } = useToastActionContext();
- const { mutate } = useReviewFavoriteMutation(productId, id);
-
- const theme = useTheme();
-
- const handleToggleFavorite = async () => {
- mutate(
- { favorite: !isFavorite },
- {
- onSuccess: () => {
- setIsFavorite((prev) => !prev);
- setCurrentFavoriteCount((prev) => (isFavorite ? prev - 1 : prev + 1));
- },
- onError: (error) => {
- if (error instanceof Error) {
- toast.error(error.message);
- return;
- }
-
- toast.error('리뷰 좋아요를 다시 시도해주세요.');
- },
- }
- );
- };
-
- const [debouncedToggleFavorite] = useTimeout(handleToggleFavorite, 200);
-
- return (
-
-
-
-
-
- {userName}
-
- {Array.from({ length: 5 }, (_, index) => (
-
- ))}
-
- {getRelativeDate(createdAt)}
-
-
-
-
- {rebuy && (
-
- 😝 또 살래요
-
- )}
-
- {image && }
-
- {content}
-
-
-
- {currentFavoriteCount}
-
-
-
- );
-};
-
-export default memo(ReviewItem);
-
-const ReviewItemContainer = styled.div`
- display: flex;
- flex-direction: column;
- row-gap: 20px;
-`;
-
-const ReviewerWrapper = styled.div`
- display: flex;
- justify-content: space-between;
- align-items: center;
-`;
-
-const ReviewerInfoWrapper = styled.div`
- display: flex;
- align-items: center;
- column-gap: 10px;
-`;
-
-const RebuyBadge = styled(Badge)`
- font-weight: ${({ theme }) => theme.fontWeights.bold};
-`;
-
-const ReviewerImage = styled.img`
- border: 2px solid ${({ theme }) => theme.colors.primary};
- border-radius: 50%;
- object-fit: cover;
-`;
-
-const RatingIconWrapper = styled.div`
- display: flex;
- align-items: center;
- margin-left: -2px;
-
- & > span {
- margin-left: 12px;
- }
-`;
-
-const ReviewImage = styled.img`
- align-self: center;
-`;
-
-const ReviewContent = styled(Text)`
- white-space: pre-wrap;
-`;
-
-const FavoriteButton = styled(Button)`
- display: flex;
- align-items: center;
- padding: 0;
- column-gap: 8px;
-`;
diff --git a/frontend/src/components/Review/ReviewList/ReviewList.tsx b/frontend/src/components/Review/ReviewList/ReviewList.tsx
deleted file mode 100644
index bb2b993b..00000000
--- a/frontend/src/components/Review/ReviewList/ReviewList.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Text } from '@fun-eat/design-system';
-import { useRef } from 'react';
-import styled from 'styled-components';
-
-import ReviewItem from '../ReviewItem/ReviewItem';
-
-import { Loading } from '@/components/Common';
-import { useIntersectionObserver } from '@/hooks/common';
-import { useInfiniteProductReviewsQuery } from '@/hooks/queries/product';
-import type { SortOption } from '@/types/common';
-
-interface ReviewListProps {
- productId: number;
- selectedOption: SortOption;
-}
-
-const ReviewList = ({ productId, selectedOption }: ReviewListProps) => {
- const { fetchNextPage, hasNextPage, data, isFetchingNextPage } = useInfiniteProductReviewsQuery(
- productId,
- selectedOption.value
- );
- const scrollRef = useRef(null);
- useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage);
-
- const reviews = data.pages.flatMap((page) => page.reviews);
-
- if (reviews.length === 0) {
- return 상품의 첫 리뷰를 작성해주세요;
- }
-
- return (
- <>
-
- {reviews.map((review) => (
-
-
-
- ))}
-
-
- {isFetchingNextPage && }
- >
- );
-};
-
-export default ReviewList;
-
-const ReviewListContainer = styled.ul`
- display: flex;
- flex-direction: column;
- row-gap: 60px;
-`;
diff --git a/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.stories.tsx b/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.stories.tsx
deleted file mode 100644
index 39ba69c6..00000000
--- a/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.stories.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ReviewRegisterForm from './ReviewRegisterForm';
-
-import ReviewFormProvider from '@/contexts/ReviewFormContext';
-import productDetail from '@/mocks/data/productDetail.json';
-
-const meta: Meta = {
- title: 'review/ReviewRegisterForm',
- component: ReviewRegisterForm,
- args: {
- productId: productDetail.id,
- },
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
diff --git a/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx b/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx
deleted file mode 100644
index 6c422c68..00000000
--- a/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx
+++ /dev/null
@@ -1,172 +0,0 @@
-import { Button, Divider, Heading, Spacing, Text, theme } from '@fun-eat/design-system';
-import type { FormEventHandler, RefObject } from 'react';
-import styled from 'styled-components';
-
-import RebuyCheckbox from '../RebuyCheckbox/RebuyCheckbox';
-import ReviewTagList from '../ReviewTagList/ReviewTagList';
-import ReviewTextarea from '../ReviewTextarea/ReviewTextarea';
-import StarRate from '../StarRate/StarRate';
-
-import { ImageUploader, SvgIcon } from '@/components/Common';
-import { ProductOverviewItem } from '@/components/Product';
-import { MIN_DISPLAYED_TAGS_LENGTH } from '@/constants';
-import { useFormData, useImageUploader, useScroll } from '@/hooks/common';
-import { useReviewFormActionContext, useReviewFormValueContext, useToastActionContext } from '@/hooks/context';
-import { useProductDetailQuery } from '@/hooks/queries/product';
-import { useReviewRegisterFormMutation } from '@/hooks/queries/review';
-import type { ReviewRequest } from '@/types/review';
-
-const MIN_RATING_SCORE = 0;
-const MIN_SELECTED_TAGS_COUNT = 1;
-const MIN_CONTENT_LENGTH = 0;
-
-interface ReviewRegisterFormProps {
- productId: number;
- targetRef: RefObject;
- closeReviewDialog: () => void;
- initTabMenu: () => void;
-}
-
-const ReviewRegisterForm = ({ productId, targetRef, closeReviewDialog, initTabMenu }: ReviewRegisterFormProps) => {
- const { scrollToPosition } = useScroll();
- const { isImageUploading, previewImage, imageFile, uploadImage, deleteImage } = useImageUploader();
-
- const reviewFormValue = useReviewFormValueContext();
- const { resetReviewFormValue } = useReviewFormActionContext();
- const { toast } = useToastActionContext();
-
- const { data: productDetail } = useProductDetailQuery(productId);
- const { mutate, isLoading } = useReviewRegisterFormMutation(productId);
-
- const isValid =
- reviewFormValue.rating > MIN_RATING_SCORE &&
- reviewFormValue.tagIds.length >= MIN_SELECTED_TAGS_COUNT &&
- reviewFormValue.tagIds.length <= MIN_DISPLAYED_TAGS_LENGTH &&
- reviewFormValue.content.length > MIN_CONTENT_LENGTH &&
- !isImageUploading;
-
- const formData = useFormData({
- imageKey: 'image',
- imageFile: imageFile,
- formContentKey: 'reviewRequest',
- formContent: reviewFormValue,
- });
-
- const resetAndCloseForm = () => {
- deleteImage();
- resetReviewFormValue();
- closeReviewDialog();
- };
-
- const handleSubmit: FormEventHandler = async (event) => {
- event.preventDefault();
-
- mutate(formData, {
- onSuccess: () => {
- resetAndCloseForm();
- initTabMenu();
- scrollToPosition(targetRef);
- toast.success('📝 리뷰가 등록 됐어요');
- },
- onError: (error) => {
- resetAndCloseForm();
- if (error instanceof Error) {
- toast.error(error.message);
- return;
- }
-
- toast.error('리뷰 등록을 다시 시도해주세요');
- },
- });
- };
-
- return (
-
- 리뷰 작성
-
-
-
-
-
-
-
-
-
-
-
- 구매한 상품 사진이 있다면 올려주세요.
-
-
-
- (사진은 5MB 이하, 1장까지 업로드 할 수 있어요.)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [작성시 유의사항] 신뢰성 확보에 저해되는 게시물은 삭제하거나 보이지 않게 할 수 있습니다.
-
-
-
- {isValid ? '리뷰 등록하기' : '꼭 입력해야 하는 항목이 있어요'}
-
-
-
- );
-};
-
-export default ReviewRegisterForm;
-
-const ReviewRegisterFormContainer = styled.div`
- position: relative;
- height: 100%;
-`;
-
-const ReviewHeading = styled(Heading)`
- height: 80px;
- font-size: 2.4rem;
- line-height: 80px;
- text-align: center;
-`;
-
-const CloseButton = styled(Button)`
- position: absolute;
- top: 24px;
- right: 32px;
-`;
-
-const ProductOverviewItemWrapper = styled.div`
- margin: 15px 0;
-`;
-
-const RegisterForm = styled.form`
- padding: 50px 20px;
-`;
-
-const ReviewImageUploaderContainer = styled.div`
- display: flex;
- flex-direction: column;
- align-items: center;
-`;
-
-const FormButton = styled(Button)`
- color: ${({ theme, disabled }) => (disabled ? theme.colors.white : theme.colors.black)};
- background: ${({ theme, disabled }) => (disabled ? theme.colors.gray3 : theme.colors.primary)};
- cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
-`;
diff --git a/frontend/src/components/Review/ReviewTagItem/ReviewTagItem.stories.tsx b/frontend/src/components/Review/ReviewTagItem/ReviewTagItem.stories.tsx
deleted file mode 100644
index 572ffe07..00000000
--- a/frontend/src/components/Review/ReviewTagItem/ReviewTagItem.stories.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import ReviewTagItem from './ReviewTagItem';
-
-import ReviewFormProvider from '@/contexts/ReviewFormContext';
-
-const meta: Meta = {
- title: 'review/ReviewTagItem',
- component: ReviewTagItem,
- args: {
- id: 0,
- name: '단짠단짠',
- isSelected: false,
- },
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Default: Story = {};
-
-export const Selected: Story = {
- args: {
- isSelected: true,
- },
-};
diff --git a/frontend/src/components/Review/ReviewTagItem/ReviewTagItem.tsx b/frontend/src/components/Review/ReviewTagItem/ReviewTagItem.tsx
deleted file mode 100644
index 7ca589fc..00000000
--- a/frontend/src/components/Review/ReviewTagItem/ReviewTagItem.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Badge, Button, useTheme } from '@fun-eat/design-system';
-import type { RuleSet } from 'styled-components';
-import styled, { css } from 'styled-components';
-
-import { useReviewFormActionContext } from '@/hooks/context';
-import type { TagVariants } from '@/types/common';
-
-interface ReviewTagItemProps {
- id: number;
- name: string;
- variant: TagVariants;
- isSelected: boolean;
-}
-
-const ReviewTagItem = ({ id, name, variant, isSelected }: ReviewTagItemProps) => {
- const { handleReviewFormValue } = useReviewFormActionContext();
- const theme = useTheme();
-
- const handleReviewTag = () => {
- handleReviewFormValue({ target: 'tagIds', value: id, isSelected });
- };
-
- return (
-
- );
-};
-
-export default ReviewTagItem;
-
-type TagStyleProps = Pick;
-
-type TagVariantStyles = Record RuleSet