Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fe6716b
feat: 초기세팅
ye6194 Oct 7, 2024
e58cc30
feat: 초기세팅
ye6194 Oct 7, 2024
57849c4
feat: 컴포넌트 그리기
ye6194 Oct 7, 2024
16b7b7f
feat: 스타일 적용
ye6194 Oct 7, 2024
efed817
Merge branch 'step1' of https://github.com/ye6194/self-paced-react in…
ye6194 Oct 8, 2024
c3c5fc7
feat: 카테고리 필터에 따라 필터된 음식점 목록을 보여줌
ye6194 Oct 8, 2024
6c6c45b
feat: 필터된 음식점 목록을 보여줄 때 카테고리에 맞는 이미지 출력
ye6194 Oct 8, 2024
972cf6e
chore: 불필요한 주석 제거
ye6194 Oct 8, 2024
8e5e16b
refactor: data를 다른 파일로 분리
ye6194 Oct 10, 2024
8794dae
refactor: 코드리뷰 반영
ye6194 Oct 14, 2024
80dce59
chore: 불필요한 설정 제거
ye6194 Oct 14, 2024
e02a97a
refactor: useEffect 삭제
ye6194 Oct 14, 2024
29016c0
fix: CRA에서 Vite로 제대로 바꿔지지 않았던 문제 해결
ye6194 Oct 20, 2024
f41ced4
refactor: 코드 리뷰 반영
ye6194 Oct 21, 2024
19d8f6e
push
ye6194 Nov 1, 2024
b73b1f5
Merge remote-tracking branch 'upstream/ye6194' into step2
ye6194 Nov 1, 2024
d364767
commit
ye6194 Nov 4, 2024
71bffb8
feat: 클릭한 레스토랑의 정보를 모달에 표시
ye6194 Nov 4, 2024
cc49523
feat: backdrop을 클릭하면 모달이 닫힘
ye6194 Nov 4, 2024
c6721b7
feat: 음식점 추가 모달 열고 닫는 기능
ye6194 Nov 4, 2024
7124439
feat: 모달의 추가 버튼을 누르면 레스토랑 목록에 추가
ye6194 Nov 4, 2024
c7e8c08
refactor: 코드리뷰 내용 반영 및 수정
ye6194 Nov 6, 2024
f25a0fd
refactor: 코드리뷰 내용 반영
ye6194 Nov 9, 2024
9e06b51
Merge branch 'ye6194' into step4
ye6194 Nov 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ module.exports = {
"react/jsx-no-target-blank": "off",
"react/prop-types": "off",
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
"no-unused-vars": "warn",
},
};
File renamed without changes.
48 changes: 38 additions & 10 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,49 @@
import "./App.css";
import Header from "./components/Header";
import CategoryFilter from "./components/CategoryFilter";
import RestaurantList from "./components/RestaurantList";
import AddRestaurantModal from "./components/AddRestaurantModal";
import RestaurantDetailModal from "./components/RestaurantDetailModal";
import { useState } from "react";
import Header from "./components/Header.jsx";
import CategoryFilter from "./components/CategoryFilter.jsx";
import RestaurantList from "./components/RestaurantList.jsx";
import AddRestaurantModal from "./components/AddRestaurantModal.jsx";
import RestaurantDetailModal from "./components/RestaurantDetailModal.jsx";
import restaurantsData from "./data/restaurantsData.js";

function App() {
const [category, setCategory] = useState("전체");

const filterRestaurants = (category) => {
if (category === "전체") return restaurantsData;
else return restaurantsData.filter((restaurant) => restaurant.category === category);
};

const filteredRestaurants = filterRestaurants(category);

const [isModalOpen, setIsModalOpen] = useState(false);
const [restaurantName, setRestaurantName] = useState("");
const [restaurantInfo, setRestaurantInfo] = useState("");

const [isAddModalOpen, setIsAddModalOpen] = useState(false);

return (
<>
<Header />
<Header setIsAddModalOpen={setIsAddModalOpen} />
<main>
<CategoryFilter />
<RestaurantList />
<CategoryFilter category={category} onChangeCategory={setCategory} />
<RestaurantList
restaurants={filteredRestaurants}
SetIsModalOpen={setIsModalOpen}
setRestaurantName={setRestaurantName}
setRestaurantInfo={setRestaurantInfo}
/>
</main>
<aside>
<RestaurantDetailModal />
<AddRestaurantModal />
{isModalOpen && (
<RestaurantDetailModal
SetIsModalOpen={setIsModalOpen}
restaurantName={restaurantName}
restaurantInfo={restaurantInfo}
/>
)}
{isAddModalOpen && <AddRestaurantModal setIsAddModalOpen={setIsAddModalOpen} />}
</aside>
</>
);
Expand Down
112 changes: 71 additions & 41 deletions src/components/AddRestaurantModal.jsx

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인터뷰 🎤

제어 컴포넌트(controlled component)와 비제어 컴포넌트(uncontrolled component)란 무엇인가요? 각 방법에서 부각되는 특징은 무엇이라고 생각하시나요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 제어 컴포넌트(controlled component)
    입력값이 상태에 의해 제어되는 컴포넌트입니다. input, textarea 등 입력 필드의 값이 바뀔 때마다 업데이트됩니다.

  • 비제어 컴포넌트(uncontrolled component)
    입력값이 상태에 의해 제어되지 않고 입력 필드가 값을 관리하는 컴포넌트 입니다. useRef를 사용해 필요할 때 입력값을 가져옵니다.

각 방법에서 부각되는 특징은 상태가 연결됐는지(컴포넌트와 입력값의 동기화) 여부라고 생각합니다!

Original file line number Diff line number Diff line change
@@ -1,48 +1,78 @@
import "../styles/AddRestaurantModalStyle.css";
import { CATEGORY_DATA } from "../data/categoryData";
import restaurantsData from "../data/restaurantsData";
import { useState } from "react";

function AddRestaurantModal({ setIsAddModalOpen }) {
const [selectedCategory, setSelectedCategory] = useState("");

const handleAddBtnClick = () => {
event.preventDefault();

restaurantsData.push({
id: Date.now(),
name: restaurantName,
description: restaurantInfo,
category: selectedCategory,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 import 한 객체를 직접 업데이트할 경우, 아래의 문제들이 발생할 수 있어요.

  1. React가 이 값이 업데이트되었음을 인지하지 못해 리렌더링 등의 적절한 조치를 취하지 못할 수 있어요. 그래서 restaurantData 의 값이 바뀌더라도 이를 사용하는 컴포넌트들이 새로운 데이터로 업데이트되지 않을 수도 있습니다. 이로 인해 UI가 최신의 상태를 보여준다는 보장을 하기 어렵고, 신뢰성이 떨어지는 컴포넌트가 될 수도 있어요.
  2. React 개발자 도구를 사용하는 경우에도 변경 내용 및 버그 발생 시 이를 추적하지 못할 수도 있어요.
  3. restaurantsData는 import만 해 준다면 어디에서든 사용할 수 있기 때문에, 아무 컴포넌트나 조작하여 변경할 수 있는 데이터가 되며, 이로 인해 은닉성을 잃게 될 수도 있습니다. 이로 인해 예측이 어려워지고 다른 곳에서의 예상치 못한 값 변경으로 버그가 생길 수도 있어보여요.

따라서 이를 이유로 피해주셔야 할 방법이라고 생각합니다.

이렇게 레스토랑의 정보를 변경해야 하는 상황인데, AddRestaurantModal 컴포넌트가 레스토랑 관련 데이터를 지니지 않아 로직 구현이 어려운 상황이라면, AddRestaurantModal은 부모에게 레스토랑 데이터가 업데이트 되야함을 알리기만 하고 이 컴포넌트를 부모로 둔 부모 컴포넌트가 이 업데이트 로직을 대신 수행하도록 구현해 보실 것을 추천드릅니다.

<App> 에서도 이 레스토랑 데이터는 가능한 한 상태로 관리하도록 개선을 부탁드리고 싶습니다. 상태에 직접 값을 할당하는 것이 아니라 굳이 setter 함수를 사용하는 이유와 비슷한 케이스이니 고민해 보시면 좋을 것 같아요 😃

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

물론 제가 이렇게 말씀드려도 당장은 작동하는 것처럼 보이기 때문에 "왜 이런 지적을...? ❓" 이렇게 느끼실 수 있다고 생각해요. 그래서 한 번 문제가 생길 수 있는 상황을 재현해보았어요.

결론부터 말씀드리자면 이렇게 해도 동작할 수 있었던 것은 setIsAddModalOpen() setter 함수가 실행되어 리렌더링을 트리거했기 때문이에요. 그래서 setIsAddModalOpen() 를 제거해 보려고 해요.

우선, handleAddBtnClick() 을 아래와 같이 변경해볼게요.

const handleAddBtnClick = () => {
  event.preventDefault();

  restaurantsData.push({
    id: Date.now(),
    name: restaurantName,
    description: restaurantInfo,
    category: selectedCategory,
  });

  alert("handleAddBtnClick() 실행!"); // 추가
  // setIsAddModalOpen(false); <-- 테스트를 위해 제거
};

setIsAddModalOpen(false)를 실행시키지 않도록 해 준 뒤, 함수가 실행되었음을 확인하기 위해 alert를 추가한 것입니다.

그 다음 평소대로 레스토랑 추가 모달을 열어 추가하면, 의도했던 것과 다르게 레스토랑이 추가되지 않는 모습을 확인하실 수 있을 거에요. (모달이 닫히는 로직을 제거했으므로 직접 HTML의 요소를 개발자 도구를 통해 제거하는 방법을 사용했습니다)

_2024_11_08_11_45_21_921.mp4

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import한 객체를 직접 업데이트하면 이런 문제가 생길 수 있군요..!!
덕분에 알아갑니다. App에서 레스토랑 데이터를 상태로 관리하고 업데이트 하도록 수정할게요!

setIsAddModalOpen(false);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

데이터를 모달 창 내에서 추가한다는 생각은 못했는데 이런 방법도 존재했었네요!
저의 경우 레스토랑 데이터를 맨 처음 불러온 곳이 App.jsx 가장 상위 컴포넌트라 데이터의 저장도 이곳에서 이루어지는게 좋다고 생각했는데,저장을 위해 데이터가 역방향으로 흐르는게 좋은 방식인지 고민을 안해봤던거 같네요... (고민거리 스택+1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gogo1414 이 질문에 답변드리자면, 하위 컴포넌트에서 상위 컴포넌트의 함수를 실행해 데이터를 업데이트하는 방법은 역방향 흐름인 것 처럼 작동하지만, 단방향 흐름의 일환으로 해석할 수 있으며, React의 단방향 흐름 원칙을 위반하지는 않는 사례입니다. 오히려 React에서는 이 방법을 권장하고 있으며, 여러 컴포넌트가 데이터를 공유해야 할 경우 부모 컴포넌트에 state를 두어 관리하는 방법 또한 설명하고 있습니다.

역방향처럼 보이지만 단방향 흐름이라는 것을 이해하기가 어렵거나 헷갈리신다면, 데이터가 흐르는 방향 에 주목하시면 좋을 것 같아요. 자식 컴포넌트가 부모 컴포넌트에게 값의 업데이트를 요청하기 위해 부모 컴포넌트의 함수를 실행하더라도, 데이터를 업데이트 하는 로직 자체는 부모 컴포넌트가 수행하며, 데이터는 여전히 부모 컴포넌트에서 자식 컴포넌트로 내려가는 형식입니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

쉽게 설명해주셔서 바로 이해했습니다!

감사합니다😊


const [restaurantName, setRestaurantName] = useState("");
const [restaurantInfo, setRestaurantInfo] = useState("");

function AddRestaurantModal() {
return (
<>
<div className="modal modal--open">
<div className="modal-backdrop"></div>
<div className="modal-container">
<h2 className="modal-title text-title">새로운 음식점</h2>
<form>
{/* 카테고리 */}
<div className="form-item form-item--required">
<label htmlFor="category text-caption">카테고리</label>
<select name="category" id="category" required>
<option value="">선택해 주세요</option>
<option value="한식">한식</option>
<option value="중식">중식</option>
<option value="일식">일식</option>
<option value="양식">양식</option>
<option value="아시안">아시안</option>
<option value="기타">기타</option>
</select>
</div>

{/* 음식점 이름 */}
<div className="form-item form-item--required">
<label htmlFor="name text-caption">이름</label>
<input type="text" name="name" id="name" required />
</div>

{/* 설명 */}
<div className="form-item">
<label htmlFor="description text-caption">설명</label>
<textarea name="description" id="description" cols="30" rows="5"></textarea>
<span className="help-text text-caption">메뉴 등 추가 정보를 입력해 주세요.</span>
</div>

{/* 추가 버튼 */}
<div className="button-container">
<button className="button button--primary text-caption">추가하기</button>
</div>
</form>
</div>
<div className="modal modal--open">
<div className="modal-backdrop" onClick={() => setIsAddModalOpen(false)}></div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍👍

<div className="modal-container">
<h2 className="modal-title text-title">새로운 음식점</h2>
<form>
<div className="form-item form-item--required">
<label htmlFor="category text-caption">카테고리</label>
<select
name="category"
id="category"
required
onChange={(selected) => setSelectedCategory(selected.target.value)}
>
<option value="">선택해 주세요</option>
{CATEGORY_DATA.slice(1).map((category) => (
<option key={category}>{category}</option>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인터뷰 🎤

categorykey 값으로 정해주신 이유에 대해 생각을 설명해주실 수 있을까요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저번주 스터디에서 배운 내용을 적용했어요!!

key prop이 바뀌면 리렌더링 됩니다. 따라서 아래 두 가지 상황을 피해야 한다고 배웠어요.

  • key에 즉석에서 생성한 값을 전달
  • key에 배열의 인덱스를 전달
    (배열 [ 'a', 'b', 'c', 'd']가 [ 'a', 'e', 'b', 'c', 'd']가 되면 'e'의 인덱스가 2가 되고 'b', 'c', 'd'의 인덱스는 1씩 밀리게 됩니다.
    이렇게 되면 'a'의 key를 제외하고 모든 key가 바뀌어 불필요하게 렌더링됩니다.)

이러한 이유로 categorykey값으로 적용했답니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key를 사용할 때 피해야 하는 상황을 잘 말씀해주셨네요 🚀

말씀해주신 예시인 [즉석에서 생성한 값을 전달] 의 경우에는 매 리렌더링마다 보통 새로운 값이 랜덤으로 생성되는 방법을 사용하기 때문에 React에서 key 값을 이용해 각 항목들을 식별하는 의미가 없어집니다. 항목이 계속해서 같은 키 값을 가지고 있어야 리스트의 여러 항목이 변경되더라도 그 항목을 식별하고 이를 이용해 더 최적화된 연산을 수행할 수 있는데, 매번 키 값이 새롭게 생성되면 React 입장에서는 실제로는 항목이 이동한 것에 불과한데도 새로운 항목이 추가된 것으로 인식하고 불필요한 연산들을 수행하려 할 겁니다.

index 를 키 값으로 사용하는 것도 비슷한 맥락입니다.

여기에 말씀하신 내용에 대해 좀 더 자세한 내용을 적어드릴게요.

리렌더링은 React.memo 등의 특수한 메모이제이션을 사용하지 않는 한 막을 수 없습니다. 리스트에서 부모 컴포넌트가 리렌더링되면 자식 컴포넌트는 리렌더링됩니다. 이건 기본 동작이자 원칙이며, key 값과 별개로 적용됩니다. 리렌더링이 되지 않으면 리스트를 지니고 있는 부모 컴포넌트의 상태가 바뀌어도 변경된 UI가 적용이 되지 않겠죠?

적절하게 key 값을 사용했을 때 작성하신 코드는 재조정(Reconcilation) 과정에서 활약할 수 있게 됩니다. 컴포넌트가 리렌더링되야 할 경우 React는 변경 사항을 비교하고 이를 기반으로 새로운 DOM 트리를 그려야 합니다. 이 때 key값을 기반으로 매번 트리를 새로 그리거나 새로운 DOM 요소를 비싼 비용으로 추가하지 않고 기존에 존재하는 DOM을 재사용할 수 있게 됩니다.

))}
</select>
</div>

<div className="form-item form-item--required">
<label htmlFor="name text-caption">이름</label>
<input
type="text"
name="name"
id="name"
required
onChange={(input) => setRestaurantName(input.target.value)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우선 구현해 주신 핸들러 로직은 적합하고 정석적인 좋은 값 업데이트 방법 중 하나에요 👍🏻 잘 구현하셨어요
여기에 작은 제안을 드리자면, 이 로직에서의 input 파라미터의 경우 실제로는 인풋 엘리먼트(요소) 그 자체라기보다는 이벤트 객체를 담고 있는 별개의 파라미터에요. 따라서 개발자들이 input을 이벤트 객체라고 더 쉽게 인지할 수 있도록 e, event 등의 네이밍을 더 추천드리고 싶어요

onChange={(event) => setRestaurantName(event.target.value)}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 수정하겠습니다

/>
</div>

<div className="form-item">
<label htmlFor="description text-caption">설명</label>
<textarea
name="description"
id="description"
cols="30"
rows="5"
onChange={(input) => setRestaurantInfo(input.target.value)}
></textarea>
Comment on lines +58 to +63

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<textarea
name="description"
id="description"
cols="30"
rows="5"
onChange={(input) => setRestaurantInfo(input.target.value)}
></textarea>
<textarea
name="description"
id="description"
cols="30"
rows="5"
value={restaurantInfo}
onChange={(input) => setRestaurantInfo(input.target.value)}
></textarea>

React에 의해 제어되도록 제어 컴포넌트를 사용하시기로 결정하셨다면, 이 요소의 value 또한 React에서 내려주는 state를 사용하실 것을 강력히 권장해 드리고 싶어요. 아래에 이유를 적어볼게요 ✏️

  • onChange 이벤트가 발생하는 경우 restaurantInfo state가 업데이트되므로 당장은 문제가 없는 것처럼 보일 수 있어요.
  • 그러나, restaurantInfo state가 다른 로직에 의해 업데이트되는 일이 생길 때에는 이 <textarea> 컴포넌트는 value 값이 React의 state로 되어 있지 않기 때문에 React의 최신화된 state 값이 반영되지 않아요. "다른 로직에 의해 업데이트 되는 일" 을 아래에 조금 설명해 볼게요.
    • 이후 [지우기] 버튼을 추가해 restaurantInfo를 포함한 모든 정보를 지우는 로직을 추가한다면?
    • 몇몇 폼들은 하나의 인풋이 변경되었을 때 여러 개의 인풋의 정보가 자동으로 바뀌도록 하거나, 보이는 메뉴를 다르게 하기도 하는 기능을 구현한다면?
  • 즉, 이러한 작업을 해 주는 이유는 이후 state가 변화하더라도 정확한 값을 지닐 수 있도록 동기화 해 주기 위함이에요.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호... 리액트에서 상태는 생각보다 엄청 중요한 개념이네요.

React에 의해 제어되도록 제어 컴포넌트를 사용하시기로 결정하셨다면, 이 요소의 value 또한 React에서 내려주는 state를 사용하실 것을 강력히 권장해 드리고 싶어요.

명심하겠습니다!!

<span className="help-text text-caption">메뉴 등 추가 정보를 입력해 주세요.</span>
</div>

<div className="button-container">
<button className="button button--primary text-caption" onClick={handleAddBtnClick}>
추가하기
</button>
</div>
</form>
</div>
</>
</div>
);
}

Expand Down
15 changes: 7 additions & 8 deletions src/components/CategoryFilter.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "../styles/CategoryFilterStyle.css";
import { CATEGORY_DATA } from "../data/categoryData";

function CategoryFilter() {
function CategoryFilter({ category, onChangeCategory }) {
return (
<>
<section className="restaurant-filter-container">
Expand All @@ -9,14 +10,12 @@ function CategoryFilter() {
id="category-filter"
className="restaurant-filter"
aria-label="음식점 카테고리 필터"
value={category}
onChange={() => onChangeCategory(event.target.value)}
>
<option value="전체">전체</option>
<option value="한식">한식</option>
<option value="중식">중식</option>
<option value="일식">일식</option>
<option value="양식">양식</option>
<option value="아시안">아시안</option>
<option value="기타">기타</option>
{CATEGORY_DATA.map((category) => (
<option key={category}>{category}</option>
))}
</select>
</section>
</>
Expand Down
11 changes: 8 additions & 3 deletions src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import "../styles/HeaderStyle.css";

function Header() {
function Header({ setIsAddModalOpen }) {
return (
<>
<header className="gnb">
<h1 className="gnb__title text-title">점심 뭐 먹지</h1>
<button type="button" className="gnb__button" aria-label="음식점 추가">
<img src="./add-button.png" alt="음식점 추가" />
<button
type="button"
className="gnb__button"
aria-label="음식점 추가"
onClick={() => setIsAddModalOpen(true)}
>
<img src="../../templates/add-button.png" alt="음식점 추가" />
</button>
</header>
</>
Expand Down
16 changes: 10 additions & 6 deletions src/components/RestaurantDetailModal.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import "../styles/RestaurantDetailModalStyle.css";

function RestaurantDetailModal() {
function RestaurantDetailModal({ setIsModalOpen, restaurantName, restaurantInfo }) {
return (
<>
<div className="modal modal--open">
<div className="modal-backdrop"></div>
<div className="modal-backdrop" onClick={() => setIsModalOpen(false)}></div>
<div className="modal-container">
<h2 className="modal-title text-title">음식점 이름</h2>
<h2 className="modal-title text-title">{restaurantName}</h2>
<div className="restaurant-info">
<p className="restaurant-info__description text-body">음식점 소개 문구</p>
<p className="restaurant-info__description text-body">{restaurantInfo}</p>
</div>
{/* 닫기 버튼 */}
<div className="button-container">
<button className="button button--primary text-caption">닫기</button>
<button
className="button button--primary text-caption"
onClick={() => setIsModalOpen(false)}
>
닫기
</button>
</div>
</div>
</div>
Expand Down
106 changes: 31 additions & 75 deletions src/components/RestaurantList.jsx
Original file line number Diff line number Diff line change
@@ -1,84 +1,40 @@
import "../styles/RestaurantListStyle.css";

function RestaurantList() {
const CATEGORY_IN_ENGLISH = Object.freeze({
한식: "korean",
중식: "chinese",
일식: "japanese",
양식: "western",
아시안: "asian",
기타: "etc",
});

function RestaurantList({ restaurants, SetIsModalOpen, setRestaurantName, setRestaurantInfo }) {
const handleClick = (restaurant) => {
SetIsModalOpen(true);
setRestaurantName(restaurant.name);
setRestaurantInfo(restaurant.description);
};

return (
<>
<section className="restaurant-list-container">
<ul className="restaurant-list">
<li className="restaurant">
<div className="restaurant__category">
<img src="/category-korean.png" alt="한식" className="category-icon" />
</div>
<div className="restaurant__info">
<h3 className="restaurant__name text-subtitle">피양콩할마니</h3>
<p className="restaurant__description text-body">
평양 출신의 할머니가 수십 년간 운영해온 비지 전문점 피양콩 할마니. 두부를 빼지 않은
되비지를 맛볼 수 있는 곳으로, ‘피양’은 평안도 사투리로 ‘평양’을 의미한다. 딸과 함께
운영하는 이곳에선 맷돌로 직접 간 콩만을 사용하며, 일체의 조미료를 넣지 않은 건강식을
선보인다. 콩비지와 피양 만두가 이곳의 대표 메뉴지만, 할머니가 옛날 방식을 고수하며
만들어내는 비지전골 또한 이 집의 역사를 느낄 수 있는 특별한 메뉴다. 반찬은 손님들이
먹고 싶은 만큼 덜어 먹을 수 있게 준비돼 있다.
</p>
</div>
</li>

<li className="restaurant">
<div className="restaurant__category">
<img src="/category-chinese.png" alt="중식" className="category-icon" />
</div>
<div className="restaurant__info">
<h3 className="restaurant__name text-subtitle">친친</h3>
<p className="restaurant__description text-body">
Since 2004 편리한 교통과 주차, 그리고 관록만큼 깊은 맛과 정성으로 정통 중식의 세계를
펼쳐갑니다
</p>
</div>
</li>

<li className="restaurant">
<div className="restaurant__category">
<img src="/category-japanese.png" alt="일식" className="category-icon" />
</div>
<div className="restaurant__info">
<h3 className="restaurant__name text-subtitle">잇쇼우</h3>
<p className="restaurant__description text-body">
잇쇼우는 정통 자가제면 사누끼 우동이 대표메뉴입니다. 기술은 정성을 이길 수 없다는
신념으로 모든 음식에 최선을 다하는 잇쇼우는 고객 한분 한분께 최선을 다하겠습니다
</p>
</div>
</li>

<li className="restaurant">
<div className="restaurant__category">
<img src="/category-western.png" alt="양식" className="category-icon" />
</div>
<div className="restaurant__info">
<h3 className="restaurant__name text-subtitle">이태리키친</h3>
<p className="restaurant__description text-body">
늘 변화를 추구하는 이태리키친입니다.
</p>
</div>
</li>

<li className="restaurant">
<div className="restaurant__category">
<img src="/category-asian.png" alt="아시안" className="category-icon" />
</div>
<div className="restaurant__info">
<h3 className="restaurant__name text-subtitle">호아빈 삼성점</h3>
<p className="restaurant__description text-body">푸짐한 양에 국물이 일품인 쌀국수</p>
</div>
</li>

<li className="restaurant">
<div className="restaurant__category">
<img src="/category-etc.png" alt="기타" className="category-icon" />
</div>
<div className="restaurant__info">
<h3 className="restaurant__name text-subtitle">도스타코스 선릉점</h3>
<p className="restaurant__description text-body">멕시칸 캐주얼 그릴</p>
</div>
</li>
{restaurants.map((restaurant) => (
<li key={restaurant.id} className="restaurant" onClick={() => handleClick(restaurant)}>
<div className="restaurant__category">
<img
src={`../../templates/category-${CATEGORY_IN_ENGLISH[restaurant.category]}.png`}
alt=""
className="category-icon"
/>
</div>
<div className="restaurant__info">
<h3 className="restaurant__name text-subtitle">{restaurant.name}</h3>
<p className="restaurant__description text-body">{restaurant.description}</p>
</div>
</li>
))}
</ul>
</section>
</>
Expand Down
9 changes: 9 additions & 0 deletions src/data/categoryData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const CATEGORY_DATA = Object.freeze([
"전체",
"한식",
"중식",
"일식",
"양식",
"아시안",
"기타",
]);
Loading