Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions React/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.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);
}
}
36 changes: 36 additions & 0 deletions React/src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { MediaDiv, Main } from './styledComponent';
import { ThemeProvider } from 'styled-components';
import { darkTheme, GlobalStyles, lightTheme } from './styles';
import { useEffect, useState } from 'react';
import { Route, Routes } from 'react-router-dom';
import Header from './Header';
import Slogun from './Slogun';
import ShowPostList from './ShowPostList';
import Footer from './Footer';
import ShowPost from './ShowPost';
import WritePost from './WritePost';

function App() {
const [darkMode, setDarkMode] = useState(true);

return (
<>
<ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
<GlobalStyles />
<MediaDiv>
<Header darkMode={darkMode} setDarkMode={setDarkMode}></Header>
<Main>
<Slogun></Slogun>
<Routes>
<Route exact path="/" element={<ShowPostList></ShowPostList>} />
<Route path="/write" element={<WritePost></WritePost>} />
<Route path="/post/:postID" element={<ShowPost></ShowPost>} />
</Routes>
</Main>
<Footer></Footer>
</MediaDiv>
</ThemeProvider>
</>
);
}
export default App;
8 changes: 8 additions & 0 deletions React/src/App.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
22 changes: 22 additions & 0 deletions React/src/EachPost.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { EachPostLi, PostLink, PostRepl } from './styledComponent';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faLocationPin } from '@fortawesome/free-solid-svg-icons';
function EachPost({ title, postID }) {
const navigate = useNavigate();

const goPost = () => {
navigate(`${'/post/' + postID}`);
};

return (
<EachPostLi onClick={goPost}>
<div>
<FontAwesomeIcon icon={faLocationPin} />
<PostLink>{title}</PostLink>
</div>
</EachPostLi>
);
}
20 changes: 20 additions & 0 deletions React/src/Footer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react'
import {
FooterDiv,
FooterBig,
FooterSmall,
} from './styledComponent';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faReact } from '@fortawesome/free-brands-svg-icons';

function Footer() {
return (
<FooterDiv>
<FontAwesomeIcon icon={faReact} />
<FooterBig>for react study</FooterBig>
<FooterSmall>2022. by chans</FooterSmall>
</FooterDiv>
)
}

export default Footer
42 changes: 42 additions & 0 deletions React/src/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import {
HeaderDiv,
TitleLogoDiv,
TitleBig,
TitleSmall,
SubHeaderDiv,
CursorDiv,
} from './styledComponent';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSun, faMoon } from '@fortawesome/free-solid-svg-icons';

import { useNavigate } from 'react-router-dom';
function Header({ darkMode, setDarkMode }) {
const navigate = useNavigate();

const goHome = () => {
navigate('/');
};
const toggleDarkMode = () => {
setDarkMode((darkMode) => !darkMode);
};
return (
<HeaderDiv>
<CursorDiv>
<TitleLogoDiv onClick={goHome}>
<TitleBig>멋사</TitleBig>
<TitleSmall>익명게시판</TitleSmall>
</TitleLogoDiv>
</CursorDiv>
<SubHeaderDiv>
{darkMode ? (
<FontAwesomeIcon onClick={toggleDarkMode} icon={faSun} />
) : (
<FontAwesomeIcon onClick={toggleDarkMode} icon={faMoon} />
)}
</SubHeaderDiv>
</HeaderDiv>
);
}
export default Header;
121 changes: 121 additions & 0 deletions React/src/ShowPost.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@

import React, { useState, useEffect, useMemo } from 'react';
import { useParams } from 'react-router-dom';
import {
LoadingDiv,
LoadingImg,
PostReadDiv,
PostReplDiv,
PostSection,
PostTitle,
PostTitleDiv,
Repl,
ReplInput,
ReplSubmitDiv,
ReplTitleDiv,
Replwriter,
WritereplDiv,
} from './styledComponent';
const postData = {
title: '어렵사리 정한 제목입니다.',
contents: `내가 그때 그렇게 딱 했는데 보이는 사람은 너라는 것을 알게 되었어, 그게
나에게는 힘들었지만, 쉽내가 그때 그렇게 딱 했는데 보이는 사람은 너라는
것을 알게 되었어, 그게 나에게는 힘들었지만, 내가 그때 그렇게 딱 했는데
보이는 사람은 너라는 것을 알게 되었어, 그게 나에게는 힘들었지만, 지
않았던 만큼 더욱 성장할 수 있다는 것을 뼈저리게 느꼈지`,
};

const replData = [
{
id: 2,
contents: `내가 생각해도 그건 아니다`,
},
{
id: 5,
contents: `그럴수도 있나?? 내가 생각해도 그건 아니다`,
},
];
const countRepls = (repls) => {
console.log('리뷰 개수를 세는 중...');
return repls.length;
};
const ShowPost = (props) => {
const Params = useParams();
const [post, setPost] = useState(null);
const [repls, setRepls] = useState([]);
const [postLoading, setPostLoading] = useState(true);
const [replLoading, setReplLoading] = useState(true);

useEffect(() => {
setTimeout(() => {
setPost(postData);
setPostLoading(false);
}, 300);
}, []);
useEffect(() => {
setTimeout(() => {
setRepls(replData);
setReplLoading(false);
}, 1000);
}, []);

const [repl, setRepl] = useState('');

const onChange = (e) => {
setRepl(e.target.value);
};

// for useMemo
const replCount = useMemo(() => countRepls(repls), [repls]);
// const replCount = countRepls(repls);

if (!Params.postID) {
return <PostSection>잘못된 접근입니다.</PostSection>;
}
return (
<div>
<PostSection>
<PostTitleDiv>
<PostTitle>
{/* title */}
{post && post.title}
</PostTitle>
</PostTitleDiv>

{postLoading ? (
<LoadingDiv>
<LoadingImg src={`${process.env.PUBLIC_URL}/img/loading.svg`} />
</LoadingDiv>
) : (
<PostReadDiv>{post && post.contents} </PostReadDiv>
)}

{/* post contents */}

<ReplTitleDiv>댓글 {replCount}</ReplTitleDiv>
{replLoading ? (
<LoadingDiv>
<LoadingImg src={`${process.env.PUBLIC_URL}/img/loading.svg`} />
</LoadingDiv>
) : (
repls &&
repls.map((element) => (
<PostReplDiv key={element.id}>
<Replwriter>익명</Replwriter>
<Repl>{element.contents}</Repl>
</PostReplDiv>
))
)}

<WritereplDiv>
<ReplInput onChange={onChange} value={repl}></ReplInput>
<ReplSubmitDiv>
<span>입력</span>
</ReplSubmitDiv>
</WritereplDiv>
</PostSection>
</div>
);
};

export default ShowPost;
Loading