[인스타클론코딩] [FrontEnd].13 - Profile 파트 만들기
by 한만섭
정리할 내용
- [] profile route Container, Presenter, Queries, index로 나누기
- [] route의 : 뭔지 찾아보기
- [] graphql에서 count하는 방법정리 ( UserConnection , aggregate , count
게시물의 Username 누르면 프로필 보여주기
링크로 /username
으로 보내줍니다.
//ProfilePresenter.js
<UserColumn>
<Link to={`/${username}`}>
<FatText text={username} />
</Link>
<Location>{location}</Location>
</UserColumn>
유저 검색의 아이디를 누르면 프로필 보여주기
//UserCard.js
export default ({ id, username, avatar, isFollowing, isSelf }) => (
<Wrapper>
<Avatar size={"md"} url={avatar} />
<Link to={`/${username}`}>
<FatText text={`${username}`} />
</Link>
{!isSelf &&
(isFollowing ? (
<FollowButton isFollowing={isFollowing} id={id} />
) : (
<FollowButton isFollowing={isFollowing} id={id} />
))}
</Wrapper>
);
그렇다면 /username
이 어떻게 프로필을 보여주는지 정리해보겠습니다. 우선 프로필을 보여준다는 것은 특정 router를 보여준다는 것이겠죠? 그 route는
profile일 것입니다.
아래는 Route.js의 일부인데, logedInRouter는 로그인 상태일 때 보여주는 Router입니다. Switch는 여러 Route중에서 하나를 고르는 것입니다. 그 중에 먼저 걸리는 것으로 보내주죠. :의 의미는 정리해봐야 할 것 같습니다.
//Route.js
const LogedInRouter = () => (
<Switch>
<Route exact path="/" component={Feed} />
<Route path="/explore" component={Explore} />
<Route path="/notifications" component={Notifications} />
<Route path="/search" component={Search} />
<Route path="/:username" component={Profile} />
</Switch>
);
(...중략...)
유저를 누르게 되면 프로필 컴포넌트로 향하게 됩니다.
여기서도 withRouter를 사용해서 match의 param정보를 얻어와서 Query요청을 보냈습니다.
대부분의 루틴이 url로 요청을 하면 해당 url로 보내고 그 route는 router객체가 보낸 match history location을 사용해서 서버에 데이터를 요청하는 방식을 사용합니다.
// ProfileContainer.js
import React from "react";
import ProfilePresenter from "./ProfilePresenter";
import { useQuery, useMutation } from "react-apollo-hooks";
import { withRouter } from "react-router-dom";
import { SEE_USER, LOCAL_LOG_Out } from "./ProfileQueries";
export default withRouter(({ match: { params: { username } } }) => {
const { data, loading } = useQuery(SEE_USER, {
variables: {
username
}
});
const logOut = useMutation(LOCAL_LOG_Out);
return <ProfilePresenter logOut={logOut} data={data} loading={loading} />;
});
ProfileQueries.js
Query에서는 logout을 하는 부분이 평소와 다릅니다. logout은 client에서 만든 resolver입니다. client것을 사용하기 위해서는 @clinet
를 붙혀줍니다.
//ProfileQueries.js
import { gql } from "apollo-boost";
export const SEE_USER = gql`
query seeUser($username: String!) {
seeUser(username: $username) {
id
avatar
username
fullName
posts {
id
likeCount
commentCount
files {
id
url
}
}
postCount
followingCount
followerCount
isFollowing
isSelf
}
}
`;
export const LOCAL_LOG_Out = gql`
mutation {
logUserOut @client
}
`;
ProfilePresenter.js
Presenter에서 많이 헛수고를 했다…
이유는 Container에서 서버에 요청한 데이터가 없는데 사용하려고 해서이다. 정확한 이유는 더 알아봐야겠지만 컴포넌트가 3번정도 render를 하려고하는데 첫번째와 두번째에는 데이터가 존재하지 않고 세번째 때 데이터가 응답되는데 그 데이터를 사용하려고 해서 에러가 발생하는 것 같습니다.
- 해결방법
- if문으로 loading인지 부터 체크함.
- data.seeUser이 존재할 때 만 데이터를 사용하는 방식 적용
import React from "react";
import styled from "styled-components";
import Avatar from "../../Components/Avatar";
import Loader from "../../Components/Loader";
import FatText from "../../Components/FatText";
import PostCard from "../../Components/PostCard";
import Button from "../../Components/Button";
const Wrapper = styled.div`
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
`;
const Header = styled.div`
display: flex;
justify-content: flex-start;
margin-bottom: 50px;
`;
const HeaderColumn = styled.div`
display: flex;
flex-direction: column;
justify-content: space-around;
&:first-child {
margin: 0 100px;
}
`;
const HeaderList = styled.ul`
display: flex;
`;
const HeaderItem = styled.li`
font-size: 16px;
margin-right: 20px;
`;
const PostContainer = styled.div`
display: grid;
grid-template-columns: repeat(3, 1fr);
`;
const MetaItem = styled.li`
opacity: 0.5;
cursor: pointer;
transition: opacity 0.2s linear;
margin-right: 50px;
padding: 15px 10px;
border-top: 2px solid rgb(0, 0, 0, 0);
`;
const MetaList = styled.ul`
border-top: ${props => props.theme.boxBorder};
margin-bottom: 50px;
display: flex;
justify-content: center;
${MetaItem}:hover {
border-top: 2px solid rgb(0, 0, 0, 1.2);
opacity: 1;
}
`;
export default ({ loading, data, logOut }) => {
if (loading) {
return (
<Wrapper>
<Loader />
</Wrapper>
);
}
if (!loading && data && data.seeUser) { // 이렇게 강제로 데이터가 있을 경우에만 데이터 적용
console.log(data.seeUser.posts);
const { seeUser: user } = data;
return (
<Wrapper>
<Header>
<HeaderColumn>
<Avatar size="lg" url={user.avatar} />
</HeaderColumn>
<HeaderColumn>
<HeaderList>
<HeaderItem>
<FatText size="28" text={user.username} />
</HeaderItem>
{user.isSelf && (
<HeaderItem>
<Button onClick={logOut} text="로그아웃" />
</HeaderItem>
)}
</HeaderList>
<HeaderList>
<HeaderItem>
게시물 <FatText text={Number(user.postCount)} />
</HeaderItem>
<HeaderItem>
팔로워 <FatText text={Number(user.followerCount)} />
</HeaderItem>
<HeaderItem>
팔로잉 <FatText text={Number(user.followingCount)} />
</HeaderItem>
</HeaderList>
<HeaderList>
<HeaderItem>
<FatText text={user.fullName} />
</HeaderItem>
</HeaderList>
</HeaderColumn>
</Header>
<MetaList>
<MetaItem>게시물</MetaItem>
<MetaItem>저장됨</MetaItem>
<MetaItem>태그됨</MetaItem>
</MetaList>
<PostContainer>
{user.posts.map(post => (
<PostCard
key={post.id}
url={post.files[0].url}
likeCount={post.likeCount}
commentCount={post.commentCount}
/>
))}
</PostContainer>
</Wrapper>
);
}
};
Subscribe via RSS