- ํ๋ก์ ํธ ๊ธฐ๊ฐ : 2023.10.12 ~ 2023.11.09
- ๋ฐฐํฌ URL ๐
ID: villain@test.com
PW: 123123
-
SUBONE์ ์์ธ ์งํ์ฒ 1ํธ์ ์น๊ฐ๋ค์ ์ํ ๋ง์ถคํ ์ปค๋ฎค๋ํฐ ์๋น์ค ์ ๋๋ค. ์ด ์ฑ์ ํตํด ์ด์ฉ์๋ค์ ์์ฝ๊ฒ ๊ฒ์๊ธ์ ์ฌ๋ฆฌ๊ณ , ์ปค๋ฎค๋ํฐ ํ๋์ ์ฆ๊ธธ ์ ์์ต๋๋ค. ๋ํ ์งํ์ฒ ์ ์ด์ฉํ ํ๋ฐฐ ๋ฐฐ์ก ์๋น์ค๋ ์ ๊ณตํ๊ณ ์์ต๋๋ค.
-
๊ธฐ์กด์ ์ฑ๋ค์ด ์ฌ์ฉ์์๊ฒ ๋ง์ ์ ๋ ฅ์ ์๊ตฌํ๋ฉฐ, ์์น์ ์๊ฐ ์ ๋ณด๋ฅผ ํ๋์ ํ์ ํ๊ธฐ ์ด๋ ค์ด ๋ถํธํจ์ ๊ฐ์ง๊ณ ์์์ต๋๋ค. SUBONE์ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด, ์ง๋ ์์ ๊ฐ ์ญ์ ์์น์ ์ฌ์ฉ์์ ํ์ฌ ์์น๋ฅผ ์ง๊ด์ ์ผ๋ก ํ์ํฉ๋๋ค. ์ฌ์ฉ์๋ ์งํ์ฒ ๋์ฐฉ ์๊ฐ๊ณผ ์ค์ผ์ค์ ์ค์๊ฐ์ผ๋ก ํ์ธํ ์ ์์ผ๋ฉฐ, ์ปค๋ฎค๋ํฐ์ ๊ฒ์๊ธ๋ ์ง๋์ ๋งํนํ์ฌ ์ฌ์ฉ์์ ํธ์์ฑ๊ณผ ์ฑ์ ์ฌ๋ฏธ๋ฅผ ๋์์ ๊ณ ๋ คํ์ต๋๋ค.
-
์์ธ๊ถ์ ์งํ์ฒ ๋ ธ์ ๋ง ์ด 23๊ฐ์ด๋ฏ๋ก ๋ชจ๋ ๋ ธ์ ์ ๊ตฌํํ๊ธฐ์ ํ๊ณ๊ฐ ์๋ค๊ณ ํ๋จํ์ต๋๋ค. ํ๋ก์ ํธ์ ๋ฒ์๋ฅผ ๋ช ํํ๊ฒ ์ค์ ํ๊ณ , ์คํ ๊ฐ๋ฅํ ๋ชฉํ๋ฅผ ์ธ์ฐ๊ธฐ ์ํด ์์ง์ฑ๊ณผ ๋์ค์ฑ์ ๊ฐ์ถ 1ํธ์ ์ ์ค์ฌ์ผ๋ก ์๋น์ค๋ฅผ ๊ตฌํํ๊ธฐ๋ก ๊ฒฐ์ ํ์์ต๋๋ค.
https://github.com/orgs/FRONTENDSCHOOL7/projects/1
์๋ ํ์ธ์, ์ ํฌ๋ 4๋ช ์ Front-end ๊ฐ๋ฐ์๋ก ๊ตฌ์ฑ๋ Villainsํ์ ๋๋ค.
(๋ฉ์์ด์ฌ์์ฒ๋ผ ํ๋ก ํธ์๋ ์ค์ฟจ 7๊ธฐ ํ๋ก์ ํธ 16ํ์ ๋๋ค.)
์ด์งํ | ๊น๊ท๋ฆฌ | ๊น์ค์ | ์ ๋ช ์ง |
---|---|---|---|
JiHyeon Lee | gyulls2 | YOONSANG KIM | mj.shin |
ํ์ฅ | ํ์ | ํ์ | ํ์ |
[๋ฐฉํฅ์ฑ]
-
ํ ๋ชฉํ
- ํต์ฌ ๊ธฐ๋ฅ(์ง๋ API, ๊ฒ์๊ธ ๋ฑ๋ก ๋ฑ)์ ๊ตฌํํ๋ ๊ฒ์ ๋๋ค.
- ๋ก๊ทธ์ธ, ๋ฉ์ธ, ์ ์ , ํ๋ฐฐ, ๊ฒ์๊ธ ํ์ด์ง๋ฅผ ๊ตฌํํ๋ ๊ฒ์ ๋๋ค.
- VITE, Recoil ๋ฑ ์๋ก์ด ๊ธฐ์ ์ ๊ณต๋ถํ์ฌ ์ ์ฉํ๋ ๊ฒ์ ๋๋ค.
-
์ํฉ
- ์๋๋ธ์์ API ๊ฐ ์ ๊ณต๋์์ง๋ง ์ํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ๋๋ฐ ํ๊ณ๊ฐ ์๋ค๊ณ ํ๋จํ์์ต๋๋ค.
- ํ๋ก์ ํธ ๊ฒฝํ์ด ์ฒ์์ธ ํ์๊ณผ ๋ ๋ฒ์งธ, ์ธ ๋ฒ์งธ์ธ ํ์ ๋ฑ ์ค๋ ฅ์ด ์ฒ์ฐจ๋ง๋ณ์ด์์ต๋๋ค.
-
๋ฌธ์
- ์ ์ฒด ์ผ์ ์ค ๊ฐ๋ฐ ์ผ์ ์ด ์งง๋ค๋ ํผ๋๋ฐฑ์ ๋ฐ์์ต๋๋ค.
- ๋ฌธ์ํ ์์ ๋น์ค์ด ํฌ๋ค๋ ๋น์ค์ด ํฌ๋ค๋ ํผ๋๋ฐฑ์ ๋ฐ์์ต๋๋ค.
- ์ค์ค๋ก ์ค๋ฅ๋ฅผ ํด๊ฒฐํ๋ ค๋ค ๋ณด๋ ๊ฐ๋ฐ์ด ๋๋์ง๋ค๋ ๋ฌธ์ ์ ์ด ์์์ต๋๋ค.
- ์๋ก์ด ๊ฒ์ด ๋ง์ ์ฝ๋์ ์ ์ฉํ์ฌ ์ฌ์ฉํ๋๋ฐ ์ด๋ ค์์ด ์์์ต๋๋ค.
-
๊ฒฐ๊ณผ
- ๊ธฐ์กด ๊ฐ๋ฐ ์ผ์ ์ด 10์ผ์ด์๋๋ฐ ๋ช ์ธ ์์ ์์๊ณผ ๋์์ ์ฃผ์ ๊ฐ๋ฐ ์์ ์ ์์ํ๋ ๊ฒ์ผ๋ก ์ผ์ ์ ๋ณ๊ฒฝํ์ฌ ์ด 15์ผ๊ฐ ๊ฐ๋ฐํ ์ ์๋๋ก ๊ธฐ๊ฐ์ ๋๋ ธ์ต๋๋ค.
- ํ์ ์์ ํ 10๋ถ๊ฐ ๊ฐ์์ ๋ชฉํ์ ๋ฌ์ฑ ์ฌ๋ถ๋ฅผ ๊ฐ์ธ์ด ์ง์ ์์ฑํ๋๋ก ํ์ฌ ํ์๋ก ์์ฑ ์๊ฐ์ ์ค์ด๊ณ ๊ฐ๋ฐ์ ์ง์คํ ์ ์๋๋ก ๊ฐ์ ํ์์ต๋๋ค.
- ์ด์๊ฐ ์๊ธฐ๋ฉด ์ค์ค๋ก 30๋ถ ๊ฐ ๊ณ ๋ฏผ ํ ๋ฌธ์ ์ ๊ณผ ๋ณธ์ธ์ด ํด๊ฒฐํ๋ ค๊ณ ํ๋ ๋ฐฉ๋ฒ์ ๊ณต์ ํ๋๋ก ๊ท์น์ ์ ํ์ฌ ์งํ ์๋๋ฅผ ๋ํ์ต๋๋ค.
- ์์์ผ๊ณผ ํ ์์ผ ์ ๋ 7์์ ๋ชจ์ฌ ์คํฐ๋๋ฅผ ์งํํจ์ผ๋ก์จ ์๋ก์ด ๊ธฐ์ ์ ๊ณต๋ถํ๋ ์๊ฐ์ ๊ฐ์ก์ต๋๋ค.
[!NOTE] ํ๋ก์ ํธ ์ธํ ์ ๋ด๋นํ๋ฉด์ ์ง์ ๋น๋ ๋๊ตฌ๋ฅผ ์ ํํ๊ณ ํ์ํ ํจํค์ง๋ค๋ง ์ฑ์์ ๊ฐ๋ณ๊ฒ ๋ง๋ค๋ ค๊ณ ํ์ต๋๋ค. ๊ทธ๋์ ๋ฆฌ์กํธ ํ๋ก์ ํธ๋ฅผ ์ฝ๊ฒ ์์ํ ์ ์๋ CRA๋ฅผ ์ ํํ์ง ์์๋๋ฐ, ์นํฉ์ ์ด๊ธฐ ์ค์ ์ด ๋ง๊ณ ์ด๋ ต๊ธฐ๋ ํด์, ์์ฆ ๋์ธ์ธ VITE๋ฅผ ์ ํํ์ต๋๋ค. ๋น๋๊ฐ ๋น ๋ฅด๊ธฐ๋ ํ๊ณ , ์ค์ ๋ ๊ฐ๋จํ์๊ณ , ๋ฐ๋ฒจ์ ๋์ฒดํ๋ SWC๋ฅผ ์ฌ์ฉํ์ฌ ๋์ฑ ๋น ๋ฅด๊ฒ ๋น๋ํ ์ ์๋๋ก ํ์์ต๋๋ค! [!NOTE] ์๋ฒ์ ๋ฐ์ดํฐ์์ ํํฐ๋งํด์ผ ํ ์กฐ๊ฑด๋ค์ด ์์๊ณ , ๋น๋๊ธฐ์ ์ผ๋ก ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋๊ธฐ ์ ๋น ๋ฅด๊ฒ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํฌ ์ ์๋ค๊ณ ํ๋จํ์ฌ ๋ฆฌ์กํธ ์ฟผ๋ฆฌ๋ฅผ ๋์ ํ์์ต๋๋ค. ๋ฆฌ์กํธ ์ฟผ๋ฆฌ๋ฅผ ์ฒ์ ์ฌ์ฉํ๋ค ๋ณด๋, ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ์ํ ๊ด๋ฆฌ๋ก๋ ๋ฌ๋ ์ปค๋ธ๊ฐ ๋ฎ์ ๋ฆฌ์ฝ์ผ์ ์ ํํ์์ต๋๋ค.
ํ ๊ธ ์ ๊ธฐ/ํผ์น๊ธฐ
๐ฆ villains
โโ .eslintrc.cjs
โโ .githu
โ โโ ISSUE_TEMPLATE
โ โ โโ -๐feat-.md
โ โโ pull_request_template.md
โ โโ workflows
โ โโ deploy.yml
โโ .gitignore
โโ .gitmessage.txt
โโ .nvmrc
โโ .prettierrc
โโ README.md
โโ index.html
โโ package-lock.json
โโ package.json
โโ public
โโ src
โ โโ App.jsx
โ โโ api
โ โ โโ delete
โ โ โ โโ deleteComments.api.js
โ โ โ โโ deleteFollow.api.js
โ โ โ โโ deletePost.api.js
โ โ โ โโ deleteProduct.api.js
โ โ โโ get
โ โ โ โโ getCheckToken.api.js
โ โ โ โโ getComments.api.js
โ โ โ โโ getFollowingList.api.js
โ โ โ โโ getMyInfo.api.js
โ โ โ โโ getPostDetail.api.js
โ โ โ โโ getPosts.api.js
โ โ โ โโ getProductDetail.api.js
โ โ โ โโ getProducts.api.js
โ โ โ โโ getSearchUser.api.js
โ โ โ โโ getSubOneInfo.api.js
โ โ โ โโ getSubTime.api.js
โ โ โ โโ getUserDetail.api.js
โ โ โ โโ getUserFollower.api.js
โ โ โ โโ getUserInfo.api.js
โ โ โ โโ getUserPost.api.js
โ โ โโ loader
โ โ โ โโ searchPlace.loader.js
โ โ โโ post
โ โ โ โโ postComments.api.js
โ โ โ โโ postCommentsReport.api.js
โ โ โ โโ postFollow.api.js
โ โ โ โโ postHeart.api.js
โ โ โ โโ postImage.api.js
โ โ โ โโ postImages.api.js
โ โ โ โโ postReport.api.js
โ โ โ โโ postUnFollow.api.js
โ โ โ โโ postUploadPost.api.js
โ โ โโ update
โ โ โโ updatePostEdit.api.js
โ โ โโ updateProduct.api.js
โ โ โโ updateUser.api.js
โ โโ assets
โ โ โโ fonts
โ โ โโ img
โ โโ atoms
โ โ โโ bottomSheetStateAtom.js
โ โ โโ followPageStateAtom.js
โ โ โโ goodsFocusEndAtom.js
โ โ โโ goodsFocusStartAtom.js
โ โ โโ goodsQueryEndAtom.js
โ โ โโ goodsQueryStartAtom.js
โ โ โโ headerBtnStateAtom.js
โ โ โโ profileAtom.js
โ โ โโ queryAtom.js
โ โ โโ queryFocusAtom.js
โ โ โโ realProductAuthorAtom.js
โ โ โโ stationPlaceAtom.js
โ โ โโ subOneAtom.js
โ โ โโ userAtom.js
โ โ โโ userPostAtom.js
โ โโ components
โ โ โโ BottomSheet.jsx
โ โ โโ Buttons.jsx
โ โ โโ CheckBox.jsx
โ โ โโ DropDown.jsx
โ โ โโ FloatingButton.style.jsx
โ โ โโ Follower.jsx
โ โ โโ GlobalButton.jsx
โ โ โโ Goods.jsx
โ โ โโ Input.style.jsx
โ โ โโ Modal.jsx
โ โ โโ PageTemplate.jsx
โ โ โโ PageTemplate.style.js
โ โ โโ SearchSub.jsx
โ โ โโ Tanghulu.jsx
โ โ โโ chat
โ โ โ โโ ChatInputField.jsx
โ โ โ โโ ChatListItem.jsx
โ โ โ โโ Message.jsx
โ โ โโ feed
โ โ โ โโ Comment.jsx
โ โ โ โโ CommentForm.jsx
โ โ โ โโ ImagePreview.jsx
โ โ โ โโ PostCard.jsx
โ โ โ โโ ResizingTextarea.jsx
โ โ โโ layout
โ โ โ โโ BackHeader.jsx
โ โ โ โโ DefautlLayout.jsx
โ โ โ โโ NavMenu.jsx
โ โ โ โโ PrivateLayout.jsx
โ โ โ โโ SearchHeader.jsx
โ โ โ โโ SearchLayout.jsx
โ โ โโ map
โ โ โ โโ TrainMap.jsx
โ โ โโ profile
โ โ โ โโ profile.jsx
โ โ โโ searchbar
โ โ โ โโ ListBox.jsx
โ โ โ โโ SearchBar.jsx
โ โ โ โโ UserCard.jsx
โ โ โ โโ UserListBox.jsx
โ โ โโ splash
โ โ โโ Splash.style.js
โ โโ config
โ โ โโ api.config.js
โ โ โโ contact.jsx
โ โ โโ pageUrlConfig.js
โ โ โโ route.config.jsx
โ โโ database
โ โ โโ 2023-2024-holiday.json
โ โโ hooks
โ โ โโ useBlockToBack.js
โ โ โโ useBottomSheetOptions.js
โ โ โโ useFormatDate.js
โ โ โโ useGeoLocation.js
โ โ โโ useInfiniteScroll.js
โ โ โโ useModal.js
โ โ โโ useSearchData.js
โ โโ index.jsx
โ โโ style
โ โ โโ GlobalStyles.jsx
โ โ โโ swiperStyle.js
โ โ โโ theme.js
โ โโ views
โ โโ Error.view.jsx
โ โโ FollowersList.view.jsx
โ โโ FollowingsList.view.jsx
โ โโ auth
โ โ โโ SignIn.view.jsx
โ โ โโ SignUp.view.jsx
โ โ โโ Splash.view.jsx
โ โ โโ index.view.jsx
โ โโ chat
โ โ โโ Chat.view.jsx
โ โ โโ ChatDetail.view.jsx
โ โ โโ index.view.jsx
โ โโ feed
โ โ โโ Feed.view.jsx
โ โ โโ FeedDetail.view.jsx
โ โ โโ FeedWrite.view.jsx
โ โ โโ index.view.jsx
โ โโ goods
โ โ โโ Goods.view.jsx
โ โ โโ GoodsDetail.view.jsx
โ โ โโ GoodsWrite.view.jsx
โ โ โโ index.view.jsx
โ โโ home
โ โ โโ Home.view.jsx
โ โ โโ Result.view.jsx
โ โ โโ Train.view.jsx
โ โ โโ index.view.jsx
โ โโ user
โ โโ Profile.view.jsx
โ โโ ProfileEdit.view.jsx
โ โโ ProfileFollow.view.jsx
โ โโ index.view.jsx
โโ vite.config.js
- ๋ก๊ทธ์ธ/ ํ์๊ฐ์
- ์ข์์
- ๊ฒ์๊ธ ๋ฑ๋ก
- ์ง๋์ ์์น ํ์
- ๊ฒ์
- ๋ฌดํ์คํฌ๋กค
- ํ๋ฐฐ ๋ฆฌ์คํธ
์คํ๋์ ํ์ด์ง | ๋ก๊ทธ์ธ ํ์ด์ง | ํ์๊ฐ์ ํ์ด์ง |
---|---|---|
ํ ํ์ด์ง |
---|
ํผ๋ ํ์ด์ง | ์ ์ ๊ฒ์ ๊ธฐ๋ฅ | ๊ฒ์๊ธ ์์ฑ ํ์ด์ง |
---|---|---|
๊ฒ์๊ธ ์์ธ ํ์ด์ง | ๊ฒ์๊ธ ์์ ๊ธฐ๋ฅ | ๊ฒ์๊ธ ์ญ์ ๊ธฐ๋ฅ |
---|---|---|
๋๊ธ ์์ฑ | ๋๊ธ ์ญ์ ๊ธฐ๋ฅ | ๋๊ธ ์ ๊ณ ๊ธฐ๋ฅ |
---|---|---|
ํ๋ฐฐ ๋ฑ๋ก | ํ๋ฐฐ ๋ฐฐ๋ฌ์๋ฝ / ์ฑํ | ์ฑํ ํ์ธ / ํ๋ฐฐ ์ํ ๋ณ๊ฒฝ |
---|---|---|
๋ฐฐ๋ฌ ์๋ฃ ์ฑํ | ๋ฐฐ๋ฌ ์๋ฃ ํ์ธ / ํ๋ฐฐ ์ํ ๋ณ๊ฒฝ | ํ๋ฐฐ ์์ |
---|---|---|
ํ๋ฐฐ ์ญ์ |
---|
ํ๋กํ ํ์ด์ง | ํ๋กํ ์์ ํ์ด์ง | ํ๋ก์ / ํ๋ก์ ํ์ด์ง |
---|---|---|
๐src/views/home/index.view.jsx
useMemo๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ณ , ๊ทธ๊ฒ์ ๋ฐํ์ผ๋ก ๋ฆฌ์ฝ์ผ์ ์ ์ฅํ์์ต๋๋ค. ๊ทธ๋์ ๋ถํ์ํ api ์์ฒญ์ ์ต๋ํ ์ค์ด๋ ค ํ๊ณ , ์ด๋ฅผ ์ด์ฉํด ListBox๋ฅผ ๋ ๋๋งํ๋๋ฐ, ์ด ๋ํ ๊ธฐ์กด์ ๊ฒ์ ๋ฆฌ์คํธ๋ฐ์ค๊ฐ position ์์ฑ์ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ๋ฌ๋ฆฌ ๋ณ๋์ ์ปดํฌ๋ํธ๋ก ํ์ฌ ๋ฆฌํ๋ก์ฐ๋ฅผ ์ค์ด๋ ค๊ณ ํ์์ต๋๋ค.
const HomeIndexPage = () => {
const navigate = useNavigate();
const [subOneInfo, setSubOneInfo] = useRecoilState(subOneAtom);
const [query, setQuery] = useRecoilState(queryAtom);
useMemo(() => {
//๋์์ฒ ๋ 1ํธ์ ์งํ์ฒ ์ญ ์ ๋ณด ๋ถ๋ฌ์ค๊ธฐ
getSubOneInfo().then((data) => {
const dataList = data.data.SearchInfoBySubwayNameService.row;
const rowInfo = [...dataList].filter((elem) => elem.LINE_NUM === '01ํธ์ ');
const newInfo = rowInfo.map((info, index) => {
return (info = { Query: info.STATION_NM, Id: info.STATION_CD });
});
setSubOneInfo(newInfo);
});
}, []);
const list = useSearchData(query, subOneInfo);
const handleClickBack = () => {
setQuery('');
};
const handleClickStation = (event) => {
navigate(`${pageUrlConfig.homePage}/${event.target.id}`, { state: event.target.textContent });
setQuery('');
};
return (
<>
<SearchHeader onClick={handleClickBack} placeholder={`์งํ์ฒ ์ญ์ ๊ฒ์ํด์ฃผ์ธ์`} />
{query ? <ListBox list={list} onClick={handleClickStation} /> : <Outlet />}
<NavMenu />
</>
);
};
export default HomeIndexPage;
๐src/config/pageUrlConfig.js
๋ณ๋์ ํด๋์ค๋ก ์์ฑํ์ฌ ๋ผ์ฐํฐ๋ฅผ ์ต๋ํ ํ ๊ณณ์์ ์์ ํ ์ ์๋๋ก ํ์์ต๋๋ค.
const baseUrl = import.meta.env.BASE_URL;
class pageUlrConfig {
splashPage = `${baseUrl}`;
//์๋ต
goodsDetailPage = `${baseUrl}goods/:id`;
goodsEditPage = `${baseUrl}goods/edit/:id`;
}
export default new pageUlrConfig();
๐src/config/route.config.jsx
PrivateLayout์ ์ฌ์ฉํ์ฌ ๋ก๊ทธ์ธ ํ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋๋ก ํ์๊ณ , loader๋ฅผ ์ฌ์ฉํด ๋ฏธ๋ฆฌ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ํ์ ์ปดํฌ๋ํธ์์ ๊ฐ์ ธ์ฌ ์ ์๋๋ก ํ์ฌ ๋งค๋ฒ localStorage์ ์ ๊ทผํ๋ ์ฝ๋๋ฅผ ์ค์์ต๋๋ค.
const routeConfig = [
{
path: baseUrl,
element: <DefaultLayout />,
children: [
{
path: pageUrlConfig.splashPage,
element: <AuthIndexPage/>,
children: [
{ index: true, element: <SplashPage /> },
{ path: pageUrlConfig.signInPage, element: <SignInPage /> },
{ path: pageUrlConfig.signUpPage, element: <SignUpPage />},
],
},
{
path: baseUrl,
loader: async()=>sendUserInfo(),
id: 'user',
element: <PrivateLayout />,
children: [
{
path: pageUrlConfig.homePage,
element: <HomeIndexPage/>,
children: [
๐src/components/default/GlobalButton.jsx
๊ณตํต์ ์ผ๋ก ์ฐ์ด๋ ์คํ์ผ ๋ฑ์ css๋ก ์ ์ํ๊ณ ๋ฒํผ์ ๊ฐ์ ธ์ฌ ์ ์๋๋ก ํ์๊ณ , ์ธ์ ๋ค๋ฅธ ์คํ์ผ์๋ ์ฝ๊ฒ ์ ์ฉํ ์ ์์์ต๋๋ค.
import styled, { css } from 'styled-components';
import theme from '../../style/theme';
/**@param variant: "primary" | "secondary" | "basic" */
const DefaultBtn = ({ children, variant, disabled, id }) => {
return (
<StyledButton variant={variant ?? 'primary'} disabled={disabled ?? false} id={id ?? ''}>
{children}
</StyledButton>
);
};
const PrimaryStyle = css`
background-color: ${theme.color.primary};
color: ${theme.color.white};
border-color: ${theme.color.primary};
`;
const SecondaryStyle = css`
background-color: ${theme.color.secondary};
color: ${theme.color.black};
border-color: ${theme.color.secondary};
`;
const BasicStyle = css`
background-color: ${theme.color.white};
color: ${theme.color.primary};
border-color: ${theme.color.primary};
`;
const StyledButton = styled.button`
width: 100%;
height: 100%;
border: 1px solid;
border-radius: 9999px;
cursor: pointer;
${(props) => {
switch (props.variant) {
case 'primary':
return PrimaryStyle;
case 'secondary':
return SecondaryStyle;
case 'basic':
return BasicStyle;
}
}}
&:disabled {
border-color: ${theme.color.grey};
background-color: ${theme.color.grey};
color: ${theme.color.white};
cursor: default;
}
`;
export { PrimaryStyle, SecondaryStyle, BasicStyle };
export default DefaultBtn;
๐src/style/theme.js
const theme = {
color: {
primary: '#3c58c1',
secondary: '#B1BCE6',
white: '#FFF',
black: '#000',
brown: '#663C06',
grey: '#767676',
light: '#DBDBDB',
},
//์๋ต
fontSize: {
h1: `48px`,
h2: `36px`,
h3: `24px`,
body1: `18px`,
body2: `16px`,
body3: `14px`,
caption: `12px`,
},
};
export default theme;
๋ง์ ๋ฒํผ๋ค์ด ํ์ํ ๊ณณ์ด์ด์ ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ ํตํด ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ๊ด๋ฆฌํ์์ต๋๋ค. ๋ํ, ๊ฒ์๊ธ๊ณผ ํ๋ฐฐ ๋ชฉ๋ก์ ๋๋๋๋ ํญ ๋ฉ๋ด๋ ๊ฐ๋จํ๊ฒ ์คํ์ผ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ฌ ์กฐ์ ํ์์ต๋๋ค.
const handleClickBtns = (event) => {
switch(event.target.id){
case 'chat':
navigate(pageUrlConfig.chatPage);
break;
case 'share':
const BASE_URL = import.meta.env.BASE_URL;
navigator.clipboard.writeText(`${BASE_URL}${pathname}`);
alert('๋งํฌ ๋ณต์ฌ ์๋ฃ!');
break;
case 'edit':
navigate(`${pageUrlConfig.profilePage}/${user.accountname}/edit`)
break;
case 'unfollow':
unFollowing();
break;
case 'follow':
Following();
break;
}
//์๋ต
<ButtonWrap onClick={handleClickBtns}>
<button id='chat'></ button>
{isMy ?
<DefaultBtn id={`edit`}>ํ๋กํ ์์ </DefaultBtn>
: (profileInfo.isfollow ?
<DefaultBtn variant={'basic'} id={`unfollow`}>์ธํ๋ก์ฐ</DefaultBtn>
: <DefaultBtn variant={'primary'} id={`follow`}>ํ๋ก์ฐ</DefaultBtn>
)}
<button id='share'></ button>
</ButtonWrap>
//์๋ต
//ํญ ๋ฉ๋ด ํด๋ฆญํ๋ฉด ์คํ์ผ ๋ฐ๋๋ก ์ฃผ๊ธฐ
const TabGroup = styled.div`
position: sticky;
top: 0;
z-index: 10;
display: flex;
width: 100%;
height: 64px;
background-color: ${theme.color.white};
& :nth-child(1){
${(props)=> props.color? SecondaryStyle : BasicStyle}
}
& :nth-child(2){
${(props)=> !props.color? SecondaryStyle : BasicStyle}
}
`;
ํ๋ก์ ํธ ์งํ ์ค ๋ก๊ทธ์ธ ํ์ด์ง, ํ์๊ฐ์ ํ์ด์ง, ๊ฒ์๊ธ ์์ฑ ํ์ด์ง, ์์ ํ์ด์ง ๋ฑ๋ฑ form data๋ฅผ ๊ด๋ฆฌํ๋ ํ์ด์ง๊ฐ ์ ์ ๋์ด๋๊ฒ ๋์์ต๋๋ค.
๊ธฐ์กด ํ์๊ฐ์
form data๋ฅผ ๊ด๋ฆฌ ๋ฐ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์งํํ๊ธฐ ์ํด์๋ form ์ํ๊ด๋ฆฌ๋ฅผ ์ํ state
,
form ์
๋ ฅ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ handler
, form ๋ฐ์ดํฐ์ ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ธฐ ์ํ validation ํจ์
,
์๋ฌ ๋ฉ์์ง ๊ฐ์ ์ํ๊ด๋ฆฌํ state
๋ฅผ ์ ๋ถ ์ฝ๋๋ก ์์ฑํด์ผํ์ต๋๋ค. ํ์ง๋ง ์ ํฌ๋ ์ฌ๋ฌ๊ฐ์ form data๋ฅผ
๊ด๋ฆฌํ๊ณ ์์๊ณ , ๊ทธ์๋ฐ๋ฅธ state์ ํจ์๋ฅผ ์ถ๊ฐ๋ก ์ ์ธํด์ผ ํ์ต๋๋ค.
์ด๋ฅผ ๊ฐ์ํ ์ํค๊ธฐ ์ํด react-hook-form
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ผ๋ฉฐ, react-hook-form
์ฌ์ฉ์ผ๋ก ์์ ์ ์ธํด์ผํ
state ๋ฐ ํจ์๋ค์ ์ ๋ถ react-hook-form
์ผ๋ก ๊ด๋ฆฌ๊ฐ ๊ฐ๋ฅํ์ต๋๋ค.
// react-hook-form
const {
register,
handleSubmit,
watch,
formState: { isSubmitting, isSubmitted, errors },
} = useForm({ mode: 'onChange' });
const userAccountId = watch('accountId');
const userEmail = watch('email');
const userPwd = watch('password');
...
<Input
id="accountId"
type="accountId"
name="accountId"
placeholder="์๋ฌธ, ์ซ์, ํน์๋ฌธ์(.),(_)๋ง ์ฌ์ฉ ๊ฐ๋ฅ"
aria-invalid={isSubmitted ? (errors.accountId ? 'true' : 'false') : undefined}
{...register('accountId', {
required: '*๊ณ์ ID๋ฅผ ์
๋ ฅํด์ฃผ์ธ์.',
pattern: {
value: /^[A-Za-z0-9._]+$/,
message: '*๊ณ์ ID ํ์์ ๋ง์ง ์์ต๋๋ค.',
},
})}
/>
...
register
๋ฅผ ์ฌ์ฉํด ์
๋ ฅ์ required ๋ฉ์์ง, ์ ํจ์ฑ ๊ฒ์ฆ ์ ๊ท์, ์ ํจ์ฑ ๊ฒ์ฆ ์๋ฌ ๋ฉ์์ง๋ฅผ ๊ด๋ฆฌ ํ ์ ์๊ณ ,
watch
๋ฅผ ํตํด form ์ํ ๊ด๋ฆฌ๋ฅผ ์ํ state, ์
๋ ฅ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ handler๋ฅผ ๋์ ํ์ฌ
react-hook-form
์ ์ฌ์ฉํ๊ธฐ ์ ๋ณด๋ค ํจ์ฌ ๊ฐ๊ฒฐํ ์ฝ๋๋ฅผ ์์ฑํ ์๊ฐ ์์์ต๋๋ค.
๋ฐํ ์ํธ UI๋ฅผ ์ฌ์ฌ์ฉํ ์ ์๋๋ก ์ํ๋ฅผ Recoil์ ํตํด ์ ์ญ์ผ๋ก ๊ด๋ฆฌํ๊ณ , ์ต์ ์ ์์ฑํ๋ ์ปค์คํ ํ ์ ๋ง๋ค์์ต๋๋ค. ๐src/atoms/bottomSheetStateAtom.js
import { atom } from 'recoil';
export const bottomSheetStateAtom = atom({
key: 'bottomSheetState',
default: false,
});
export const bottomSheetOptions = atom({
key: 'bottomSheetOptions',
default: [],
});
๐src/hooks/uswBottomSheetOption.js
const useBottomSheetOptions = ({
currentAccountname,
authorAccountname,
postEdit,
postDelete,
postReport,
commentDelete,
commentReport,
type,
}) => {
let options = [];
switch (type) {
case 'post':
if (currentAccountname === authorAccountname) {
options = [
{ label: '๊ฒ์๊ธ ์์ ', callback: postEdit },
{ label: '๊ฒ์๊ธ ์ญ์ ', callback: postDelete },
];
} else {
options = [{ label: '๊ฒ์๊ธ ์ ๊ณ ', callback: postReport }];
}
break;
case 'comment':
if (currentAccountname === authorAccountname) {
options = [{ label: '๋๊ธ ์ญ์ ', callback: commentDelete }];
} else {
options = [{ label: '๋๊ธ ์ ๊ณ ', callback: commentReport }];
}
break;
default:
break;
}
return options;
};
export default useBottomSheetOptions;
๋ธ๋์น ๋ชจ๋ธ์ ๊น ํ๋ก์ฐ(Git Flow) ์ฐธ๊ณ ํ์ฌ main- development - feature ๋จ๊ณ๋ก ๋๋์ด ์ฝ๋๋ฅผ ๊ด๋ฆฌํ๋ ์ ๋ต์ ์ฌ์ฉํ์์ต๋๋ค. ๋ธ๋์น๋ ์ธ ๊ฐ์ง๊ฐ ์๋๋ฐ ํญ์ ์ ์ง๋๋ (main, development(dev)) ๋ธ๋์น๊ฐ ์๊ณ , ์ผ์์ ์ผ๋ก ์์ฑ๋์๋ค๊ฐ ์ฌ๋ผ์ง๋ (feature(feat)) ๋ธ๋์น๊ฐ ์์ต๋๋ค. ๊ธฐ๋ฅ, ํ์ด์ง ์์ ์ feature ๋ธ๋์น๋ฅผ ์์ฑ ํ ์์ ํ๊ณ dev ์ push ํ์ฌ rebase ํ๊ณ , ์๋ฃ๋๋ฉด ๋ธ๋์น๋ฅผ ์ญ์ ํ ์๋ก ์์ฑํ๋ ๋ฐฉ์์ผ๋ก ์งํํ์์ต๋๋ค. main ์ผ๋ก push ํ๋ ๊ฒฝ์ฐ 2๋ช ์ด์์ ์น์ธ ํ ๊ฐ๋ฅํ๋๋ก ํ์ฌ ์ค์๋ก ํ์ผ์ด ๋ ์๊ฐ๋ ๊ฒ์ ๋ฐฉ์งํ์์ต๋๋ค.
git flow: main - dev(test) - feat/๊ธฐ๋ฅ๋ช
main: ์ต์ข
๋ฐฐํฌ์ฉ ๋ธ๋์น
development: ํ
์คํธ์ฉ ๋ธ๋์น
feat/: ๊ธฐ๋ฅ๋ณ ์์
์ฉ ๋ธ๋์น
- ์์ด์ค๋ธ๋ ์ดํน
- ์ฒซ ๋ชจ์์ ๋ฏ๊ฐ๋ฆผ์ด ์์์ง๋ง ์ฌ๋ ์ง์ญ, MBTI, ์ ๊ณต, ๊ฐ์ธ ์ผ์ ๋ฑ์ ์ฃผ์ ๋ก ์ด์ผ๊ธฐ ๋๋์์ต๋๋ค. ์์ด์ค๋ธ๋ ์ดํน์ผ๋ก ๋ชจ๋ ํ์์ด ์์ธ ์งํ์ฒ 1ํธ์ ๊ทผ์ฒ์ ๊ฑฐ์ฃผ ํ๋ค๋ ๊ณตํต์ ์ ๋ฐ๊ฒฌํ์๊ณ ์ด ํน์ง์ ๋ฐํ์ผ๋ก ๊ฐ์ฑ ์๋ ํ ํ๋ก์ ํธ ์์ด๋์ด๊ฐ ๋์ค๊ฒ ๋์์ต๋๋ค.
- ๋งค์ผ ์ค์ 9์, ์คํ 5์์ ํ๊ณ ๋ก์ ์์ฑํ๋ฉด์ ์์ด์ค๋ธ๋ ์ดํน ํ์์ ๊ฐ์ง ์ ์๋๋ก ๋ ธ๋ ฅํ์ต๋๋ค.
- ํ๋ก์ ํธ ์ค ํ๋ค์ด์ ์ง์น๋ฉด ํด์ ์๊ฐ์ ๊ฐ์ง๋ฉฐ ๋ถ์๊ธฐ๋ฅผ ํ์ด๊ฐ ์ ์๋๋ก ํ์ต๋๋ค.
- ์ปค๋ฎค๋์ผ์ด์
- ๊ฐ์ ์ ๊ณต๊ณผ ํ๋ก์ ํธ ๊ฒฝํ ์ฌ๋ถ๋ฅผ ์ฒดํฌํ์ฌ ๋์ด๋์ ๋ง๋ ์ญํ ๋ถ๋ด์ ํ์ฌ ๋ถ๋ด์ ์ค์ผ ์ ์๋๋ก ๋ ธ๋ ฅํ์์ต๋๋ค.
- ์ด๋ฐ์ ์์ด๋์ด ๋ฐ ์ค์ ๊ด๋ จํด์ ์์ฌ ๊ฒฐ์ ์ฌํญ์ด ๋ง์์ต๋๋ค. ์ํํ ์งํ์ ์ํด ๊ฐ ์ ํ ์ฌํญ์ ๋ํ ์ฅ๋จ์ ์ ์ค๋ช ํ๊ณ ์ฌ์ฉ ๊ฒฝํ ์ฌ๋ถ์ ์ด๋ค ๋ถ๋ถ์ด ๋ถ๋ด์ธ์ง ์๊ฒฌ์ ๋ค์ด๋ณด๊ณ ๋ณด์ํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ฐพ์ ์ ์๋๋ก ํ์์ต๋๋ค.
- ๋ชจ๋ฅด๋ ๋ด์ฉ์ด ์์ผ๋ฉด ์์ ๋กญ๊ฒ ์ง๋ฌธํ๋ ๋ถ์๊ธฐ๋ฅผ ๋ง๋ค ์ ์๋๋ก ๋ ธ๋ ฅํ์์ต๋๋ค.
- ๊ฐ์ ์ํ๋ ๋ถ๋ถ์ ์นญ์ฐฌํ๊ณ ๋ ธ๋ ฅ์ ์์ํ๋ฉด์ ์ฑ์คํ ์ฐธ์ฌํ ์ ์๋ ๋ถ์๊ธฐ๋ฅผ ๋ง๋ค์์ต๋๋ค.
-
๋งค์ผ ์ค์ , ์คํ ์คํ๋ฆฐํธ 2ํ ์งํ
- ๊ฐ์ ์งํ ์ํฉ์ ํ์ ํ๊ณ ์ค์ , ์คํ ๋ชฉํ๋ฅผ ์ ํ์์ต๋๋ค. ๋จธ์ง ๊ณผ์ ์ ๊ฑฐ์น๋ฉด์ ์ฝ๋๊ฐ ๋๋ฌด ๋ฌ๋ผ์ ธ์ ํฐ ์ถฉ๋์ด ์๊ธฐ๋ ์ผ์ ๋ฐฉ์งํ์์ต๋๋ค.
-
์ฃผ 1ํ ์คํ๋ผ์ธ ๋ชจ์
- ๋ชจ๋ ์์ธ์ ๊ฑฐ์ฃผํ์ฌ ์ ๊ทผ์ฑ์ด ์ข์์ผ๋ฏ๋ก ์ฃผ 1ํ ์ด์ ์คํ๋ผ์ธ์ผ๋ก ๋ง๋ ํ๋ก์ ํธ๋ฅผ ์งํ ํ๋๋ก ์ผ์ ์ ๊ณํํ์ต๋๋ค.
-
- ์ค์ 9์๋ถํฐ ์คํ 6์๊น์ง๋ ๊ฐ๋ฐ์ ์ง์คํ๊ณ ๊ณต๋ถ๊ฐ ํ์ํ ๋ถ๋ถ์ ์ ๋ 7์ ์ดํ์ ๋ฐ๋ก ์คํฐ๋๋ฅผ ์งํํ์์ต๋๋ค.
- ๋ฆฌ์ฝ์ผ์ ์ด์ฉํด ๊ฒ์์ด๋ฅผ ์ ์ฅํ๊ณ ๋ฆฌ์กํธ ์ฟผ๋ฆฌ๋ฅผ ์ด์ฉํด ์์ฃผ์ฌ์ฉํ๋ ๊ฒ์์ด ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๊ดํด ์คํฐ๋๋ฅผ ์งํํ์์ต๋๋ค.
-
๋ฌธ์ํ
- ๋ ธ์ , ๊น ์ํค, ์ต์๋์ธ ๋ฑ์ ํ์ฉํ์ฌ ์ฃผ๊ธฐ์ ์ผ๋ก ์งํํ๋ ํ์์ ํ๊ณ , ์คํฐ๋์ ๊ดํ ์๋ฃ๋ฅผ ์ ๋ฆฌํ์์ต๋๋ค.
- ๋ ธ์ , ๋ง์ผ์คํค์ผ๋ก ํ๋ก์ ํธ ๊ด๋ จ ๊ธฐ๋ฅ ๋ช ์ธ ์์ฑ๊ณผ ์ผ์ ๊ด๋ฆฌ๋ฅผ ํ์์ต๋๋ค.
๐ ํ ๋ ธ์
๐ ๊ธฐ๋ฅ ๋ช ์ธ์
๐ ์์ด์ดํ๋ ์
๐ User Flow Chart
๐ SITE MAP
๐ ํผ๊ทธ๋ง ๋์์ธ
๐ ๊นํ๋ธ ์ํค
- svg๋ก ์ฌ์ฉํ๋ ์ด๋ฏธ์ง๋ ์ปดํฌ๋ํธ๋ก ๋ฐ๊พธ์ด์ ์ฌ์ฉํ ์์ ์ ๋๋ค.
- BrowerRouter๋ฅผ ์ฌ์ฉํ์ฌ github page ๋ฑ์ ์ ์ ํ์ด์ง์์๋ ์๋ก๊ณ ์นจ ์ 404 ํ์ด์ง๊ฐ ๋จ๋ ์ค๋ฅ๋ฅผ ์์ ํ ์์ ์ ๋๋ค.
- http์ https ๊ฐ์ ํต์ ์ด ๋งํ ์ผ๋ถ ์คํ api ์ฌ์ฉ์ด ๋ถ๊ฐํ ์ ์ ์์ ํ ์์ ์ ๋๋ค.
- ๋ฌดํ์คํฌ๋กค์ ๋ฆฌ์กํธ ์ฟผ๋ฆฌ๋ฅผ ์ ์ฉํ ์์ ์ ๋๋ค. ํ์ฌ ๊ฒ์๊ธ ํํฐ๋ง๊ณผ ๊ฐ์ด ์ ์ฉ์ ํ๋ ค๋ค ๋ณด๋ ์ ์๋์ด ์๋๋ ์ ์ด ์์ด์ ์คํฌ๋กค ์ด๋ฒคํธ๋ก ๊ตฌํํ์์ต๋๋ค.
๋น๋์ ๋ผ์ฐํฐ์ ๋ฏธ์ด ์ ์ด ๋ค์์ต๋๋ค. ์ ์ ํ์ด์ง์ ๊ฒฝ์ฐ HashRouter๋ฅผ ์ฌ์ฉํด์ผ ํ์ง๋ง, ๊ทธ๋ ๋ค๊ณ ๋ฐ๊พธ๊ธฐ์ ๋ก์ปฌ์์ ์๋๊ณ โฆ ๊ฒฐ๊ตญ BrowerRouter๋ก ๊ฐ๋ฐ์ ํธ์์ฑ์ ์ฑ๊ธด ๋ค ๋ฆฌํฉํ ๋ง ๊ธฐ๊ฐ์ ์ด์ฌํ ์์ ํ๊ธฐ๋ก ํ์ต๋๋ค. ๋ฆฌ์กํธ ์ฟผ๋ฆฌ, ๋ฆฌ์ฝ์ผ, vite ๋ชจ๋ ์ด๋ฒ์ ์ฒ์ ์จ๋ณธ ๊ฒ๋ค์ด๋ผ์ ๋ฒ๋ฒ ๊ฑฐ๋ ธ์ง๋ง ๋์ค์๋ ์ง์ ์๋ ค์ค ์ ์์ ์ ๋๋ก ๋น ๋ฅด๊ฒ ์ต์ํด์ก์ต๋๋ค. ์ ์ฅ์ ์ ์ ์ ์๋ ์๊ฐ์ด์ด์ ์ข์๊ณ , ๋ฌด์๋ณด๋ค ์ ๊ฐ ์ค๊ฐ์ ๋ด ๊ธธ๋ก ์๊ฑฐ๋ ํ๋๋ง ํ๊ณ ๋ค ๋ ๋๋ฃ๋ค์ด ์์์ ์ ์ก์์ค์ ์ข์์ต๋๋ค.โจ api ๋ณ๊ฑฐ ์๋๋ค์~~(์ง๋ ํ๋๋ก ์ผ์ฃผ์ผ ์ก์ ์ฌ๋)~~
์งง์ ๊ธฐ๊ฐ ๋ด์ ํ๋ก์ ํธ๋ฅผ ์์ฑํ ์ ์์์ง ๊ฑฑ์ ์ด ์ปธ์ง๋ง ๋ฉ์ง ํ์๋ถ๋ค์ ๋ง๋ ์ฆ๊ฒ๊ฒ, ๋๊น์ง ๋ง๋ฌด๋ฆฌ ํ ์ ์์๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ด๋ฒ ํ๋ก์ ํธ๋ฅผ ํ๋ฉด์ ์ฐ๋ฆฌ๊ฐ ํ๋ ๊ฒ์ ๋์์์ด ์ด์ ๋ฅผ ์ฐพ๊ณ , ์๊ฒฌ์ ๋๋๊ณ , ๋ฌธ์ํ๋ฅผ ํ๋ฉฐ ์ฒด๊ณ์ ์ผ๋ก ํ์ ํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ด ๊ฒ ๊ฐ์ต๋๋ค. ๋๋ ๋ค ๋์๋ณด๋ ๋ถ์กฑํ ๋ถ๋ถ๋ ๋ง์ด ๋ณด์ด์ง๋ง, ์์ผ๋ก์ ๋ฆฌํฉํ ๋ง์ ํตํด ์ฑ์๋๊ฐ๊ณ ์ถ์ต๋๋ค. ์ต๊ณ ์ ๋น๋ฐ๋ค ๐ ์์ผ๋ก๋ ํ์ดํ ์ ๋๋ค!
ํญ์ ๊ธฐ๋ฅ ์์ฑ๋ง ๋ชฉ์ ์ ๋ ์ฝ๋ฉ์ ํด์์๋๋ฐ ์ฒ์์ผ๋ก ๊ธฐํ๋ถํฐ ํ๋ก์ ํธ ๊ตฌ์กฐ, ์ปจ๋ฒค์ ๋ฑ ์ฒซ ๊ตฌ์กฐ๋ฅผ ํํํ ์ก๊ณ ์์ํ ํ๋ก์ ํธ๋ ์ฒ์์ด์์ต๋๋ค. ๊ทธ๋งํผ ์์ฌ๋ ๋ง์๊ณ , ์์ฌ์ด ๋ง์๋ ๋งํผ ์์ฌ์๋ ๋ง์๋ ํ๋ก์ ํธ ๊ฐ์ต๋๋ค. ํ์ง๋ง ํ ๋ฆฌ๋๋ก ์ธ์ ๋ ์ ํฌ ํ์๋ค์ ์ด๋์ด์ฃผ๊ณ ๋ง์ด ์๋ ค์ฃผ์ ์งํ๋๊ณผ ๋๊น์ง ์ด์ฌํ ์ฐธ์ฌํด์ฃผ์ ๊ท๋ฆฌ๋ ๋ช ์ง๋ ๋๋ถ์ ๊ทธ ์์ฌ์์ด ์์ด์ง ๋งํผ ๋ป ๊น์ ์๊ฐ์ด์๋ ๊ฒ ๊ฐ์ต๋๋ค. ์์ผ๋ก๋ ํ์๋ถ๋ค๊ณผ ๊พธ์คํ ๋ฆฌํํ ๋ง์ ๊ฑฐ์ณ ๋ง๋ฌด๋ฆฌ๋ ์๋ฒฝํ๊ฒ ํ๊ณ ์ถ์ต๋๋ค~~!
๋๋ฌด ์ข์ ์กฐ์๋ค์ ๋ง๋ ๋๋ถ์ ์ฝ ํ ๋ฌ๊ฐ ํ๋ก์ ํธ๊ฐ ์ ๋ง ์ฆ๊ฑฐ์ ์ต๋๋ค. ๋ถ์กฑํ ์ ์ด ๋ง์์ง๋ง ๋ค๋ค ์ ์ฑ๊ฒจ์ฃผ์ จ๊ณ ๋ชจ๋ฅด๋ ๊ฒ์ ์ธ์ ๋ ์ง ๋ฌผ์ด๋ณผ ์ ์๋ ๋ถ์๊ธฐ์ ์์ธํ๊ฒ ์ค๋ช ํด์ฃผ์๋ ์กฐ์๋ค ๋๋ถ์ ์ง๋ฌธ๋ ํธํ๊ฒ ํ ์ ์์์ต๋๋ค. ํ ์ ์๋ ๊ฒ์ด ์ ๋ค๊ณ ์๊ฐํ ์๊ฐ๋ ์ข ์ข ์์๋๋ฐ ๋ฌธ์์์ฑ์ด๋ ์ผ์ ๊ด๋ฆฌ, ์ปค๋ฎค๋์ผ์ด์ ๋ฑ ์ ๊ฐ ํ ์ ์๋ ๋ถ๋ถ์ ์ ๊ทน์ ์ผ๋ก ํ์๊ณ API ๋ช ์ธ์์ฑ, ์คํ์ผ๋ ์ปดํฌ๋ํธ ๋ฑ ๋ชจ๋ฅด๋ ๊ฒ๋ ์ผ๋จ ์๋ํด๋ณด๊ณ ๋ณด์ํด๊ฐ๋ ๋ฐฉ์์ผ๋ก ์ฐธ์ฌํ์์ต๋๋ค. ์ด์ฌํ ํ๋ ์กฐ์๋ค์ ๋ณด๋ฉด์ ์ ๋ ๋๊น์ง ํ๋ผ ์ ์์๊ณ ๋ง์ด ๋ฐฐ์ธ ์ ์์์ต๋๋ค. ์์ผ๋ก๋ ๊ณ์ ๋ฆฌํฉํ ๋ง์ผ๋ก ํ๋ก์ ํธ๋ฅผ ์ ๋ง๋ฌด๋ฆฌํ๊ณ ์ถ์ต๋๋ค. ๊ฐ์ฌํฉ๋๋ค.