-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[4주차] 윤영준 과제 제출합니다. #11
base: master
Are you sure you want to change the base?
Changes from all commits
499f752
453fd43
1857e3f
75fff72
991f49d
cba375e
802aed9
892eeb7
d14ceda
b7f4aaf
fabe96d
33eb7c3
5954576
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
{ | ||
"name": "react-messenger-19th", | ||
"name": "react-messenger-20th", | ||
"version": "0.1.0", | ||
"private": true, | ||
"dependencies": { | ||
|
@@ -8,13 +8,18 @@ | |
"@testing-library/user-event": "^13.5.0", | ||
"@types/jest": "^27.5.2", | ||
"@types/node": "^16.18.91", | ||
"@types/react": "^18.2.69", | ||
"@types/react-dom": "^18.2.22", | ||
"@types/react": "^18.3.8", | ||
"@types/react-dom": "^18.3.0", | ||
"lucide-react": "^0.454.0", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"react-router-dom": "^6.26.2", | ||
"react-scripts": "5.0.1", | ||
"react-window": "^1.8.10", | ||
"tailwind-scrollbar-hide": "^1.1.7", | ||
"typescript": "^4.9.5", | ||
"web-vitals": "^2.1.4" | ||
"web-vitals": "^2.1.4", | ||
"zustand": "^5.0.0-rc.2" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 헉!! 전역 상태 관리에 zustatnd를 사용하셨군요... 뭔가 제가 다뤄보지 않은 기술을 사용하시는 걸 보면 항상 멋있는 것 같습니다... 👍🏻 그동안 Context API랑 대체 뭔 차이인지 아리송한 부분이 있었는데 전체적인 개념은 비슷할지언정 리렌더링 측면에서 훨씬 효율적이네요... 가볍기두 하고용... 반드시 공부해서 사용해 봐야겠습니다 여담이지만... 항상 이른 순서로 과제 제출하시는 게 참... 대단한 것 같습니다 🔥 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 zustand 라이브러리는 카토 다이시라는 일본인 프로그래머가 만든 전역 상태 관리 라이브러리로, 이 분께서 jotai 같은 라이브러리도 만드셨어요! |
||
}, | ||
"scripts": { | ||
"start": "react-scripts start", | ||
|
@@ -39,5 +44,14 @@ | |
"last 1 firefox version", | ||
"last 1 safari version" | ||
] | ||
}, | ||
"devDependencies": { | ||
"@svgr/core": "^8.1.0", | ||
"@svgr/plugin-jsx": "^8.1.0", | ||
"@svgr/webpack": "^8.1.0", | ||
"autoprefixer": "^10.4.20", | ||
"postcss": "^8.4.47", | ||
"postcss-loader": "^8.1.1", | ||
"tailwindcss": "^3.4.13" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module.exports = { | ||
plugins: { | ||
tailwindcss: {}, | ||
autoprefixer: {}, | ||
}, | ||
} | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React from 'react'; | ||
import TNB from '../../components/Navigation/TNB'; | ||
import ListChat from '../../components/List/ListChat'; | ||
import useChatStore from '../../zustand/userStore'; | ||
|
||
|
||
export default function ChatList() { | ||
const { users } = useChatStore(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저랑 같은 zustand를 사용하셨네요!! |
||
return ( | ||
<div className='w-full h-full flex flex-col justify-center items-center'> | ||
{/* ChatList TNB */} | ||
<TNB name='chatlist'/> | ||
<div className='w-full h-[40px] flex justify-between items-center px-4 py-2'> | ||
<p className='text-title-2'>Messages</p> | ||
<p className='text-body-2-b text-gray500'>Requests</p> | ||
</div> | ||
|
||
{/* ChatList User */} | ||
<section className='w-full h-[636px] overflow-y-auto scollbar-hide'> | ||
{users.map((user) => ( | ||
<ListChat key={user.user_id} user={user} /> | ||
))} | ||
</section> | ||
</div> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import React, { useEffect, useRef, useState } from 'react'; | ||
import { useParams } from 'react-router-dom'; | ||
import TNB from '../../components/Navigation/TNB'; | ||
import TextBubble from '../../components/ChatBar/TextBubble'; | ||
import useChatStore from '../../zustand/userStore'; | ||
import ChatBar from '../../components/ChatBar/ChatBar'; | ||
import { User } from '../../types/types'; | ||
|
||
|
||
|
||
const ChatRoom = () => { | ||
const { username } = useParams(); // URL에서 유저명을 추출 | ||
const [userChange, setUserChange] = useState<boolean>(false); | ||
const { users, getUserMessages } = useChatStore(); | ||
|
||
|
||
// 현재 시각 | ||
const formatTime = () => { | ||
const date = new Date(); | ||
const options: Intl.DateTimeFormatOptions = { | ||
month: 'short', | ||
day: 'numeric', | ||
hour: 'numeric', | ||
minute: 'numeric', | ||
hour12: true // 12시간제로 표시 (AM/PM) | ||
}; | ||
return date.toLocaleString('en-US', options).toUpperCase(); // 예: "SEP 2 AT 3:26 PM" | ||
}; | ||
|
||
let currentUser = users.find(user => user.userName === username); | ||
|
||
|
||
|
||
// 현재 유저의 메시지를 zustand로부터 가져옴 | ||
const messages = currentUser ? getUserMessages(currentUser.user_id) : []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. currentUser를 users 배열에서 찾은 후, userChange 상태에 따라 덮어쓰는 로직이 한 곳에서 함께 처리되는 이 부분을 아래 혜인 언니의 말처럼 set 함수를 사용해 상태를 업데이트하여 ChatRoom 컴포넌트에서 상태를 직접 관리하는 대신, Zustand 스토어에서 처리하게 하는 거 어떨까용?!!! 🔥 |
||
|
||
// 유저 변경 시 currentUser 변경 | ||
if (userChange) { | ||
currentUser = { | ||
user_id: 5, | ||
userName: 's.ol_lala', | ||
displayName: 'minsol', | ||
profileImage: require('../../assets/Image/profile.jpg'), | ||
posts: 0, | ||
followers: 1000, | ||
following: 1000, | ||
} as User; | ||
} | ||
Comment on lines
+38
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요부분 set함수로 바꾸는게 더 직관적이지 않을까 싶습니다! |
||
// 스크롤 자동 하강 | ||
const messagesEndRef = useRef<HTMLDivElement>(null); | ||
|
||
useEffect(() => { | ||
const timeout = setTimeout(() => { | ||
if (messagesEndRef.current) { | ||
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' }); | ||
} | ||
}, 500); // 100ms의 짧은 지연 시간 설정 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오!! 영준 님의 스크롤이 뭐랄까 되게 부드럽게 내려간다고 생각했는데 지연 시간을 짧게 설정해서 가능했던 거군요 ㅎㅎ UX가 훨씬 개선되는 것 같아요 👍🏻 |
||
|
||
return () => { | ||
clearTimeout(timeout); | ||
}; | ||
|
||
}, [messages, userChange]); | ||
const handleChangeUser = () => { | ||
setUserChange(!userChange); | ||
} | ||
|
||
return ( | ||
<div className="w-full h-full flex flex-col"> | ||
{/* ChatRoom TNB 렌더링 속도차이로 인한 조건문 header*/} | ||
{currentUser && <TNB name="chatroom" user={currentUser} handleChangeUser={handleChangeUser} />} | ||
|
||
{/* middle */} | ||
<div className={`w-full h-full flex flex-col overflow-y-auto scrollbar-hide !important justify-between`}> | ||
{/* ChatRoom User Description */} | ||
<div className={`flex flex-col items-center gap-3 pt-8 px-12`}> | ||
<div className='w-[279px] flex flex-col items-center gap-2'> | ||
<img src={currentUser?.profileImage} alt="Profile" className="w-[96px] h-[96px] rounded-full cursor-pointer"/> | ||
<p className='h-[21px] text-center text-title-2 text-black'>{currentUser?.userName}</p> | ||
<span className='w-full text-body-2-m text-gray500 text-center self-stretch'> | ||
<p>{currentUser?.displayName}</p> | ||
<p>{currentUser?.followers} followers · {currentUser?.posts} posts</p> | ||
<p>You don’t follow each other on Instagram</p> | ||
</span> | ||
</div> | ||
Comment on lines
+75
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이렇게 컴포넌트를 분리해도 될만한 부분은 컴포넌트 분리해서 불러오면 좀더 가독성이 좋을 것 같습니당 |
||
<button className='w-[112px] h-[32px] flex justify-center items-center px-4 py-[5px] rounded-lg bg-gray100 text-caption text-gray600 hover:bg-gray200 cursor-pointer'>View Profile</button> | ||
</div> | ||
|
||
{/* Spacer 요소 */} | ||
<div style={{ minHeight: '200px' }} /> | ||
|
||
{/* ChatRoom Chat */} | ||
<div className='w-full flex flex-col items-center justify-end space-y-5'> | ||
{/* 채팅 시작 시각 */} | ||
<span className='w-[108px] h-[16px] text-center text-body-3 text-gray500'>{formatTime()}</span> | ||
{/* 채팅 내용 */} | ||
<div className='w-full flex flex-col space-y-3 px-3'> | ||
{messages.map((message, index) => ( | ||
<TextBubble | ||
key={message.id} | ||
text={message.text} | ||
isMine={message.isMine} | ||
user={currentUser} | ||
index={index} | ||
userChange={userChange} | ||
/> | ||
))} | ||
</div> | ||
{/* 스크롤 이동을 위한 빈 div */} | ||
<div ref={messagesEndRef} /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 채팅 맨 아래의 빈 div를 참조하게 해서 useEffect로 새로운 메세지가 입력될 때마다 자동으로 스크롤되게 구현하셨군요! 이런 방법도 참고해 봐야겠습니다 ㅎㅎ 저는 |
||
</div> | ||
</div> | ||
{/* Input Chatting Bar footer*/} | ||
<div className='py-2'> | ||
<ChatBar/> | ||
</div> | ||
</div> | ||
|
||
|
||
); | ||
}; | ||
|
||
export default ChatRoom; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import React from 'react'; | ||
import { Outlet, useLocation } from 'react-router-dom'; | ||
import GNB from '../../components/Navigation/GNB'; | ||
import ChatBar from '../../components/ChatBar/ChatBar'; | ||
// import NavBar from './NavBar'; // 하단의 네비게이션 바 | ||
|
||
const Layout = () => { | ||
const location = useLocation(); // 현재 경로 정보를 가져옴 | ||
const isLocationCheck = location.pathname.startsWith("/chatting/chatRoom"); | ||
|
||
return ( | ||
<div className="flex flex-col w-[375px] h-[100vh] shadow-lg bg-white m-auto overflow-hidden"> | ||
<main className={` | ||
w-full h-full | ||
`}> | ||
<Outlet /> {/* 자식 Route 컴포넌트가 여기에서 렌더링됩니다 */} | ||
</main> | ||
{/* <NavBar /> */} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 NavBar를 주석처리하신 이유가 따로 있으시ㄹ까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 궁금합니다!! 🔎 |
||
{!isLocationCheck && <GNB />} | ||
</div> | ||
); | ||
}; | ||
|
||
export default Layout; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,43 @@ | ||||||||||||||||||||||||||||||||||||||||||||
import React from "react"; | ||||||||||||||||||||||||||||||||||||||||||||
import profileImage from '../../assets/Image/profile.jpg' | ||||||||||||||||||||||||||||||||||||||||||||
import { ReactComponent as LinkIcon } from '../../assets/svg/link.svg' | ||||||||||||||||||||||||||||||||||||||||||||
import { useNavigate } from "react-router-dom"; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
export default function Description() { | ||||||||||||||||||||||||||||||||||||||||||||
const navigate = useNavigate(); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||
<div className='w-full h-[243px] flex flex-col items-center'> | ||||||||||||||||||||||||||||||||||||||||||||
<header className='w-full h-[110px] flex items-center gap-[21px] pl-3 pr-[26px] py-4'> | ||||||||||||||||||||||||||||||||||||||||||||
<img src={profileImage} alt='profile' className='w-[88px] h-[88px] rounded-full cursor-pointer'/> | ||||||||||||||||||||||||||||||||||||||||||||
<div className='w-[228px] h-[40px] inline-flex gap-8'> | ||||||||||||||||||||||||||||||||||||||||||||
<span className='text-center cursor-pointer'> | ||||||||||||||||||||||||||||||||||||||||||||
<p className='text-title-2'>3</p> | ||||||||||||||||||||||||||||||||||||||||||||
<p className='text-body-2-m'>posts</p> | ||||||||||||||||||||||||||||||||||||||||||||
</span> | ||||||||||||||||||||||||||||||||||||||||||||
<span onClick={()=>navigate("/profile/followList")} className='text-center cursor-pointer'> | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 button 태그를 쓰지 않고 span으로 처리한 이유가 있으신가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 음 그리고 개인적으로, 저라면, 요 부분을 따로 빼서 중복을 줄일 것 같습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혜인 언니가 시멘틱 태그 짚어주셨는데 제 과제에서도 div를 습관적으로 써서 지금 div 밭이라 얼른 수정해야 할 것 같네용... 매번 까먹는 시멘틱 태그지만 기본적인 거일 수록 항상 중요한 것 같아용... 🥹 |
||||||||||||||||||||||||||||||||||||||||||||
<p className='text-title-2'>1000</p> | ||||||||||||||||||||||||||||||||||||||||||||
<p className='text-body-2-m'>followers</p> | ||||||||||||||||||||||||||||||||||||||||||||
</span> | ||||||||||||||||||||||||||||||||||||||||||||
<span onClick={()=>navigate("/profile/followList")} className='text-center cursor-pointer'> | ||||||||||||||||||||||||||||||||||||||||||||
<p className='text-title-2'>1000</p> | ||||||||||||||||||||||||||||||||||||||||||||
<p className='text-body-2-m'>following</p> | ||||||||||||||||||||||||||||||||||||||||||||
</span> | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+22
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이렇게 말이에요!! 저는 개인적으로 겹치는 컴포넌트 (그게 디자인이든 로직이든 관계없이)가 있으면 일단 최대한 공통으로 묶을 수 있는지부터 생각하는 편이라서 요렇게 제안드려 볼게요! |
||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
</header> | ||||||||||||||||||||||||||||||||||||||||||||
<section className='w-full h-[143px] flex flex-col gap-2 items-start px-4 py-3'> | ||||||||||||||||||||||||||||||||||||||||||||
<div> | ||||||||||||||||||||||||||||||||||||||||||||
<p className='h-[19px] text-body-2-b self-stretch'>민솔</p> | ||||||||||||||||||||||||||||||||||||||||||||
<p className='h-[19px] text-body-2-m self-stretch'>반갑습니다</p> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
<div className='flex gap-2 items-center h-[18px] text-caption text-main'> | ||||||||||||||||||||||||||||||||||||||||||||
<LinkIcon className='fill-current text-main'/> | ||||||||||||||||||||||||||||||||||||||||||||
<a href="https://www.instagram.com/confiwns_/">https://www.instagram.com/confiwns_/</a> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
</section> | ||||||||||||||||||||||||||||||||||||||||||||
<footer className='px-4'> | ||||||||||||||||||||||||||||||||||||||||||||
<button className='w-full h-[37px] inline-flex justify-center items-center gap-[10px] px-[135px] py-2 rounded-lg bg-gray100 text-gray600 text-center text-caption hover:bg-gray200'>Edit Profile</button> | ||||||||||||||||||||||||||||||||||||||||||||
</footer> | ||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
우왁 처음 보는 라이브러리이네용... 이름만 보고 생각했을 때는 윈도우? 반응형인가 싶긴 했는데 찾아보니 메모리 사용량 측에서 매우 최적화 시킬 수 있는 라이브러리이군요!!! 이게 이번 key-question 2번의 대한 답을 찾다가 lazy-loading 기법과 어떤 차이인 건지 이것도 약간 아리송 ㅎㅎ 했었는데 DOM에 요소가 계속해서 쌓이고 안 쌓이고의 차이라 메모리 성능 최적화를 위해서는 window을 사용한다고 이해했습니다. 이미 알고 계실지 모르겠지만 react-window를 찾아보다 보니 react-virtualized라는 것도 찾아볼 수 있었는데 window와 비교해 알아보니 각각의 장단점을 훨씬 더 잘 이해할 수 있었어용 아직 모르신다면 찾아보시는 걸 추천 드리며...... ✨