패션사이트 피드 클론 -Head 작성 및 Feed List 목록 구현-

Main Image

d.code 패션 피드 구현

- Head 작성 및 Feed List 목록 구현 -

목차

  1. 라이브러리 인스톨하기

  2. Head 작성하기

  3. 파일 정리하기

  4. FeedList 폴더 및 파일 스트럭쳐 구성하기


라이브러리 인스톨하기

1
2
cd d-code-feed
npm i react-helemt

Head 작성하기

위치 : src/components/Head.jsx

  • 요구사항 : 피드를 페이스북에 공유하는 기능 추가하기
  • 페이스북에 공유하기 기능을 추가합니다.
  • 페이스북이 제공하는 open graph 공유 기능을 이용하기 위해서 <head>~</head> 사이에 meta를 집어넣습니다.
  • url의 위치에 따라서 meta가 변해야만 합니다. 따라서 react-helmet을 이용하여 동적으로 meta를 바꿔주도록 하겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react';
import { Helmet } from 'react-helmet';

const Head = ({ keywords, description, title, favicon }) => {
return (
<Helmet>
<meta name="description" content={description} />
<meta name="keywords" content={keywords} />
<title>{title}</title>
<meta property="og:title" content={title} />
<meta property="og:image" content={favicon} />
<meta property="og:site_name" content="디코드(d.code) 패션 셀렉트샵" />
<meta property="og:description" content={description} />
</Helmet>
);
};
export default Head;

Feed page에 Head 컴포넌트 삽입하기

위치 : src/components/Feed.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React from 'react';
import Head from '../components/Head';
import Header from '../components/Header';
import FeedListContainer from '../containers/FeedListContainer';
import Footer from '../components/Footer';

const Feed = () => {
return (
<>
<Head
keywords="d.code, Feed"
description="최신 패션 뉴스를 전해드립니다."
title="디코드(d.code) 패션 셀렉트샵"
favicon="/static/favicon.ico"
/>
<Header />
</>
);
};

export default Feed;

파일 정리하기

  1. containers/FeedContainer.jsx -> containers/FeedListContainer 로 변경

  2. Test.jsx 삭제

  3. containers/FeedListContainer에 props로 줄 컴포넌트 변경하기

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import { connect } from 'react-redux';
    import { getFeeds } from '../redux/modules/feed';
    import FeedList from '../components/FeedList';

    const mapStateToProps = state => ({
    feeds: state.feed.feeds,
    loading: state.feed.loading,
    error: state.feed.error,
    });

    const mapDispatchToProps = dispatch => ({
    getFeed: () => {
    dispatch(getFeeds());
    },
    });

    export default connect(mapStateToProps, mapDispatchToProps)(FeedList);
  4. pages/Feed.jsx 에서도 container 바꿔주기

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import React from 'react';
    import Head from '../components/Head';
    import Header from '../components/Header';
    import FeedListContainer from '../containers/FeedListContainer';
    import Footer from '../components/Footer';

    const Feed = () => {
    return (
    <>
    <Head
    keywords="d.code, Feed"
    description="최신 패션 뉴스를 전해드립니다."
    title="디코드(d.code) 패션 셀렉트샵"
    favicon="/static/favicon.ico"
    />
    <Header />
    <FeedListContainer />
    </>
    );
    };

    export default Feed;

FeedList 폴더 및 파일 스트럭쳐 구성하기

위치 : src/components/FeedList/

FeedList-chart

FeedList-Structure

Styles.jsx

  • FeedList 영역에서 사용되는 모든 styleStytled Components 를 이용하여 관리하고 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import styled from 'styled-components';

// FeedList/index.jsx
export const StyledMain = styled.main`
padding-top: 7.2rem;
width: 144rem;
margin: 0 auto;
margin-top: 10.7rem;
`;

// FeedList/MainTitle.jsx
export const StyledMainTitle = styled.div`
h2 {
font-size: 3.2rem;
line-height: 1.5;
font-weight: 900;
}

p {
line-height: 1.5;
font-weight: 700;
}
`;

// FeedList/index.jsx
export const StyledFeedArticle = styled.article`
display: inline-block;
width: 32%;
margin-top: 2.5rem;
`;

// FeedList/index.jsx
export const StyledContent = styled.div`
width: 100%;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
`;

// FeedList/FeedContent.jsx
export const StyledFeedImg = styled.img`
width: 100%;
`;

// FeedList/FeedContent.jsx
export const StyledFeedTagBox = styled.div`
font-size: 2.4rem;
line-height: 1.3;
font-weight: 900;
margin: 1rem 0;

span {
margin-right: 1rem;
}
`;

// FeedList/FeedContent.jsx
export const StyledFeedContent = styled.p`
white-space: pre-line;
line-height: 1.5;
word-break: keep-all;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 10;
-webkit-box-orient: vertical;
`;

// FeedList/FeedFooter.jsx
export const StyledCountSpan = styled.span`
margin-left: 1rem;
margin-right: 1rem;
`;

// FeedList/FeedFooter.jsx
export const StyledFeedMdInfo = styled.figure`
img {
width: 10rem;
float: left;
}

figcaption {
line-height: 1.5;
font-style: italic;
}
`;

// FeedList/FeedFooter.jsx
export const StyledFeedFooter = styled.footer`
color: #a6a7a9;
margin-top: 1rem;
padding-left: 25%;

span {
font-style: italic;
}

div {
margin-bottom: 0.3rem;
}
`;

// FeedList/FeedFooter.jsx
export const StyledShareButton = styled.a.attrs(({ id }) => ({
role: 'button',
target: '_blank',
withoutrel: 'noopener noreferrer',
href: `http://www.facebook.com/sharer/sharer.php?u=https://www.itsdcode.com/feed/${id}`,
}))`
background: #1877f2;
border-radius: 0.3rem;
border: none;
font-size: 11px;
height: 2rem;
padding: 0.3rem 0.6rem;
color: white;

svg {
margin-bottom: 0.1rem;
}

span {
font-style: normal;
margin-left: 0.5rem;
}
`;

index.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React, { useEffect } from 'react';
import { StyledMain, StyledContent, StyledFeedArticle } from './Styles';
import { v4 as uuidv4 } from 'uuid';
import MainTitle from './MainTitle';
import FeedHeader from './FeedHeader';
import FeedContent from './FeedContent';
import FeedFooter from './FeedFooter';

const FeedList = ({ feeds, loading, error, getFeed }) => {
useEffect(() => {
getFeed();
}, [getFeed]);

return (
<StyledMain>
<MainTitle />
<StyledContent>
{feeds &&
feeds.list.map(feed => (
<StyledFeedArticle key={uuidv4()}>
<FeedHeader feed={feed} />
<FeedContent feed={feed} />
<FeedFooter feed={feed} />
</StyledFeedArticle>
))}
</StyledContent>
</StyledMain>
);
};

export default FeedList;

MainTitle.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';
import { StyledMainTitle } from './Styles';

const MainTitle = () => {
return (
<StyledMainTitle>
<h2>Feed</h2>
<p>최신 패션 뉴스를 전해드립니다.</p>
</StyledMainTitle>
);
};

export default MainTitle;

FeedHeader.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react';
import { v4 as uuidv4 } from 'uuid';

const FeedHeader = ({ feed }) => {
return (
<header>
<h3 key={uuidv4()} className="a11y-hidden">
{feed.tags.map(tag => `${tag} `)}
</h3>
</header>
);
};

export default FeedHeader;

FeedContent.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React from 'react';
import { Link } from 'react-router-dom';
import { StyledFeedImg, StyledFeedTagBox, StyledFeedContent } from './Styles';
import { v4 as uuidv4 } from 'uuid';

const FeedContent = ({ feed }) => {
return (
<Link to={`/feed/${feed.id}`}>
<StyledFeedImg src={feed.mediaList[0].url} alt="images" />
<StyledFeedTagBox>
{feed.tags.map(tag => (
<span key={uuidv4()}>{`#${tag}`}</span>
))}
</StyledFeedTagBox>
<StyledFeedContent>{feed.text}</StyledFeedContent>
</Link>
);
};

export default FeedContent;

FeedFooter.jsx

  • 페이스북의 공유기능에서 local로 설정시 공유가 안되는 문제를 발견했습니다.
  • 따라서 일단은 실제 d.code와 연결해서 공유를 해놓았고 링크는 선택한 피드의 id값을 토대로 url에 집어넣어 공유되도록 설정하였습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React from 'react';
import { HeartIcon, CommentAltIcon, LinkIcon, FacebookIcon } from '../Icons';
import { StyledFeedFooter, StyledFeedMdInfo, StyledCountSpan, StyledShareButton } from './Styles';

const FeedFooter = ({ feed }) => {
return (
<StyledFeedFooter>
<StyledFeedMdInfo>
<img src={feed.mdInfo.mdThumb} alt="MD 사진" />
<figcaption>MD : {feed.mdInfo.mdName}</figcaption>
</StyledFeedMdInfo>
<span>{feed.createdAt.split(' ')[0]}</span>
<div>
<HeartIcon />
<StyledCountSpan>{feed.likedCount ? feed.likedCount : '0'}</StyledCountSpan>
<CommentAltIcon />
<StyledCountSpan>{feed.replyCount ? feed.replyCount : '0'}</StyledCountSpan>
<LinkIcon />
<StyledCountSpan>{feed.sharedCount ? feed.sharedCount : '0'}</StyledCountSpan>
</div>
<StyledShareButton id={feed.id}>
<FacebookIcon />
<span>공유하기</span>
</StyledShareButton>
</StyledFeedFooter>
);
};

export default FeedFooter;

Share