diff --git a/craco.config.js b/craco.config.js
index e39f09a..f3fab18 100644
--- a/craco.config.js
+++ b/craco.config.js
@@ -4,12 +4,15 @@ module.exports = {
webpack: {
alias: {
"@": path.resolve(__dirname, "src"),
+ "@contexts": path.resolve(__dirname, "src/contexts"),
"@apis": path.resolve(__dirname, "src/apis"),
"@components": path.resolve(__dirname, "src/components"),
"@hooks": path.resolve(__dirname, "src/hooks"),
"@pages": path.resolve(__dirname, "src/pages"),
"@routes": path.resolve(__dirname, "src/routes"),
"@utils": path.resolve(__dirname, "src/utils"),
+ "@icons": path.resolve(__dirname, "src/icons"),
+ "@styles": path.resolve(__dirname, "src/styles"),
},
},
};
diff --git a/package-lock.json b/package-lock.json
index f1c2e1b..a4eef19 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
"axios": "^1.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-helmet-async": "^1.3.0",
"react-router-dom": "^6.4.2",
"react-scripts": "5.0.1",
"styled-components": "^5.3.6",
@@ -8901,6 +8902,14 @@
"node": ">= 0.4"
}
},
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/ipaddr.js": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz",
@@ -14013,6 +14022,27 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
},
+ "node_modules/react-fast-compare": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
+ "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
+ },
+ "node_modules/react-helmet-async": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz",
+ "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "invariant": "^2.2.4",
+ "prop-types": "^15.7.2",
+ "react-fast-compare": "^3.2.0",
+ "shallowequal": "^1.1.0"
+ },
+ "peerDependencies": {
+ "react": "^16.6.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
@@ -23506,6 +23536,14 @@
"side-channel": "^1.0.4"
}
},
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"ipaddr.js": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz",
@@ -27153,6 +27191,23 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
},
+ "react-fast-compare": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
+ "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
+ },
+ "react-helmet-async": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz",
+ "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==",
+ "requires": {
+ "@babel/runtime": "^7.12.5",
+ "invariant": "^2.2.4",
+ "prop-types": "^15.7.2",
+ "react-fast-compare": "^3.2.0",
+ "shallowequal": "^1.1.0"
+ }
+ },
"react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
diff --git a/package.json b/package.json
index 513f9cb..0bf9e34 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
"axios": "^1.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-helmet-async": "^1.3.0",
"react-router-dom": "^6.4.2",
"react-scripts": "5.0.1",
"styled-components": "^5.3.6",
diff --git a/src/App.js b/src/App.js
index 491dfe5..0ea0594 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,5 +1,19 @@
+import Router from "@routes/Router";
+import { CarListContextProvider } from "@contexts/CarListContext";
+import { BrowserRouter } from "../node_modules/react-router-dom/dist/index";
+import { theme } from "@styles/theme";
+import { ThemeProvider } from "styled-components";
+
function App() {
- return
Hello React!
;
+ return (
+
+
+
+
+
+
+
+ );
}
export default App;
diff --git a/src/apis/instance.js b/src/apis/instance.js
new file mode 100644
index 0000000..77c7f12
--- /dev/null
+++ b/src/apis/instance.js
@@ -0,0 +1,7 @@
+import axios from "../../node_modules/axios/index";
+
+const instance = axios.create({
+ baseURL: process.env.REACT_APP_API_URL,
+});
+
+export default instance;
diff --git a/src/components/CarItem.jsx b/src/components/CarItem.jsx
new file mode 100644
index 0000000..f07d4ce
--- /dev/null
+++ b/src/components/CarItem.jsx
@@ -0,0 +1,134 @@
+import React from "react";
+
+import { convertDate } from "@utils/date";
+import { fuelEnum, segmentEnum } from "@utils/enum";
+
+import { GSInfoText } from "@styles/styled";
+import styled from "styled-components";
+
+const CarItem = ({ carItem }) => {
+ const { car, isLoading, isError } = carItem;
+
+ return (
+ <>
+ {isLoading && 불러오는중}
+ {isError && 에러가 발생했습니다}
+ {car && (
+ <>
+
+
+
+
+
+ {car.attribute.brand}
+ {car.attribute.name}
+ 월 {car.amount.toLocaleString("ko-KR")}원
+
+
+ 차량정보
+
+ 차종
+ {segmentEnum[car.attribute.segment]}
+
+
+ 연료
+ {fuelEnum[car.attribute.fuelType]}
+
+
+ 이용 가능일
+ {convertDate(car.startDate)}
+
+
+ {car.insurance.length !== 0 && (
+ <>
+ 보험
+ {car.insurance.map((item) => (
+
+ {item.name}
+ {item.description}
+
+ ))}
+ >
+ )}
+
+ {car.additionalProducts.length !== 0 && (
+ <>
+ 추가상품
+ {car.additionalProducts.map((item) => (
+
+ {item.name}
+ 월 {item.amount.toLocaleString("ko-KR")}원
+
+ ))}
+ >
+ )}
+ >
+ )}
+ >
+ );
+};
+
+const SCarImage = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ height: 205px;
+
+ img {
+ width: 100%;
+ }
+`;
+
+const SCarInfo = styled.div`
+ display: flex;
+ flex-direction: column;
+ padding: 0 20px;
+
+ > span:nth-child(1) {
+ font-size: 20px;
+ font-weight: 700;
+ }
+
+ > span:nth-child(2) {
+ font-size: 24px;
+ font-weight: 700;
+ }
+
+ > span:nth-child(3) {
+ height: 48px;
+ margin-top: 21px;
+ text-align: end;
+ }
+`;
+
+const SSubTitle = styled.div`
+ display: flex;
+ align-items: center;
+
+ width: 100%;
+ height: 48px;
+ padding: 0 20px;
+ background-color: ${({ theme }) => theme.color.blue};
+
+ font-size: 17px;
+ font-weight: 700;
+ color: ${({ theme }) => theme.color.white};
+`;
+
+const SContent = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ height: 48px;
+ padding: 0 20px;
+
+ font-size: 17px;
+
+ > span:nth-child(1) {
+ font-weight: 700;
+ }
+`;
+
+export default CarItem;
diff --git a/src/components/CarList.jsx b/src/components/CarList.jsx
new file mode 100644
index 0000000..29a78b6
--- /dev/null
+++ b/src/components/CarList.jsx
@@ -0,0 +1,92 @@
+import React from "react";
+import { useNavigate } from "../../node_modules/react-router-dom/dist/index";
+import { fuelEnum, segmentEnum } from "@/utils/enum";
+import { GSInfoText } from "@styles/styled";
+import styled from "styled-components";
+
+const CarList = ({ carList }) => {
+ const navigate = useNavigate();
+ const { cars, isLoading, isError } = carList;
+
+ const handleClickCarItem = (id) => {
+ navigate(`/detail/${id}`);
+ };
+
+ return (
+
+ {isLoading && 불러오는중}
+ {isError && 에러가 발생했습니다}
+ {cars?.length === 0 && 차량이 없습니다.}
+
+ {cars?.map((car) => (
+ handleClickCarItem(car.id)}>
+
+
+ {car.attribute.brand}
+ {car.attribute.name}
+
+
+
+ {segmentEnum[car.attribute.segment]} / {fuelEnum[car.attribute.fuelType]}
+
+ 월 {car.amount.toLocaleString("ko-KR")}원 부터
+
+
+
+
+
+
+
+ ))}
+
+ );
+};
+
+const SCarListContainer = styled.main`
+ display: flex;
+ flex-direction: column;
+`;
+
+const SCarItem = styled.div`
+ display: flex;
+ justify-content: space-between;
+
+ height: 120px;
+ padding: 20px;
+ border-bottom: 1px solid ${({ theme }) => theme.color.black};
+
+ cursor: pointer;
+
+ > div:nth-child(1) {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ }
+
+ > div:nth-child(2) {
+ width: 152px;
+
+ img {
+ width: 100%;
+ }
+ }
+`;
+
+const SCarName = styled.div`
+ display: flex;
+ flex-direction: column;
+
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 17px;
+`;
+
+const SCarInfo = styled.div`
+ display: flex;
+ flex-direction: column;
+
+ font-size: 12px;
+ line-height: 15px;
+`;
+
+export default CarList;
diff --git a/src/components/Header.jsx b/src/components/Header.jsx
new file mode 100644
index 0000000..aaf69e4
--- /dev/null
+++ b/src/components/Header.jsx
@@ -0,0 +1,41 @@
+import React from "react";
+import { useNavigate } from "../../node_modules/react-router-dom/dist/index";
+import IconBack from "@icons/ICON_Back.svg";
+import styled from "styled-components";
+
+const Header = ({ children, isBack }) => {
+ const navigate = useNavigate();
+
+ return (
+
+ {isBack && navigate(-1)} />}
+ {children}
+
+ );
+};
+
+const SHeader = styled.header`
+ position: relative;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ width: 100%;
+ height: 60px;
+ border-bottom: 1px solid ${({ theme }) => theme.color.black};
+
+ img {
+ position: absolute;
+ left: 25px;
+
+ cursor: pointer;
+ }
+
+ span {
+ font-size: 17px;
+ font-weight: 700;
+ }
+`;
+
+export default Header;
diff --git a/src/components/Layout.jsx b/src/components/Layout.jsx
new file mode 100644
index 0000000..10a159f
--- /dev/null
+++ b/src/components/Layout.jsx
@@ -0,0 +1,22 @@
+import React from "react";
+import styled from "styled-components";
+
+const Layout = ({ children }) => {
+ return {children};
+};
+
+const SLayout = styled.div`
+ @media screen and (min-width: 450px) {
+ width: 450px;
+ }
+
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
+
+ min-width: 360px;
+ width: 100%;
+ height: 100%;
+`;
+
+export default Layout;
diff --git a/src/components/Nav.jsx b/src/components/Nav.jsx
new file mode 100644
index 0000000..678ecbe
--- /dev/null
+++ b/src/components/Nav.jsx
@@ -0,0 +1,56 @@
+import React from "react";
+import { segmentArr, fuelArr } from "@utils/enum";
+import styled from "styled-components";
+
+const Nav = ({ handleClickCategory }) => {
+ return (
+
+
+ {segmentArr.map((segment) => (
+
+ {segment}
+
+ ))}
+
+
+
+ {fuelArr.map((fuel) => (
+
+ {fuel}
+
+ ))}
+
+
+ );
+};
+
+const SNav = styled.nav`
+ display: flex;
+ flex-direction: column;
+
+ border-bottom: 1px solid ${({ theme }) => theme.color.black};
+
+ ul {
+ display: flex;
+ gap: 8px;
+ padding: 6px 12px;
+ }
+`;
+
+const SButton = styled.li`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ padding: 5px 18px;
+ background-color: ${({ theme }) => theme.color.gray};
+
+ border-radius: 999px;
+
+ font-size: 14px;
+ font-weight: 700;
+
+ cursor: pointer;
+`;
+
+export default Nav;
diff --git a/src/contexts/CarItemContext.js b/src/contexts/CarItemContext.js
new file mode 100644
index 0000000..a72be30
--- /dev/null
+++ b/src/contexts/CarItemContext.js
@@ -0,0 +1,36 @@
+import { createContext, useCallback, useContext, useState } from "react";
+import instance from "@apis/instance";
+
+const CarItemContext = createContext(null);
+
+export const CarItemContextProvider = ({ children }) => {
+ const [carItem, setCarItem] = useState({
+ car: null,
+ isLoading: true,
+ isError: false,
+ });
+
+ const handleGetCarItem = useCallback(async (id) => {
+ try {
+ const { data } = await instance.get("/cars");
+ const [carItem] = data.payload.filter((item) => item.id === +id);
+ setCarItem((prev) => ({ ...prev, car: carItem, isLoading: false }));
+ } catch (error) {
+ setCarItem((prev) => ({ ...prev, isLoading: false, isError: true }));
+ }
+ }, []);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useCarItemState = () => {
+ const state = useContext(CarItemContext);
+ if (!state) {
+ throw new Error("프로바이더를 찾을 수 없습니다.");
+ }
+ return state;
+};
diff --git a/src/contexts/CarListContext.js b/src/contexts/CarListContext.js
new file mode 100644
index 0000000..26867c7
--- /dev/null
+++ b/src/contexts/CarListContext.js
@@ -0,0 +1,68 @@
+import { createContext, useCallback, useContext, useEffect, useState } from "react";
+import instance from "@apis/instance";
+import { getKeyByValue } from "@utils/function";
+import { fuelEnum, segmentEnum } from "@utils/enum";
+
+const CarListContext = createContext(null);
+
+export const CarListContextProvider = ({ children }) => {
+ const [carList, setCarList] = useState({
+ cars: null,
+ isLoading: true,
+ isError: false,
+ });
+
+ const [category, setCategory] = useState({
+ segment: null,
+ fuelType: null,
+ });
+
+ const handleGetCarList = useCallback(async () => {
+ try {
+ let data;
+ if (!category.segment && !category.fuelType) {
+ data = await instance.get("/cars");
+ } else {
+ const query = Object.keys(category)
+ .map((item) => category[item] && `${item}=${category[item]}`)
+ .join("&");
+ data = await instance.get(`/cars?${query}`);
+ }
+ setCarList((prev) => ({ ...prev, cars: data.data.payload, isLoading: false }));
+ } catch (error) {
+ setCarList((prev) => ({ ...prev, isLoading: false, isError: true }));
+ }
+ }, [category]);
+
+ useEffect(() => {
+ handleGetCarList();
+ }, [handleGetCarList]);
+
+ const handleClickCategory = (event) => {
+ const name = event.target.getAttribute("name");
+ const value = event.target.getAttribute("value");
+
+ let newValue;
+ if (name === "segment") {
+ newValue = getKeyByValue(segmentEnum, value);
+ } else {
+ newValue = getKeyByValue(fuelEnum, value);
+ }
+
+ setCategory((prev) => ({ ...prev, [name]: newValue }));
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useCarListState = () => {
+ const state = useContext(CarListContext);
+ if (!state) {
+ throw new Error("프로바이더를 찾을 수 없습니다.");
+ }
+ return state;
+};
diff --git a/src/icons/ICON_Back.svg b/src/icons/ICON_Back.svg
new file mode 100644
index 0000000..62d1f16
--- /dev/null
+++ b/src/icons/ICON_Back.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/index.css b/src/index.css
index 714ab0e..427c7e8 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,11 +1,22 @@
+* {
+ box-sizing: border-box;
+}
+
body {
margin: 0;
+ padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
"Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
+ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}
diff --git a/src/pages/Detail.jsx b/src/pages/Detail.jsx
new file mode 100644
index 0000000..60072f2
--- /dev/null
+++ b/src/pages/Detail.jsx
@@ -0,0 +1,27 @@
+import React, { useEffect } from "react";
+import { useParams } from "../../node_modules/react-router-dom/dist/index";
+import Header from "@components/Header";
+import Layout from "@components/Layout";
+
+import { useCarItemState } from "@contexts/CarItemContext";
+
+import CarItem from "../components/CarItem";
+
+const Detail = () => {
+ const { id } = useParams();
+ const { carItem, handleGetCarItem } = useCarItemState();
+
+ useEffect(() => {
+ handleGetCarItem(id);
+ }, [id, handleGetCarItem]);
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default Detail;
diff --git a/src/pages/Main.jsx b/src/pages/Main.jsx
new file mode 100644
index 0000000..bd2dfb7
--- /dev/null
+++ b/src/pages/Main.jsx
@@ -0,0 +1,23 @@
+import React from "react";
+import { useCarListState } from "@contexts/CarListContext";
+
+import Header from "@components/Header";
+import CarList from "@components/CarList";
+import Layout from "@components/Layout";
+import Nav from "@components/Nav";
+
+const Main = () => {
+ const { carList, handleClickCategory } = useCarListState();
+
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+export default Main;
diff --git a/src/routes/Router.jsx b/src/routes/Router.jsx
new file mode 100644
index 0000000..5a90277
--- /dev/null
+++ b/src/routes/Router.jsx
@@ -0,0 +1,24 @@
+import { Route, Routes } from "../../node_modules/react-router-dom/dist/index";
+import { ROUTH_PATH } from "@routes/routesPath";
+
+import Detail from "@pages/Detail";
+import Main from "@pages/Main";
+import { CarItemContextProvider } from "@contexts/CarItemContext";
+
+const Router = () => {
+ return (
+
+ } />
+
+
+
+ }
+ />
+
+ );
+};
+
+export default Router;
diff --git a/src/routes/routesPath.js b/src/routes/routesPath.js
new file mode 100644
index 0000000..8bc49f0
--- /dev/null
+++ b/src/routes/routesPath.js
@@ -0,0 +1,4 @@
+export const ROUTH_PATH = {
+ MAIN: "/",
+ DETAIL: "/detail/:id",
+};
diff --git a/src/styles/styled.js b/src/styles/styled.js
new file mode 100644
index 0000000..3683c21
--- /dev/null
+++ b/src/styles/styled.js
@@ -0,0 +1,13 @@
+import styled from "styled-components";
+
+const GSInfoText = styled.span`
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+
+ font-size: 17px;
+ font-weight: 700;
+`;
+
+export { GSInfoText };
diff --git a/src/styles/theme.js b/src/styles/theme.js
new file mode 100644
index 0000000..fbad776
--- /dev/null
+++ b/src/styles/theme.js
@@ -0,0 +1,12 @@
+const color = {
+ white: "#fff",
+ black: "#000",
+ gray: "#d9d9d9",
+ blue: "#0094ff",
+};
+
+const theme = {
+ color,
+};
+
+export { theme };
diff --git a/src/utils/date.js b/src/utils/date.js
new file mode 100644
index 0000000..cd7963f
--- /dev/null
+++ b/src/utils/date.js
@@ -0,0 +1,12 @@
+const convertDate = (startDate) => {
+ const newDate = new Date(startDate);
+ const weekArr = "월화수목금토일".split("");
+
+ const month = newDate.getMonth() + 1;
+ const date = newDate.getDate();
+ const dayOfWeek = weekArr[date];
+
+ return `${month}월 ${date}일 (${dayOfWeek})부터`;
+};
+
+export { convertDate };
diff --git a/src/utils/enum.js b/src/utils/enum.js
new file mode 100644
index 0000000..7e665ff
--- /dev/null
+++ b/src/utils/enum.js
@@ -0,0 +1,20 @@
+const segmentEnum = {
+ C: "소형",
+ D: "중형",
+ E: "대형",
+ SUV: "SUV",
+};
+
+const fuelEnum = {
+ gasoline: "가솔린",
+ ev: "전기",
+ hybrid: "하이브리드",
+};
+
+const segmentArr = ["전체", ...Object.values(segmentEnum)];
+const fuelArr = ["전체", ...Object.values(fuelEnum)];
+
+Object.freeze(segmentEnum);
+Object.freeze(fuelEnum);
+
+export { segmentEnum, fuelEnum, segmentArr, fuelArr };
diff --git a/src/utils/function.js b/src/utils/function.js
new file mode 100644
index 0000000..35cfda2
--- /dev/null
+++ b/src/utils/function.js
@@ -0,0 +1,5 @@
+const getKeyByValue = (obj, value) => {
+ return Object.keys(obj).find((key) => obj[key] === value);
+};
+
+export { getKeyByValue };