([]);
+```
-- ํผ๊ทธ๋ง๋ฅผ ๋ณด๊ณ [๊ฒฐ๊ณผํ๋ฉด](https://3th-fb-messenger.netlify.app)๊ณผ ๊ฐ์ด ๊ตฌํํฉ๋๋ค.
-- ๋์์ธ ์์คํ
์ ๊ตฌ์ถํฉ๋๋ค.
-- ์ฑํ
๋ฐฉ ์๋จ์ ํ๋กํ์ ํด๋ฆญํ๋ฉด ์ฌ์ฉ์๋ฅผ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
-- ๋ฉ์ธ์ง๋ฅผ ๋ณด๋ด๋ฉด ์ฑํ
๋ฐฉ ํ๋จ์ผ๋ก ์คํฌ๋กค์ ์ด๋์ํต๋๋ค.
-- ๋ฉ์ธ์ง์ ์ ์ ์ ๋ณด(ํ๋กํ ์ฌ์ง, ์ด๋ฆ)๋ฅผ ํ์ํฉ๋๋ค.
-- user์ message ๋ฐ์ดํฐ๋ฅผ json ํ์ผ์ ์ ์ฅํฉ๋๋ค.
-- UI๋ **๋ฐ์ํ์ ์ ์ธ**ํ๊ณ ํผ๊ทธ๋งํ์ผ์ ๋ฐ๋ผ์ ์งํํฉ๋๋ค.
+ChatBodyProps ์ธํฐํ์ด์ค๋ ChatBody ์ปดํฌ๋ํธ๊ฐ ๋ฐ์์ผ ํ props์ ๊ตฌ์กฐ์ ํ์
์ ๋ช
์์ ์ผ๋ก ์ ์ํด์ฃผ๊ธฐ ๋๋ฌธ์,
+์ปดํฌ๋ํธ์ ์๋ชป๋ ํ์
์ props๋ฅผ ์ ๋ฌํ๊ฑฐ๋ ์ ๋ฌํ์ง ์์์ ๊ฒฝ์ฐ, ์ปดํ์ผ ๋จ๊ณ์์ ๋ฐ๋ก ์ค๋ฅ๋ฅผ ๋ฐ๊ฒฌํ ์ ์์ต๋๋ค.
-### ์ถ๊ฐ ๊ตฌํ ๊ธฐ๋ฅ
+๋ํ ๋ฉ์์ง๋ฅผ ์ถ๊ฐํ๋ ํจ์ ๋ด์์ newMessage ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋, Message ์ธํฐํ์ด์ค์์ ์๊ตฌํ๋ ์์ฑ์ ๋นผ๋จน๊ฑฐ๋ ์๋ชป๋ ํ์
์ ์ฌ์ฉํ๋ฉด TypeScript ์ปดํ์ผ๋ฌ๊ฐ ์ฆ์ ์ค๋ฅ๋ฅผ ํ์ํ๊ธฐ ๋๋ฌธ์, ๋ฐํ์ ์ค๋ฅ๋ฅผ ๋ฐฉ์งํ๊ณ , ์ค์ ๊ฐ๋ฅ์ฑ์ ์ค์
๋๋ค.
-- ๋๋ธ ํด๋ฆญ ํ๋ฉด ๊ฐ์ ํํ์ ์ถ๊ฐํฉ๋๋ค.
-- ๊ทธ ์ธ ์ถ๊ฐํ๊ณ ์ถ์ ๊ธฐ๋ฅ์ด ์๋ค๋ฉด ๋ง์๊ป ์ถ๊ฐํด ์ฃผ์ธ์!
+2. ์ฝ๋ ์๋ ์์ฑ ๋ฐ ๋ฆฌํฉํ ๋ง ์ฉ์ด
+ ์ปดํฌ๋ํธ์ ์ด๋ค props๋ฅผ ์ ๋ฌํด์ผ ํ๋์ง ์ฝ๊ฒ ์ ์ ์๊ณ , ์ด๋ ๊ฐ๋ฐ ํจ์จ์ฑ์ ๋์
๋๋ค.
+ ๋ํ ํน์ ์ธํฐํ์ด์ค์ ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํด์ผ ํ๋ค๋ฉด, TypeScript๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋น ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ ๊ณณ์ ์ฐพ์ ์์ ํด์ผ ํจ์ ์ ์ ์๊ธฐ ๋๋ฌธ์, ๋์ฑ ์์ ํ๊ณ ๋น ๋ฅธ ๋ฆฌํฉํ ๋ง์ด ๊ฐ๋ฅํฉ๋๋ค.
-์ฐธ๊ณ ๋ก ์ด๋ฒ ๊ณผ์ ๋ ๋ค์์ฃผ๊น์ง ์ด์ด์ง๋ ๊ณผ์ ์ด๋ฏ๋ก **ํ์ฅ์ฑ**์ ์ถฉ๋ถํ ๊ณ ๋ คํด ์ฃผ์ธ์. ์ฐธ๊ณ ๋ก **4์ฃผ์ฐจ ๊ณผ์ ์์๋ ์ ์ ๋ฐ ๊ธฐ๋ฅ ์ถ๊ฐ์ Routing์** ์งํํฉ๋๋ค. ์ด๋ฅผ ์ํด [recoil](https://recoiljs.org/ko/)์ด๋ [redux](https://ko.redux.js.org/introduction/getting-started/)๋ฅผ ์ด์ฉํ ์ํ๊ด๋ฆฌ๋ฅผ ๋ฏธ๋ฆฌ ํด๋ณด์๋ ๊ฒ์ ์ถ์ฒํฉ๋๋ค!! ๋ชจ๋ ๊ณต์๋ฌธ์ ๋ง์ด ์ฝ์ด๋ณด์๊ณ ์์ ๋ง์ ์ํ๊ด๋ฆฌ ์กฐํฉ๋ ์ฐพ์๋ณด๋ฉด ์ฌ๋ฐ์ ๊ฑฐ์์ XD
+3. ์ฝ๋ ์๋์ ๊ตฌ์กฐ ๋ช
ํํ
+ ์ธํฐํ์ด์ค๋ฅผ ํตํด ์ฝ๋์ ์๋์ ๊ตฌ์กฐ๋ฅผ ๋ค๋ฅธ ๊ฐ๋ฐ์์๊ฒ ๋ช
ํํ ์ ๋ฌํ ์ ์์ด ์ฝ๋์ ๊ฐ๋
์ฑ์ด ํฅ์๋ฉ๋๋ค.
-## ๋งํฌ ๋ฐ ์ฐธ๊ณ ์๋ฃ
+## ๋์์ด๋๋ก๋ถํฐ ์ ๋ฌ๋ฐ์ ํผ๊ทธ๋ง ๋งํฌ ํน์, ํผ๊ทธ๋ง ์บก์ฒ๋ณธ
-- [React docs - Hook](https://ko.reactjs.org/docs/hooks-intro.html)
-- [React์ Hooks ์๋ฒฝ ์ ๋ณตํ๊ธฐ](https://velog.io/@velopert/react-hooks#1-usestate)
-- [useEffect ์๋ฒฝ ๊ฐ์ด๋](https://overreacted.io/ko/a-complete-guide-to-useeffect/)
-- [์ฝ๋ฉ ์ปจ๋ฒค์
](https://ui.toast.com/fe-guide/ko_CODING-CONVENTION)
-- [ํ์
์คํฌ๋ฆฝํธ ํธ๋๋ถ](https://joshua1988.github.io/ts/intro.html)
-- [๋ฆฌ์กํธ ํ๋ก์ ํธ์์ ํ์
์คํฌ๋ฆฝํธ ์ฌ์ฉํ๊ธฐ (์๋ฆฌ์ฆ)](https://velog.io/@velopert/series/react-with-typescript)
-- [๋์์ธ ์์คํ
๊ตฌ์ถ๊ธฐ](https://yozm.wishket.com/magazine/detail/1830/)
-- [[์์] : ์ปดํฌ๋ํธ์ ๋ํ ์ดํด](https://www.youtube.com/watch?v=21eiJc90ggo)
-- [Styled Component๋ก ๋์์ธ ์์คํ
๊ตฌ์ถํ๊ธฐ](https://zaat.dev/blog/building-a-design-system-in-react-with-styled-components/)
-- [ts ์ ๋๊ฒฝ๋ก ์ค์ ํ๊ธฐ](https://tesseractjh.tistory.com/232)
+
+
+## ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌํ ๊ธฐ์ค์ ๋ฌด์์ธ๊ฐ์?
+
+1. ๊ฐ ์ปดํฌ๋ํธ๋ฅผ UI ์์ญ๋ณ & ๊ธฐ๋ฅ๋ณ๋ก ๋ถ๋ฆฌํ์ต๋๋ค.
+ ๋จ์ผ ์ฑ
์ ์์น์ ๋ฐ๋ผ ํ ๊ฐ์ง ๊ธฐ๋ฅ๋ง ์ํํ๋๋ก ์ค๊ณํ์ต๋๋ค.
+
+- **ChatHead**: ์ฑํ
๋ฐฉ์ ์๋จ์ ์์นํ ์ฌ์ฉ์ ํ๋กํ์ ํ์ํฉ๋๋ค. ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ์ด๋ฏธ์ง๋ฅผ ํฌํจํฉ๋๋ค.
+- **ChatBody**: ์ฑํ
๋ฐฉ์์์ ๋ํ ๋ด์ฉ์ ํ์ํฉ๋๋ค. ๋ฉ์์ง๋ง๋ค ์๊ฐ๊ณผ ๋ ์ง, ์ฌ์ฉ์ ํ๋กํ ์ด๋ฏธ์ง๋ฅผ ํ์ํ ์ ์๋๋ก ๊ตฌํํ์ต๋๋ค.
+- **ChatBottom**: ๋ฉ์์ง ์
๋ ฅ์ฐฝ๊ณผ ์ ์ก ๋ฒํผ์ ํฌํจํฉ๋๋ค. ์ฌ์ฉ์ ์
๋ ฅ์ ๋ฐ๊ณ , 'Enter' ํค๋ ์ ์ก ๋ฒํผ ํด๋ฆญ ์ ๋ฉ์์ง๋ฅผ ์ ์กํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ์ต๋๋ค.
+
+2. ๋ํ, ๋ฒํผ, ์
๋ ฅ ํ๋๊ฐ์ด, ์ฌ์ฉ์ ์ด๋ฒคํธ์ ๋ฐ๋ผ, ํน์ ๊ธฐ๋ฅ์ ํ๊ณ , ์์ฃผ ์ฌ์ฉ๋๋ UI๋ค๋ ์ฌ์ฌ์ฉ์ด ์ฉ์ดํ๊ฒ ๋ณ๋์ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํ์ต๋๋ค.
+
+## ๋์์ธ ์์คํ
์ ์ ์ฉํ๋ฉด์ ๋๋ ์ ์ ๋ฌด์์ธ๊ฐ์?
+
+๋์์ธ ์์คํ
์ ์ ์ฉํจ์ผ๋ก์จ UI์ ์ผ๊ด์ฑ์ ์ ์งํ ์ ์์๋ ๊ฒ ๊ฐ์ต๋๋ค.
+๋ํ, ๊ฐ๋ฐ ๊ณผ์ ์์๋ ํ์คํ ํธ๋ฆฌํจ์ ๋๊ผ์ต๋๋ค.
+์ฐ์ , ๋์์ธ ์์คํ
์ ํตํด ๊ฐ๋ฐ์์ ๋์์ด๋ ๊ฐ์ ์ปค๋ฎค๋์ผ์ด์
์ด ๋ช
ํํด์ก๊ณ , ์ผ๊ด๋ ๋์์ธ ์ธ์ด๋ฅผ ๊ณต์ ํ ์ ์์์ต๋๋ค.
+๋ํ, UX/UI์ ์ธ ๊ณ ๋ฏผ์ ๋์ด ๊ฐ๋ฐ์๋ง ์ง์คํ ์ ์์ด ์ข์๋ ๊ฒ ๊ฐ์ต๋๋ค.
+๋์์ธ ์์คํ
์ ํ์ฉํ๋ฉด์ ์๋น์ค๋ฅผ ๋ง๋ค ๋๋ ์ ๋ง ์ฌ๋ฌ UI ์์๋ค์ด ์๋ค๋ ๊ฒ์ ๋๊ผ์ต๋๋ค. ์ด๋ฌํ UI ์์๋ค์ ๋ชจ๋ ๊ต์ฒด ๊ฐ๋ฅ์ฑ์ด ์์ผ๋ฏ๋ก, ์๋น์ค์ ๊ท๋ชจ๊ฐ ์ปค์ง์๋ก, ๋์ค์ UI ์์๋ค์ ๊ต์ฒดํ๊ธฐ ํธํ๊ฒ ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌํ๊ณ ๊ฐ๋
์ฑ ์๋ ์ฝ๋๋ฅผ ์ง๋ ๊ฒ์ด ์ค์ํ๋ค๋ ๊นจ๋ฌ์์ ์ป์์ต๋๋ค.
+
+## ๋์์ด๋์ ์ํตํ๋ฉฐ ๋๋์ ์ ๋ฌด์์ธ๊ฐ์?
+
+ํผ๊ทธ๋ง์ ๋ง๋ค์ด์ฃผ์ UI๋ฅผ ๊ฐ์ง๊ณ ๊ฐ๋ฐํ๋ฉด์, ํผ์ ๊ฐ๋ฐํ๊ณ , UI๊น์ง ๋ง๋ค ๋์๋ ํ๋ฆฌํฐ๊ฐ ๋ค๋ฅธ ๊ฒฐ๊ณผ๋ฌผ์ด ๋์จ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ญ๋๋ค.
+ํนํ, ๋ฉ์์ง๋ฅผ ์
๋ ฅํ ๋๋ ์์ด์ฝ์ด ๋ณด๋ด๊ธฐ ๋ฒํผ์ด๊ณ , ์
๋ ฅํ์ง ์์ ๋๋ ๋ง์ดํฌ ์์ด์ฝ์ธ ๊ฒ์, ๋๋ฌด ๋น์ฐํ UX์ด๋, ์ผ์ ์์์๋ ์ ๊ฒฝ์ฐ์ง ์๊ณ ์ง๋์ณค์๋๋ฐ, ํผ๊ทธ๋ง์์ UI ํ๋ํ๋๋ฅผ ๊ฐ์ ธ๋ค ๊ฐ๋ฐํ๋ฉด์, ์ ๋ง ์ด๋ ์ฌ์ฉ์ ์นํ์ ์ธ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์์ต๋๋ค.
+
+# ๐ก ๋๋ ์
+
+์ฒ์์๋ json์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ค๋๊ฒ, ์๋ก ์
๋ ฅ๋ ๋ํ ๋ด์ญ๋ ์
๋ฐ์ดํธํด์ผ ํ๋ค๊ณ ์๊ฐํด, ์ด๋ป๊ฒ ๊ตฌํํ ์ง ๊ฐ์ด ์กํ์ง ์์,
+์ฐ์ ์ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ useEffect๋ฅผ ์ฌ์ฉํ์ฌ ๋ํ ๋ด์ญ์ ์ ์ฅํ๊ณ ๊ด๋ฆฌํ๋ ๊ฒ์ ๊ตฌํํ์ต๋๋ค.
+json์ ๋๋ฏธ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํด์ผํ๋ ๊ฒ์ ์ด์ด์ ๊ณ์ ๊ณ ๋ฏผํด๋ณด๋ค๊ฐ ๋ฐฑ์๋๋ ๊ตฌํํด์ผ ํ๋๊ฑด๊ฐ?...๋ผ๋ ์๊ฐ์ ํ๋ฉฐ json-server๋ฅผ ํตํด json ๋ฐ์ดํฐ๋ฅผ ์
๋ฐ์ดํธํ๋ ๋ฒ์ ๊ณ์ํด์ ์๋ํ์์ต๋๋ค.
+๊ทธ๋ฐ๋ฐ, ์ด๊ธฐ ๋ฐ์ดํฐ๋ง json ํ์ผ์ ์ ์ฅํ๊ณ , ์ด๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฒ์ด๋ ๊ฑธ ์๊ณ , ์์ฌํ๋ฉฐ ์ด๊ธฐ ๋ฉ์์ง ๋ก๋ ํ๋ ๋ถ๋ถ์ ๋ก์ปฌ ์คํ ๋ฆฌ์ง์์, -> json ํ์ผ์์ json ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ผ๋ก ์์ ํ์ต๋๋ค.
+์ด๋ฅผ ํตํด, json์ ์๋ก ์
๋ ฅ๋ ๋ํ ๋ด์ญ๋ ์
๋ฐ์ดํธํด์ผ ํ๋ ๊ฒ๋ ๊ตฌํํด๋ณด๊ณ ์ถ๋ค๋ ์๊ฐ์ด ๋๋ฌด ์ปค์ ธ์, ์ค๊ฐ๊ณ ์ฌ ์ดํ์ ์ด ๋ถ๋ถ์ ๊ตฌํํ๋๊ฒ ๊ธฐ๋๊ฐ ๋ฉ๋๋ค.
diff --git a/image.png b/image.png
new file mode 100644
index 0000000..b085d62
Binary files /dev/null and b/image.png differ
diff --git a/package-lock.json b/package-lock.json
index c27bbe4..4769908 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,9 +15,13 @@
"@types/node": "^16.18.91",
"@types/react": "^18.2.69",
"@types/react-dom": "^18.2.22",
+ "nodejs": "^0.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-router-dom": "^6.23.0",
"react-scripts": "5.0.1",
+ "recoil": "^0.7.7",
+ "styled-components": "^6.1.8",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
}
@@ -2288,6 +2292,24 @@
"postcss-selector-parser": "^6.0.10"
}
},
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz",
+ "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==",
+ "dependencies": {
+ "@emotion/memoize": "^0.8.1"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
+ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz",
+ "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw=="
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -3338,6 +3360,14 @@
}
}
},
+ "node_modules/@remix-run/router": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz",
+ "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -4290,6 +4320,11 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
},
+ "node_modules/@types/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw=="
+ },
"node_modules/@types/testing-library__jest-dom": {
"version": "5.14.9",
"resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz",
@@ -5750,6 +5785,14 @@
"node": ">= 6"
}
},
+ "node_modules/camelize": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
+ "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/caniuse-api": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
@@ -6182,6 +6225,14 @@
"postcss": "^8.4"
}
},
+ "node_modules/css-color-keywords": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
+ "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/css-declaration-sorter": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz",
@@ -6372,6 +6423,16 @@
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
},
+ "node_modules/css-to-react-native": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
+ "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
+ "dependencies": {
+ "camelize": "^1.0.0",
+ "css-color-keywords": "^1.0.0",
+ "postcss-value-parser": "^4.0.2"
+ }
+ },
"node_modules/css-tree": {
"version": "1.0.0-alpha.37",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
@@ -8861,6 +8922,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/hamt_plus": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
+ "integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="
+ },
"node_modules/handle-thing": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
@@ -12623,6 +12689,11 @@
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
},
+ "node_modules/nodejs": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/nodejs/-/nodejs-0.0.0.tgz",
+ "integrity": "sha512-1V+0HwaB/dhxzidEFc4uJ3k52gLI4B6YBZgJIofjwYCSAkD6CI0me6TDBT2QM2nbGWNxCHcq9/wVynzQYZOhUg=="
+ },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -14864,6 +14935,36 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-router": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.0.tgz",
+ "integrity": "sha512-wPMZ8S2TuPadH0sF5irFGjkNLIcRvOSaEe7v+JER8508dyJumm6XZB1u5kztlX0RVq6AzRVndzqcUh6sFIauzA==",
+ "dependencies": {
+ "@remix-run/router": "1.16.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.0.tgz",
+ "integrity": "sha512-Q9YaSYvubwgbal2c9DJKfx6hTNoBp3iJDsl+Duva/DwxoJH+OTXkxGpql4iUK2sla/8z4RpjAm6EWx1qUDuopQ==",
+ "dependencies": {
+ "@remix-run/router": "1.16.0",
+ "react-router": "6.23.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
"node_modules/react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@@ -14968,6 +15069,25 @@
"node": ">=8.10.0"
}
},
+ "node_modules/recoil": {
+ "version": "0.7.7",
+ "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz",
+ "integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==",
+ "dependencies": {
+ "hamt_plus": "1.0.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.13.1"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
+ }
+ },
"node_modules/recursive-readdir": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz",
@@ -15719,6 +15839,11 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
+ "node_modules/shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -16260,6 +16385,70 @@
"webpack": "^5.0.0"
}
},
+ "node_modules/styled-components": {
+ "version": "6.1.8",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.8.tgz",
+ "integrity": "sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==",
+ "dependencies": {
+ "@emotion/is-prop-valid": "1.2.1",
+ "@emotion/unitless": "0.8.0",
+ "@types/stylis": "4.2.0",
+ "css-to-react-native": "3.2.0",
+ "csstype": "3.1.2",
+ "postcss": "8.4.31",
+ "shallowequal": "1.1.0",
+ "stylis": "4.3.1",
+ "tslib": "2.5.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/styled-components"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0",
+ "react-dom": ">= 16.8.0"
+ }
+ },
+ "node_modules/styled-components/node_modules/csstype": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
+ "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
+ },
+ "node_modules/styled-components/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/styled-components/node_modules/tslib": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
+ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
+ },
"node_modules/stylehacks": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",
@@ -16275,6 +16464,11 @@
"postcss": "^8.2.15"
}
},
+ "node_modules/stylis": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz",
+ "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ=="
+ },
"node_modules/sucrase": {
"version": "3.35.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
diff --git a/package.json b/package.json
index ea335d3..1ee9965 100644
--- a/package.json
+++ b/package.json
@@ -10,9 +10,13 @@
"@types/node": "^16.18.91",
"@types/react": "^18.2.69",
"@types/react-dom": "^18.2.22",
+ "nodejs": "^0.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-router-dom": "^6.23.0",
"react-scripts": "5.0.1",
+ "recoil": "^0.7.7",
+ "styled-components": "^6.1.8",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
diff --git a/public/github_logo.png b/public/github_logo.png
new file mode 100644
index 0000000..d97dc61
Binary files /dev/null and b/public/github_logo.png differ
diff --git a/public/instagram_logo.png b/public/instagram_logo.png
new file mode 100644
index 0000000..611e5af
Binary files /dev/null and b/public/instagram_logo.png differ
diff --git a/public/item/back.png b/public/item/back.png
new file mode 100644
index 0000000..b15dad2
Binary files /dev/null and b/public/item/back.png differ
diff --git a/public/item/chatBottom.png b/public/item/chatBottom.png
new file mode 100644
index 0000000..402dc13
Binary files /dev/null and b/public/item/chatBottom.png differ
diff --git a/public/item/chatFalse.png b/public/item/chatFalse.png
new file mode 100644
index 0000000..957f544
Binary files /dev/null and b/public/item/chatFalse.png differ
diff --git a/public/item/chatHead.png b/public/item/chatHead.png
new file mode 100644
index 0000000..a72c9eb
Binary files /dev/null and b/public/item/chatHead.png differ
diff --git a/public/item/chatTrue.png b/public/item/chatTrue.png
new file mode 100644
index 0000000..cedeae8
Binary files /dev/null and b/public/item/chatTrue.png differ
diff --git a/public/item/micIcon.png b/public/item/micIcon.png
new file mode 100644
index 0000000..d58f6ed
Binary files /dev/null and b/public/item/micIcon.png differ
diff --git a/public/item/profileFalse.png b/public/item/profileFalse.png
new file mode 100644
index 0000000..f3fa692
Binary files /dev/null and b/public/item/profileFalse.png differ
diff --git a/public/item/profileTrue.png b/public/item/profileTrue.png
new file mode 100644
index 0000000..21786f9
Binary files /dev/null and b/public/item/profileTrue.png differ
diff --git a/public/item/profile_mini.png b/public/item/profile_mini.png
new file mode 100644
index 0000000..0942828
Binary files /dev/null and b/public/item/profile_mini.png differ
diff --git a/public/item/profile_mini2.png b/public/item/profile_mini2.png
new file mode 100644
index 0000000..c284156
Binary files /dev/null and b/public/item/profile_mini2.png differ
diff --git a/public/item/searchbar.png b/public/item/searchbar.png
new file mode 100644
index 0000000..64f91ee
Binary files /dev/null and b/public/item/searchbar.png differ
diff --git a/public/item/sendIcon.png b/public/item/sendIcon.png
new file mode 100644
index 0000000..05bec3e
Binary files /dev/null and b/public/item/sendIcon.png differ
diff --git a/public/item/userFalse.png b/public/item/userFalse.png
new file mode 100644
index 0000000..699ce49
Binary files /dev/null and b/public/item/userFalse.png differ
diff --git a/public/item/userTrue.png b/public/item/userTrue.png
new file mode 100644
index 0000000..e58b9a6
Binary files /dev/null and b/public/item/userTrue.png differ
diff --git a/public/item/userbg.png b/public/item/userbg.png
new file mode 100644
index 0000000..8572ea4
Binary files /dev/null and b/public/item/userbg.png differ
diff --git a/src/App.css b/src/App.css
index 74b5e05..e69de29 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,38 +0,0 @@
-.App {
- text-align: center;
-}
-
-.App-logo {
- height: 40vmin;
- pointer-events: none;
-}
-
-@media (prefers-reduced-motion: no-preference) {
- .App-logo {
- animation: App-logo-spin infinite 20s linear;
- }
-}
-
-.App-header {
- background-color: #282c34;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- font-size: calc(10px + 2vmin);
- color: white;
-}
-
-.App-link {
- color: #61dafb;
-}
-
-@keyframes App-logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
-}
diff --git a/src/App.tsx b/src/App.tsx
index bd79c18..aa1ce6c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,9 +1,46 @@
-function App() {
+import React from 'react';
+import { Route, Routes, useLocation } from 'react-router-dom';
+import ChattingRoom from './pages/ChattingRoom';
+import UserList from './pages/userList';
+import ChattingList from './pages/ChattingList';
+import Profile from './pages/Profile';
+import Navbar from './components/Navbar';
+import NavbarHead from './components/NavbarHead';
+import ChatHead from './components/ChatHead';
+import ChatBody from './components/ChatBody';
+import ChatBottom from './components/ChatBottom';
+import GlobalStyle from './style/GlobalStyle';
+import styled from 'styled-components';
+
+
+
+const App = () => {
+ const { pathname } = useLocation();
+ const isChattingRoomPage = pathname.startsWith('/chattingroom');
+
return (
-
-
19๊ธฐ ํ๋ก ํธ์๋ ํ์ดํ
!!! ๋์์ธ๊ณผ ์ฌ์ด์ข๊ฒ ์ง๋ด์~~~
-
+ <>
+
+ {isChattingRoomPage ? (
+
+ ) : (
+ //
+
+ )}
+
+
+
+ } />
+ } />
+ } />
+ } />
+
+
+ {!isChattingRoomPage && }
+
+
+ >
);
-}
+};
export default App;
diff --git a/src/ChattingRoom.js b/src/ChattingRoom.js
new file mode 100644
index 0000000..b0af876
--- /dev/null
+++ b/src/ChattingRoom.js
@@ -0,0 +1,87 @@
+/*import React, { useState, useEffect, useRef } from 'react';
+import ChatHead from './components/ChatHead';
+import ChatBody from './components/ChatBody';
+import ChatBottom from './components/ChatBottom';
+import dummy from './dummy.json';
+
+interface Message {
+ id: number;
+ from: string;
+ content: string;
+ date: string;
+}
+
+interface User {
+ name: string;
+ image: string;
+}
+
+const initialUsers: User[] = [
+ { name: '์ด์ง์ธ', image: '/item/profile_mini.png' },
+ { name: '์ด์์ธ', image: '/item/profile_mini2.png' },
+];
+
+const initialMessages: Message[] = [];
+
+function ChattingRoom() {
+ // ํ์ฌ ์ฌ์ฉ์ ์ํ ๊ด๋ฆฌ
+ const [currentUserIndex, setCurrentUserIndex] = useState(0);
+ // ํ์ฌ ๋ฉ์์ง ๊ธฐ๋กฅ ์ํ ๊ด๋ฆฌ
+ const [messages, setMessages] = useState(() => {
+ // ๋ก์ปฌ ์คํ ๋ฆฌ์ง์์ ์ด๊ธฐ ๋ฉ์์ง ๋ก๋
+ // const savedMessages = localStorage.getItem('messages');
+ const savedMessages = dummy;
+ //return savedMessages ? JSON.parse(savedMessages) : [];
+ return savedMessages;
+ });
+ const currentUser = initialUsers[currentUserIndex];
+ const otherUserIndex = (currentUserIndex + 1) % initialUsers.length;
+ const otherUser = initialUsers[otherUserIndex];
+
+ // ๋ฉ์ธ์ง ๋ชฉ๋ก ๋์ผ๋ก ์คํฌ๋กค
+ const messagesEndRef = useRef(null);
+
+ useEffect(() => {
+ // ๋ฉ์์ง ๋ฐ์ดํฐ๋ฅผ localStorage์ ์ ์ฅ
+ localStorage.setItem('messages', JSON.stringify(messages));
+
+ //dummy.message = JSON.stringify(messages);
+ //console.log(dummy.message);
+ }, [messages]);
+
+ useEffect(() => {
+ // ๋ฉ์์ง๊ฐ ์ถ๊ฐ๋ ๋๋ง๋ค ์คํฌ๋กค์ ํ๋จ์ผ๋ก ์ด๋
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ }, [messages]);
+
+ const sendMessage = (messageContent: string) => {
+ const newMessage: Message = {
+ id: Date.now(),
+ from: currentUser.name,
+ content: messageContent,
+ date: new Date().toISOString(), // ISO ํ์์ ๋ ์ง ๋ฌธ์์ด ์ฌ์ฉ
+ };
+
+ setMessages((prevMessages) => [...prevMessages, newMessage]);
+ };
+
+ const toggleUser = () => {
+ setCurrentUserIndex((currentIndex) => (currentIndex === 0 ? 1 : 0));
+ };
+
+ return (
+
+ );
+}
+
+export default ChattingRoom;
+*/
diff --git a/src/components/ChatBody.tsx b/src/components/ChatBody.tsx
new file mode 100644
index 0000000..758b262
--- /dev/null
+++ b/src/components/ChatBody.tsx
@@ -0,0 +1,81 @@
+
+import React from 'react';
+import {
+ ChatBodyContainer,
+ TimeDisplay,
+ DateSeparator,
+ ChatMessageBox,
+ UserProfileImage
+
+} from '../style/ChatBodyStyle';
+
+interface Message {
+ id: number;
+ from: string;
+ content: string;
+ date: string;
+}
+
+interface ChatBodyProps {
+ messages: Message[];
+ currentUser: string;
+ userImage: string;
+}
+
+const formatDate = (isoDateString: string) => {
+ const date = new Date(isoDateString);
+ return `${date.getFullYear()}๋
${date.getMonth() + 1}์ ${date.getDate()}์ผ`;
+};
+
+const formatTime = (isoDateString: string) => {
+ const date = new Date(isoDateString);
+ return `${date.getHours().toString().padStart(2, '0')}:${date
+ .getMinutes()
+ .toString()
+ .padStart(2, '0')}`;
+};
+
+const ChatBody: React.FC = ({ messages,userImage, currentUser}) => {
+ let lastDate = '';
+ let lastMinute = '';
+
+
+ return (
+
+ {messages.map((message, index) => {
+ const messageDate = formatDate(message.date);
+ const messageMinute = formatTime(message.date);
+ let displayDateSeparator = false;
+ let displayMinuteSeparator = false;
+
+ if (messageDate !== lastDate) {
+ lastDate = messageDate;
+ displayDateSeparator = true;
+ }
+
+ if (messageMinute !== lastMinute) {
+ lastMinute = messageMinute;
+ displayMinuteSeparator = true;
+ }
+
+ return (
+
+ {displayDateSeparator && (
+ {formatDate(message.date)}
+ )}
+ {displayMinuteSeparator && (
+ {formatTime(message.date)}
+ )}
+
+
+ {message.content}
+
+
+
+ );
+ })}
+
+ );
+};
+
+export default ChatBody;
\ No newline at end of file
diff --git a/src/components/ChatBottom.tsx b/src/components/ChatBottom.tsx
new file mode 100644
index 0000000..1196a9f
--- /dev/null
+++ b/src/components/ChatBottom.tsx
@@ -0,0 +1,51 @@
+import React, { useState, KeyboardEvent, ChangeEvent } from 'react';
+
+import {
+ ChatBottomContainer,
+ ChatInput,
+ SendButton,
+} from '../style/ChatBottomStyle';
+
+const sendIcon = '/item/sendIcon.png';
+const micIcon = '/item/micIcon.png';
+
+interface ChatBottomProps {
+ onSendMessage: (messageContent: string) => void;
+}
+
+const ChatBottom: React.FC = ({ onSendMessage }) => {
+ const [inputValue, setInputValue] = useState('');
+
+ const handleKeyPress = (event: KeyboardEvent) => {
+ if (event.key === 'Enter' && inputValue.trim()) {
+ sendMessage();
+ }
+ };
+
+ const sendMessage = () => {
+ if (inputValue.trim()) {
+ onSendMessage(inputValue.trim());
+ setInputValue('');
+ }
+ };
+
+ // ์
๋ ฅ๋ ํ
์คํธ์ ๋ฐ๋ผ ์ด๋ฏธ์ง ๊ฒฐ์
+ const buttonImage = inputValue.trim() ? sendIcon : micIcon;
+
+ return (
+
+ ) =>
+ setInputValue(e.target.value)
+ }
+ onKeyPress={handleKeyPress}
+ />
+
+
+ );
+};
+
+export default ChatBottom;
diff --git a/src/components/ChatHead.tsx b/src/components/ChatHead.tsx
new file mode 100644
index 0000000..65a9ce1
--- /dev/null
+++ b/src/components/ChatHead.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import {
+ ChatHeadContainer,
+ UserImage,
+ UserName,
+ SendButton,
+} from '../style/ChatHeadStyle';
+import { useNavigate } from 'react-router-dom';
+
+interface User {
+ name: string;
+ image: string;
+}
+
+interface ChatHeadProps {
+ user: User;
+ onUserClick: () => void;
+}
+
+const buttonImage = '/item/back.png';
+
+const ChatHead: React.FC = ({ user, onUserClick }) => {
+ const navigate = useNavigate();
+
+ const handleUserClick = () => {
+ navigate(`/chattinglist`);
+ };
+
+ return (
+
+
+
+ {user.name}
+
+ );
+};
+
+export default ChatHead;
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
new file mode 100644
index 0000000..2401871
--- /dev/null
+++ b/src/components/Navbar.tsx
@@ -0,0 +1,81 @@
+// src/components/Navbar.tsx
+
+import React from 'react';
+import { Link, useLocation } from 'react-router-dom';
+import styled from 'styled-components';
+
+const NavbarContainer = styled.div`
+ display: flex;
+ justify-content: space-around;
+ position: fixed;
+ font-size: 20px;
+ width: 375px;
+ bottom: 300px;
+ height: 50px;
+ background: #f0f0f0;
+ padding-top: 30px;
+`;
+
+const NavbarLink = styled(Link)`
+ text-decoration: none;
+ color: black;
+`;
+
+const NavButton = styled.img`
+ position: fixed;
+ bottom: 320px;
+ margin-left: -35px;
+ /* width: 30px;
+height: 30px;
+margin-right: 10px; */
+`;
+
+const Navbar = () => {
+ const { pathname } = useLocation();
+ let userPageButton;
+ let chatPageButton;
+ let profilePageButton;
+
+ switch (pathname) {
+ case '/chattingroom':
+ userPageButton = '/item/back.png';
+ chatPageButton = '/item/back.png';
+ profilePageButton = '/item/profileFalse.png';
+ break;
+ case '/userlist':
+ userPageButton = '/item/userTrue.png';
+ chatPageButton = '/item/chatFalse.png';
+ profilePageButton = '/item/profileFalse.png';
+ break;
+ case '/chattinglist':
+ userPageButton = '/item/userFalse.png';
+ chatPageButton = '/item/chatTrue.png';
+ profilePageButton = '/item/profileFalse.png';
+ break;
+ case '/profile':
+ userPageButton = '/item/userFalse.png';
+ chatPageButton = '/item/chatFalse.png';
+ profilePageButton = '/item/profileTrue.png';
+ break;
+ default:
+ userPageButton = '/item/userFalse.png';
+ chatPageButton = '/item/chatFalse.png';
+ profilePageButton = '/item/profileFalse.png';
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Navbar;
diff --git a/src/components/NavbarHead.tsx b/src/components/NavbarHead.tsx
new file mode 100644
index 0000000..653d5a8
--- /dev/null
+++ b/src/components/NavbarHead.tsx
@@ -0,0 +1,57 @@
+// src/components/NavbarHead.tsx
+
+import React from 'react';
+import { useLocation } from 'react-router-dom';
+import styled from 'styled-components';
+
+const NavbarHeadContainer = styled.div`
+ display: flex;
+ justify-content: flex-start;
+ position: fixed;
+ font-size: 20px;
+ padding-left: 15px;
+ width: 375px;
+ top: -33px;
+ height: 80px;
+ background: #fffff;
+ padding-top: 30px;
+
+
+`;
+const NavbarHeadTexts=styled.div`
+background: #fffff;
+padding-top: 30px;
+font-family: 'Noto Sans KR';
+font-size: 20px;
+font-style: normal;
+font-weight: 500;
+`;
+
+const NavbarHead = () => {
+ const { pathname } = useLocation();
+ let navText;
+
+ switch (pathname) {
+ case '/chattingroom':
+ navText = '';
+ break;
+ case '/userlist':
+ navText = '์น๊ตฌ ๋ชฉ๋ก';
+ break;
+ case '/chattinglist':
+ navText = '๋ํ';
+ break;
+ case '/profile':
+ navText = '๋ด ํ๋กํ';
+ break;
+ default:
+ navText = '';
+ }
+
+ return(
+
+ {navText}
+ );
+};
+
+export default NavbarHead;
diff --git a/src/dummy.json b/src/dummy.json
new file mode 100644
index 0000000..782935e
--- /dev/null
+++ b/src/dummy.json
@@ -0,0 +1,35 @@
+
+
+[
+ {
+ "id": 1711705694689,
+ "from": "์ด์ง์ธ",
+ "content": "๊ตญ๊ฐ๋ ๋์์ฐ๋ฌผ์ ์๊ธ๊ท ํ๊ณผ ์ ํต๊ตฌ์กฐ์ ๊ฐ์ ์ ๋
ธ๋ ฅํ์ฌ",
+ "date": "2024-03-29T09:48:14.689Z"
+ },
+ {
+ "id": 1711705702873,
+ "from": "์ด์์ธ",
+ "content": "๊ฐ๊ฒฉ์์ ์ ๋๋ชจํจ์ผ๋ก์จ ๋ยท์ด๋ฏผ์ ์ด์ต์ ๋ณดํธํ๋ค.",
+ "date": "2024-03-29T09:48:22.873Z"
+ },
+ {
+ "id": 1711705711651,
+ "from": "์ด์ง์ธ",
+ "content": "์ ๋น์ ๊ทธ ๋ชฉ์ ยท์กฐ์ง๊ณผ ํ๋์ด ๋ฏผ์ฃผ์ ์ด์ด์ผ ํ๋ฉฐ,",
+ "date": "2024-03-29T09:48:31.651Z"
+ },
+ {
+ "id": 1711705718508,
+ "from": "์ด์ง์ธ",
+ "content": "๊ตญ๋ฏผ์ ์ ์น์ ์์ฌํ์ฑ์ ์ฐธ์ฌ",
+ "date": "2024-03-29T09:48:38.508Z"
+ },
+ {
+ "id": 1711705726747,
+ "from": "์ด์์ธ",
+ "content": "ํ๋๋ฐ ํ์ํ ์กฐ์ง์ ๊ฐ์ ธ์ผ ํ๋ค.",
+ "date": "2024-03-29T09:48:46.747Z"
+ }
+
+]
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index ec2585e..e69de29 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,13 +0,0 @@
-body {
- margin: 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;
-}
-
-code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
- monospace;
-}
diff --git a/src/index.tsx b/src/index.tsx
index d10be77..6bda164 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,13 +1,19 @@
+// src/index.tsx
import React from 'react';
-import ReactDOM from 'react-dom/client';
-import './index.css';
+import ReactDOM from 'react-dom';
import App from './App';
+import { BrowserRouter as Router } from 'react-router-dom';
+import { RecoilRoot } from 'recoil';
+import './index.css';
-const root = ReactDOM.createRoot(
- document.getElementById('root') as HTMLElement
-);
-root.render(
+ReactDOM.render(
-
-
+
+
+
+
+
+ ,
+
+ document.getElementById('root')
);
diff --git a/src/pages/ChattingList.tsx b/src/pages/ChattingList.tsx
new file mode 100644
index 0000000..96c11ea
--- /dev/null
+++ b/src/pages/ChattingList.tsx
@@ -0,0 +1,132 @@
+import React, { useState } from 'react';
+import { Link } from 'react-router-dom';
+import { useRecoilValue } from 'recoil';
+import { userListState } from '../state/userState';
+import styled from 'styled-components';
+
+const ChatListContainer = styled.div`
+ padding: 20px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 300px;
+
+`;
+
+const ChatItem = styled(Link)`
+ width: 100%;
+ padding: 10px;
+ margin: 5px 0;
+ background-color: #fffff;
+ color: black;
+ text-decoration: none;
+ border-radius: 5px;
+ white-space: pre-line;
+ /*background-image: url('/path/to/background-image.jpg');*/
+ display: flex;
+ align-items: center;
+
+
+ &:hover {
+ background-color: #fffff;
+ }
+`;
+
+const ProfileImage = styled.img`
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ margin-right: 20px;
+`;
+
+const ChatDetails = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ align-items: flex-start;
+ width: 100%;
+
+`;
+
+const LastMessage = styled.div`
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+ font-size: 14px;
+ color: grey;
+`;
+
+const MessageTime = styled.span`
+ font-size: 12px;
+ color: darkgray;
+ margin-left: auto;
+ margin-right: 5px;
+`;
+
+const SearchInput = styled.input`
+ background-image: url('/item/searchbar.png');
+ background-repeat: no-repeat;
+ width: 260px;
+ height: 21px;
+ padding: 10px 43px;
+ margin-bottom: 0px;
+ margin-left: 5px;
+ border: none;
+`;
+
+const getLastMessageAndTime = (userId: string) => {
+ const messagesString = localStorage.getItem(`messages_${userId}`);
+ if (messagesString) {
+ const messages = JSON.parse(messagesString);
+ if (messages.length > 0) {
+ const lastMessage = messages[messages.length - 1];
+ const messageTime = new Date(lastMessage.date).toLocaleTimeString('en-US', {
+ hour: '2-digit',
+ minute: '2-digit',
+ hour12: true
+ });
+ return { content: lastMessage.content, time: messageTime };
+ }
+ }
+ return { content: '๋ํ๋ฅผ ์์ํ์ง ์์์ต๋๋ค.', time: '' };
+};
+
+const ChattingList = () => {
+ const userList = useRecoilValue(userListState);
+ const [searchTerm, setSearchTerm] = useState('');
+
+ const filteredUsers = userList
+ .filter((user) =>
+ user.name.toLowerCase().includes(searchTerm.toLowerCase())
+ )
+ .slice(1); // ์ฒซ ๋ฒ์งธ ์ ์ ๋ฅผ ์ ์ธํ๊ณ ํํฐ๋ง
+
+ return (
+ <>
+ setSearchTerm(e.target.value)}
+ />
+
+ {filteredUsers.map((user) => (
+
+
+
+ {user.name}
+
+ {getLastMessageAndTime(user.id).content}
+ {getLastMessageAndTime(user.id).time}
+
+
+
+ ))}
+
+ >
+ );
+};
+
+export default ChattingList;
diff --git a/src/pages/ChattingRoom.tsx b/src/pages/ChattingRoom.tsx
new file mode 100644
index 0000000..38b217c
--- /dev/null
+++ b/src/pages/ChattingRoom.tsx
@@ -0,0 +1,120 @@
+import React, { useEffect, useId, useState, useRef } from 'react';
+import { useParams } from 'react-router-dom';
+import { useRecoilValue } from 'recoil';
+import { userListState } from '../state/userState';
+import { userProfileState } from '../state/userProfileState';
+import { messagesState } from '../state/messageState';
+import ChatHead from '../components/ChatHead';
+import ChatBody from '../components/ChatBody';
+import ChatBottom from '../components/ChatBottom';
+import { ChatRoomContainer } from '../style/ChattingRoomStyle';
+import styled from 'styled-components';
+
+interface Message {
+ id: number;
+ from: string;
+ to: string;
+ content: string;
+ date: string;
+}
+
+const ChatBottomMask = styled.div`
+ position: fixed;
+ bottom: 0;
+ width: 100%; // ๋ทฐํฌํธ ์ ์ฒด ๋๋น ์ฌ์ฉ
+ height: 360px; // ChatBottom์ ๋์ด์ ๋์ผํ๊ฒ ์ค์
+ background-color: white; // ํฐ์ ๋ฐฐ๊ฒฝ
+ z-index: 1; // ChatBottom๋ณด๋ค ๋ฎ์ z-index ์ค์
+`;
+
+const ChattingRoom = () => {
+ const { userId } = useParams<{ userId: string }>();
+ console.log(useId);
+
+ const messagesStateValue = useRecoilValue(messagesState);
+ const [messages, setMessages] = useState([]);
+
+ const messagesEndRef = useRef(null);
+
+ useEffect(() => {
+ // ๋ก์ปฌ ์คํ ๋ฆฌ์ง์์ ์ด์ ๋ฉ์์ง ๊ฐ์ ธ์ค๊ธฐ
+ const storedMessages = localStorage.getItem(`messages_${userId}`);
+ if (storedMessages) {
+ // ์ด์ ๋ฉ์์ง๊ฐ ์์ผ๋ฉด ํ์ฑํ์ฌ ์ํ์ ์ค์
+ setMessages(JSON.parse(storedMessages));
+ }
+ }, [userId]); // userId๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ดํํธ ์ฌ์คํ
+
+ const userList = useRecoilValue(userListState);
+ // ๋ํ ์๋๋ฐฉ
+ const otherUser = userList.find((user) => user.id === userId);
+
+ const otherUserIndex = Number(otherUser?.id);
+
+ const [currentUserIndex, setCurrentUserIndex] = useState(0);
+ const [counterUserIndex, setCounterUserIndex] = useState(otherUserIndex);
+
+ let currentUser = userList[currentUserIndex];
+ let counterUser = userList[counterUserIndex];
+
+
+ useEffect(() => {
+ // ๋ฉ์์ง๊ฐ ์ถ๊ฐ๋ ๋๋ง๋ค ์คํฌ๋กค์ ํ๋จ์ผ๋ก ์ด๋
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ }, [messages, currentUserIndex, counterUserIndex]);
+
+ // ๋ฉ์ธ์ง ๋ชฉ๋ก ๋์ผ๋ก ์คํฌ๋กค
+
+ // ์ ์ ๊ด๋ฆฌ
+ // ํ์ฌ ์ฌ์ฉ์ ์ํ ๊ด๋ฆฌ
+
+
+ console.log(currentUser);
+
+ //console.log(otherUserIndex);
+
+ const toggleUser = () => {
+ setCurrentUserIndex(counterUserIndex);
+ setCounterUserIndex(currentUserIndex);
+ };
+
+ const sendMessage = (content: string, from: string, to: string) => {
+ const newMessage: Message = {
+ id: Date.now(),
+ from,
+ to,
+ content,
+ date: new Date().toISOString(),
+ };
+
+ // ์ด์ ๋ฉ์์ง ๋ฐฐ์ด๊ณผ ์๋ก์ด ๋ฉ์์ง๋ฅผ ํฉ์นจ
+ const updatedMessages = [...messages, newMessage];
+ setMessages(updatedMessages);
+
+ // localStorage์ ์
๋ฐ์ดํธ๋ ๋ฉ์์ง ์ ์ฅ
+ localStorage.setItem(`messages_${userId}`, JSON.stringify(updatedMessages));
+ };
+
+ return (
+ <>
+
+
+
+
+
+ sendMessage(content, currentUser?.id ?? '', counterUser?.id ?? '')
+ }
+ />
+ >
+ );
+};
+
+export default ChattingRoom;
diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx
new file mode 100644
index 0000000..d501fcc
--- /dev/null
+++ b/src/pages/Profile.tsx
@@ -0,0 +1,127 @@
+import React from 'react';
+import { useRecoilState } from 'recoil';
+import { userProfileState } from '../state/userProfileState';
+import styled from 'styled-components';
+
+interface Profile {
+ name: string;
+ phone: string;
+ image: string;
+ github: string;
+ instagram: string;
+}
+
+const ProfileContainer = styled.div`
+margin-left:10px;
+position:fixed;
+widfht:375px;
+display: flex;
+flex-direction: column;
+align-items: center;
+/* padding: 20px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;*/
+
+`;
+
+const ProfileImage = styled.img`
+ width: 100px;
+ height: 100px;
+ border-radius: 50%;
+ margin-bottom: 20px;
+`;
+
+const ProfileFieldName = styled.div`
+ margin: 10px 0;
+ text-align: center;
+font-family: "Noto Sans KR";
+font-size: 24px;
+font-style: normal;
+font-weight: 500;
+line-height: normal;
+letter-spacing: 0.33px;
+`;
+
+const ProfileFieldPhone = styled.div`
+color: var(--gray, #8D8D8F);
+text-align: center;
+font-family: "Noto Sans KR";
+font-size: 15px;
+font-style: normal;
+font-weight: 400;
+line-height: normal;
+letter-spacing: -0.2px;
+`;
+
+const Input = styled.input`
+ padding: 10px;
+ margin-top: 5px;
+ width: 200px;
+ border-radius: 5px;
+ border: 1px solid #ccc;
+`;
+
+const Button = styled.button`
+ padding: 10px 20px;
+ margin-top: 20px;
+ background-color: #4caf50;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+`;
+
+// ๋ฐ๋ก๊ฐ๊ธฐ ๋งํฌ๋ฅผ ๊ฐ์ธ๋ ์คํ์ผ๋ ์ปดํฌ๋ํธ
+const ShortcutLink = styled.a`
+ display: flex;
+ align-items: center;
+ text-decoration: none;
+ color: #333;
+ margin-top: 10px;
+`;
+
+// ๋ก๊ณ ์ด๋ฏธ์ง ์คํ์ผ๋ง
+const InstagramLogo = styled.img`
+ /* width: 30px;
+height: 30px;
+margin-right: 10px; */
+`;
+
+const GitHubLogo = styled.img`
+ /* width: 30px;
+height: 30px;
+margin-right: 10px; */
+`;
+
+const Profile = () => {
+ const [profile, setProfile] = useRecoilState(userProfileState);
+
+ const handleChange = (event: React.ChangeEvent) => {
+ const { name, value } = event.target;
+ setProfile((prev) => ({ ...prev, [name]: value }));
+ };
+
+ return (
+
+
+
+ {profile.name}
+
+
+ {profile.phone}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Profile;
diff --git a/src/pages/userList.tsx b/src/pages/userList.tsx
new file mode 100644
index 0000000..7483d82
--- /dev/null
+++ b/src/pages/userList.tsx
@@ -0,0 +1,73 @@
+import React, { useState } from 'react';
+import { useRecoilValue } from 'recoil';
+import { userListState } from '../state/userState';
+import styled from 'styled-components';
+
+interface User {
+ id: string;
+ name: string;
+ profileImage: string; // ํ๋กํ ์ด๋ฏธ์ง URL์ ์ถ๊ฐ
+}
+
+// ์คํ์ผ ์ปดํฌ๋ํธ
+const UserContainer = styled.li`
+ background-image: url('/item/userbg.png'); // ์ฌ๊ธฐ์ ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง URL์ ์ค์
+ background-size: cover;
+ display: flex;
+ align-items: center;
+ padding: 5px 0px 5px 0px;
+ margin-bottom: 11px;
+ width:300px;
+ margin-left:-10px;
+
+`;
+
+const ProfileImage = styled.img`
+ width: 45px;
+ height: 45px;
+ border-radius: 50%;
+ margin-right: 20px;
+ margin-left:-20px;
+`;
+
+const SearchInput = styled.input`
+ background-image: url('/item/searchbar.png');
+ background-repeat: no-repeat;
+ width: 260px;
+ height: 21px;
+ padding: 10px 43px;
+ margin-bottom: 0px;
+ margin-left: 5px;
+ border: none;
+`;
+
+const UserList = () => {
+ const users = useRecoilValue(userListState);
+ const [searchTerm, setSearchTerm] = useState('');
+
+ const filteredUsers = users
+ .filter((user) =>
+ user.name.toLowerCase().includes(searchTerm.toLowerCase())
+ )
+ .slice(1); // ์ฒซ ๋ฒ์งธ ์ ์ ๋ฅผ ์ ์ธํ๊ณ ํํฐ๋ง
+
+ return (
+ <>
+ setSearchTerm(e.target.value)}
+ />
+
+ {filteredUsers.map((user) => (
+
+
+ {user.name}
+
+ ))}
+
+ >
+ );
+};
+
+export default UserList;
diff --git a/src/state/chatListState.ts b/src/state/chatListState.ts
new file mode 100644
index 0000000..094f6ad
--- /dev/null
+++ b/src/state/chatListState.ts
@@ -0,0 +1,19 @@
+// src/state/chatListState.ts
+
+import { atom } from 'recoil';
+
+interface ChatRoom {
+ id: string;
+ partner: string;
+ recentMsg: number;
+}
+
+export const chatListState = atom({
+ key: 'chatListState',
+ default: [
+ { id: 'room1', partner: '๊น๋ชจ์จ', recentMsg: 3 },
+ { id: 'room2', partner: '๋ฐ๋ชจ์จ', recentMsg: 1 },
+ { id: 'room3', partner: '์ต๋ชจ์จ', recentMsg: 1 },
+ // ์ถ๊ฐ ๋ํ๋ฐฉ ์ ๋ณด
+ ],
+});
\ No newline at end of file
diff --git a/src/state/messageState.ts b/src/state/messageState.ts
new file mode 100644
index 0000000..1a79adc
--- /dev/null
+++ b/src/state/messageState.ts
@@ -0,0 +1,16 @@
+// src/state/messageState.ts
+
+import { atom } from 'recoil';
+
+interface Message {
+ id: number;
+ from: string;
+ to: string;
+ content: string;
+ date: string;
+}
+
+export const messagesState = atom<{ [key: string]: Message[] } | undefined>({
+ key: 'messagesState',
+ default: undefined,
+});
diff --git a/src/state/userProfileState.ts b/src/state/userProfileState.ts
new file mode 100644
index 0000000..d1c263e
--- /dev/null
+++ b/src/state/userProfileState.ts
@@ -0,0 +1,14 @@
+// src/state/userProfileState.ts
+
+import { atom } from 'recoil';
+
+export const userProfileState = atom({
+ key: 'userProfileState',
+ default: {
+ name: '์ด์์ธ',
+ phone: '82) 10-1234-5678',
+ image: '/item/profile_mini2.png',
+ github: 'https://github.com/jinnyleeis',
+ instagram: 'https://www.instagram.com/',
+ },
+});
diff --git a/src/state/userState.ts b/src/state/userState.ts
new file mode 100644
index 0000000..a0fd3dc
--- /dev/null
+++ b/src/state/userState.ts
@@ -0,0 +1,60 @@
+// src/state/userState.ts
+
+import { atom } from 'recoil';
+
+export interface Message {
+ id: number;
+ from: string;
+ to: string;
+ content: string;
+ date: string;
+}
+
+interface User {
+ id: string;
+ name: string;
+ image: string;
+ chatHistory?: Message[]; // ๋ํ ๋ด์ญ ์์
+}
+
+export const userListState = atom({
+ key: 'userListState',
+ default: [
+ // 0๋ฒ์ current user - ๋ฉ์ ์ ์ฑ ์ฃผ์ธ์ผ๋ก ์ค์
+ {
+ id: '0',
+ name: '์ด์์ธ',
+ image: '/item/profile_mini2.png',
+ chatHistory: [],
+ },
+ {
+ id: '1',
+ name: '๊น๋ชจ์จ',
+ image: '/item/profile_mini.png',
+ chatHistory: [],
+ },
+ {
+ id: '2',
+ name: '๋ฐ๋ชจ์จ',
+ image: '/item/profile_mini.png',
+ chatHistory: [],
+ },
+ {
+ id: '3',
+ name: '์ต๋ชจ์จ',
+ image: '/item/profile_mini.png',
+ chatHistory: [],
+ },
+ {
+ id: '4',
+ name: '์ต๋ชจ์จ',
+ image: '/item/profile_mini.png',
+ chatHistory: [],
+ },
+ ],
+});
+
+export const currentUserState = atom({
+ key: 'currentUserState',
+ default: { id: '0', name: '๊น๋ชจ์จ', image: '', chatHistory: [] },
+});
diff --git a/src/style/ChatBodyStyle.tsx b/src/style/ChatBodyStyle.tsx
new file mode 100644
index 0000000..b583baa
--- /dev/null
+++ b/src/style/ChatBodyStyle.tsx
@@ -0,0 +1,85 @@
+import styled, { css } from 'styled-components';
+
+export const ChatMessageBox = styled.div<{ isCurrentUser: boolean }>`
+ display: inline-block;
+ padding: 8px 9px 8px 9px;
+
+ width: 196px;
+ margin-left: 30px;
+ margin-bottom: 5px;
+ margin-top: 5px;
+ border-radius: 8px;
+ background-color: #ededed;
+ align-self: flex-start;
+ border-radius: 20px 20px 20px 5px;
+
+ color: var(--dark-gray, #4e5058);
+ font-family: 'Noto Sans KR';
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 150%; /* 24px */
+ letter-spacing: -0.41px;
+
+ ${(props) =>
+ props.isCurrentUser &&
+ css`
+ border-radius: 20px 20px 5px 20px;
+ background-color: #dcf7c5;
+ align-self: flex-end;
+ margin-right: 5px;
+ `}
+
+ ${(props) => !props.isCurrentUser && css``}
+`;
+
+export const ChatBodyContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ margin-top: 96px;
+
+ margin-bottom: 400px;
+ width: 375px;
+`;
+
+export const UserProfileImage = styled.img<{ isCurrentUser: boolean }>`
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ margin-right: 0px; /* ๋ฉ์์ง ๋ฐ์ค์์ ๊ฐ๊ฒฉ */
+ margin-left: 5px; /* ๋ฉ์์ง ๋ฐ์ค์์ ๊ฐ๊ฒฉ */
+ display: inline-block;
+ margin-top: -10px;
+ position: relative;
+ z-index: -5;
+
+ ${(props) =>
+ props.isCurrentUser &&
+ css`
+ display: none;
+ `}
+`;
+
+export const DateSeparator = styled.div`
+ width: 122px;
+ height: 21px;
+ flex-shrink: 0;
+ border-radius: 4px;
+ background: var(--medium-gray, #d9d9d9);
+ color: #000;
+ text-align: center;
+ margin: 10px auto; // ์ ์ค์ ์ ๋ ฌ
+ font-family: 'Noto Sans KR';
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: 180%; /* 18px */
+`;
+
+export const TimeDisplay = styled.span`
+ display: block; // ๋ฉ์์ง ๋ฐ์ค ์์ ํ์
+ text-align: center; // ์ ์ค์ ์ ๋ ฌ
+ margin-bottom: 5px;
+ margin-top: 5px;
+ font-size: 12px;
+`;
diff --git a/src/style/ChatBottomStyle.tsx b/src/style/ChatBottomStyle.tsx
new file mode 100644
index 0000000..29e2787
--- /dev/null
+++ b/src/style/ChatBottomStyle.tsx
@@ -0,0 +1,39 @@
+import styled from 'styled-components';
+
+// ChatBottomContainer๋ฅผ div๋ก ๋ณ๊ฒฝํ์ฌ ์ปจํ
์ด๋๋ก ์ฌ์ฉ
+export const ChatBottomContainer = styled.div`
+ position: fixed;
+ bottom: 280px;
+ width: 375px;
+ height: 80px;
+ flex-shrink: 0;
+
+ background-image: url('/item/chatBottom.png');
+ background-size: cover;
+ z-index: 2;
+`;
+
+export const ChatInput = styled.input`
+ width: 275px;
+ height: 32px;
+ flex-shrink: 0;
+ font-size: 14px;
+ margin-left: 11%;
+ border-radius: 16px;
+ border: 0.5px solid #8e8e93;
+ padding: 0px 0px 0px 7px;
+ opacity: 0.4496;
+ margin-top: 8px;
+ background: #fff;
+`;
+
+export const SendButton = styled.img`
+ width: 30px;
+ height: 30px;
+ flex-shrink: 0;
+ cursor: pointer;
+ border: none;
+ position: absolute;
+ margin-top: 9px;
+ margin-left: 4px;
+`;
diff --git a/src/style/ChatHeadStyle.tsx b/src/style/ChatHeadStyle.tsx
new file mode 100644
index 0000000..a0c4346
--- /dev/null
+++ b/src/style/ChatHeadStyle.tsx
@@ -0,0 +1,44 @@
+import styled from 'styled-components';
+
+export const ChatHeadContainer = styled.div`
+ width: 375px;
+ height: 96px;
+ position: fixed;
+ top: 0px;
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+
+ flex-shrink: 0;
+ background-image: url('/item/chatHead.png');
+ background-size: cover;
+
+`;
+
+export const UserImage = styled.img`
+ margin-left: 45px;
+ margin-right: 10px;
+ margin-top: 30px;
+ border-radius: 50%;
+
+ width: 10px;
+ height: 40px;
+`;
+
+export const UserName = styled.h2`
+ font-size: 16px;
+ margin-top: 47px;
+ font-style: normal;
+ font-weight: 500;
+`;
+
+export const SendButton = styled.img`
+ width: 25px;
+ height: 25px;
+ flex-shrink: 0;
+ cursor: pointer;
+ border: none;
+ position: absolute;
+ margin-top: 30px;
+ margin-left: 10px;
+`;
diff --git a/src/style/ChattingRoomStyle.tsx b/src/style/ChattingRoomStyle.tsx
new file mode 100644
index 0000000..5eb6497
--- /dev/null
+++ b/src/style/ChattingRoomStyle.tsx
@@ -0,0 +1,10 @@
+import styled from 'styled-components';
+
+export const ChatRoomContainer = styled.div`
+ width: 375px;
+ height: 212px;
+ background: var(--white, #fff);
+ display: flex;
+
+
+`;
diff --git a/src/style/GlobalStyle.tsx b/src/style/GlobalStyle.tsx
new file mode 100644
index 0000000..bc89290
--- /dev/null
+++ b/src/style/GlobalStyle.tsx
@@ -0,0 +1,10 @@
+import { createGlobalStyle } from 'styled-components';
+
+const GlobalStyle = createGlobalStyle`
+
+*{font-family: "Noto Sans KR"};
+
+
+`;
+
+export default GlobalStyle;
diff --git a/tsconfig.json b/tsconfig.json
index a273b0c..7c03434 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,26 +1,20 @@
{
- "compilerOptions": {
- "target": "es5",
- "lib": [
- "dom",
- "dom.iterable",
- "esnext"
- ],
- "allowJs": true,
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "noFallthroughCasesInSwitch": true,
- "module": "esnext",
- "moduleResolution": "node",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "noEmit": true,
- "jsx": "react-jsx"
- },
- "include": [
- "src"
- ]
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ // "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": ["src"]
}