From 906dabca02b1020b47bee1424ad386b675b76450 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 11:24:39 +0900 Subject: [PATCH 01/90] Added channel about router MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 라우터에 채널 어바웃을 추가했습니다. --- src/appRoute.js | 1 + src/pages/general/channel/ChannelAbout.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/appRoute.js b/src/appRoute.js index 9af8142..0947b4a 100644 --- a/src/appRoute.js +++ b/src/appRoute.js @@ -91,6 +91,7 @@ export const homeLinks = [ { to: urls.home, children: '추천' }, { to: urls.webtoon, children: '웹툰' }, { to: urls.webnovel, children: '웹소설' }, + { to: urls.home, children: '잘생긴 정호 메뉴'}, ]; export const subsLinks = [ diff --git a/src/pages/general/channel/ChannelAbout.js b/src/pages/general/channel/ChannelAbout.js index 19828e3..c81ca16 100644 --- a/src/pages/general/channel/ChannelAbout.js +++ b/src/pages/general/channel/ChannelAbout.js @@ -1,5 +1,5 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; -import channelAboutData from '../../../tempData/channel/channelAbout.json'; +import ㅎ from '../../../tempData/channel/channelAbout.json'; export default function ChannelAbout() { return ( From 910c8283373a564d9cf2aa44a92e050e4edb59a3 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 13:16:19 +0900 Subject: [PATCH 02/90] Add: add react pagination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 리액트 페이지네이션을 설치 --- package.json | 1 + src/pages/general/channel/ChannelAbout.js | 2 +- yarn.lock | 9 ++++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8b82a09..60e9973 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.8.0", "react-modal": "^3.16.1", + "react-paginate": "^8.2.0", "react-router-dom": "^6.10.0", "react-scripts": "5.0.1", "react-tippy": "^1.4.0", diff --git a/src/pages/general/channel/ChannelAbout.js b/src/pages/general/channel/ChannelAbout.js index c81ca16..19828e3 100644 --- a/src/pages/general/channel/ChannelAbout.js +++ b/src/pages/general/channel/ChannelAbout.js @@ -1,5 +1,5 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; -import ㅎ from '../../../tempData/channel/channelAbout.json'; +import channelAboutData from '../../../tempData/channel/channelAbout.json'; export default function ChannelAbout() { return ( diff --git a/yarn.lock b/yarn.lock index 180bd82..8a8ea28 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7890,7 +7890,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8078,6 +8078,13 @@ react-modal@^3.16.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" +react-paginate@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-8.2.0.tgz#947c3dcb444a6c16c1bcf8361871aa135baa3dcd" + integrity sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw== + dependencies: + prop-types "^15" + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" From 8888b8a4741780033f7344ab0140132fde1e74f5 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 15:47:22 +0900 Subject: [PATCH 03/90] feat: Added page handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 포스트 탭 pagination 처리. 2. 하드코딩이었던 전체 포스트 개수를 실제 데이터로 바꿈 --- src/pages/general/channel/ChannelPosts.js | 86 ++++++- src/tempData/channel/channel.json | 60 +++-- src/tempData/channel/channelPosts.json | 262 ++++++++++++---------- 3 files changed, 259 insertions(+), 149 deletions(-) diff --git a/src/pages/general/channel/ChannelPosts.js b/src/pages/general/channel/ChannelPosts.js index c93e2ca..6ab98c5 100644 --- a/src/pages/general/channel/ChannelPosts.js +++ b/src/pages/general/channel/ChannelPosts.js @@ -1,6 +1,10 @@ import styled from 'styled-components'; import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; import channelPostsData from '../../../tempData/channel/channelPosts.json'; +import React, { useState, useEffect } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; +import {faChevronRight} from '@fortawesome/free-solid-svg-icons'; const SectionHeader = styled.div` padding: 0 0 10px; @@ -97,7 +101,7 @@ const WebtoonCount = styled.span` margin-left: 8px; color: rgba(0,0,0,.47); font-size: 12px; - + `; const WebtoonViewCountIcon = styled.img` @@ -105,11 +109,62 @@ const WebtoonViewCountIcon = styled.img` margin-right: 2px; ` +// Pagination style +const PaginationContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + margin-top: 20px; +`; + +const PaginationButton = styled.div` + min-width: 30px; + height: 30px; + padding: 4px; + margin: 0 2px; + font-weight: 500; + font-size: 14px; + //width: 30px; + display: flex; + justify-content: center; + align-items: center; + border: ${({ isSelected }) => (isSelected ? '1px solid #ccc' : '')}; + border-radius: 4px; + cursor: pointer; + background-color: white; +`; + export default function ChannelPosts() { + const [currentPage, setCurrentPage] = useState(0); + const [pageCount, setPageCount] = useState(0); + + const totalCount = channelPostsData.data.channel.chnlPostCnt; + const size = 12; + + useEffect(() => { + const calculatePageCount = () => { + setPageCount(Math.ceil(totalCount / size)); + }; + + calculatePageCount(); + }, [totalCount, size]); + + const handlePrevPage = () => { + if (currentPage > 0) { + setCurrentPage(currentPage - 1); + } + }; + + const handleNextPage = () => { + if (currentPage < pageCount - 1) { + setCurrentPage(currentPage + 1); + } + }; + return ( - - 67개의 포스트 + + {channelPostsData.data.channel.chnlPostCnt}개의 포스트 최신순 | 인기순 @@ -131,8 +186,8 @@ export default function ChannelPosts() { ♡ {post.postLikCnt} - - 21시간 전 + + 21시간 전 @@ -140,6 +195,7 @@ export default function ChannelPosts() { ))} + // 임시로 currentPage를 보여주기 위한 코드 {currentPage} 현재페이지 {/* {channelPostsData.data.channelPosts.map((post, index) => ( @@ -151,6 +207,24 @@ export default function ChannelPosts() { ))} */} + + + + + + {Array.from({ length: pageCount }).map((_, index) => ( + setCurrentPage(index)} + > + {index + 1} + + ))} + + + + ); -} +} \ No newline at end of file diff --git a/src/tempData/channel/channel.json b/src/tempData/channel/channel.json index 0927acf..898be6b 100644 --- a/src/tempData/channel/channel.json +++ b/src/tempData/channel/channel.json @@ -44,20 +44,22 @@ "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-07-03 22:10:33", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-03 22:10:40", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 19, "nextPostId": 21, "serTtl": "잡설", "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { "postId": 22, @@ -73,20 +75,22 @@ "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-07-03 22:10:33", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-03 22:10:40", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 21, "nextPostId": 23, "serTtl": "잡설", "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { "postId": 21, @@ -102,20 +106,22 @@ "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-07-03 22:10:33", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-03 22:10:40", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 20, "nextPostId": 22, "serTtl": "잡설", "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { "postId": 23, @@ -131,20 +137,22 @@ "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-07-03 22:10:33", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-03 22:10:40", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 22, "nextPostId": null, "serTtl": "잡설", "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { "postId": 19, @@ -160,20 +168,22 @@ "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-07-03 22:10:33", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-03 22:10:40", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 17, "nextPostId": 20, "serTtl": "잡설", "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { "postId": 17, @@ -189,20 +199,22 @@ "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-07-03 22:10:33", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-03 22:10:40", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 16, "nextPostId": 19, "serTtl": "잡설", "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null } ], "channelSerieses": [ diff --git a/src/tempData/channel/channelPosts.json b/src/tempData/channel/channelPosts.json index aaad171..47ef777 100644 --- a/src/tempData/channel/channelPosts.json +++ b/src/tempData/channel/channelPosts.json @@ -1,353 +1,377 @@ { "data": { "channelPosts": [ + { + "postId": 16, + "postTtl": "클라우드 컴퓨팅의 이점", + "postSbTtl": "클라우드 컴퓨팅의 이점", + "postInqrCnt": 0, + "postRepCnt": 0, + "postLikCnt": 0, + "spacInclCharCnt": null, + "postPchrgYn": true, + "postThumnPath": "https://cdn.pixabay.com/photo/2014/08/24/19/01/apps-426559_150.jpg", + "serId": 1, + "opubPlanId": null, + "pchrgBlkPurcPnt": 1000, + "ntceSettYn": true, + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", + "chnlId": 7, + "basicFontCd": "FT00110", + "basicParagAlgnCd": "AL00110", + "itdYn": true, + "paragGapMargYn": true, + "nowPostStusCd": "ST00420", + "nowPostStudChgDtm": "2023-07-04 00:18:15", + "befPostId": 15, + "nextPostId": 17, + "serTtl": "잡설", + "nowPostStusCdNm": "최초발행", + "styleText": null, + "userNic": "genius", + "userImgPath": null + }, { "postId": 28, "postTtl": "자바의 정석 정리3", - "postSbTtl": "정호 포스트 테스트25", + "postSbTtl": "자바의 정석 개념 정리3", "postInqrCnt": 0, "postRepCnt": 0, "postLikCnt": 0, "spacInclCharCnt": 0, "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2016/03/03/10/17/social-media-1233873_150.jpg", + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/06/02/16/17/5d54c9cf7f49003d5909145452d30af1.jpeg?w=360&h=270&q=65", "serId": null, "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-06-30 18:51:40", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-06-30 18:51:40", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 26, "nextPostId": null, "serTtl": null, "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { "postId": 27, "postTtl": "자바의 정석 정리2", - "postSbTtl": "정호 포스트 테스트24", + "postSbTtl": "자바의 정석 개념 정리2", "postInqrCnt": 0, "postRepCnt": 0, "postLikCnt": 0, "spacInclCharCnt": 0, "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2016/03/03/10/17/social-media-1233873_150.jpg", + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/00/1a6473c680c055e69d6f605cbb7145dc.jpeg?w=360&h=270&q=65", "serId": 5, "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-06-30 18:47:28", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-06-30 18:47:28", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": null, "nextPostId": null, "serTtl": "정호 시리즈 테스트5 edit", "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { "postId": 26, "postTtl": "자바의 정석 정리1", - "postSbTtl": "정호 포스트 테스트21", + "postSbTtl": "자바의 정석 개념 정리1", "postInqrCnt": 0, "postRepCnt": 0, "postLikCnt": 0, "spacInclCharCnt": 0, "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2016/06/09/20/38/woman-1446557_150.jpg", + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/06/09/20/09/6bf2cac77ffd3324b1f47820e36da56d.jpeg?w=360&h=270&q=65", "serId": null, "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-06-30 17:15:41", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-06-30 17:15:41", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 25, "nextPostId": 28, "serTtl": null, "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { "postId": 25, "postTtl": "컴퓨터 그래픽스 개발 기법", - "postSbTtl": "정호 포스트 테스트19", + "postSbTtl": "컴퓨터 그래픽스 개발 기법", "postInqrCnt": 0, "postRepCnt": 0, "postLikCnt": 0, "spacInclCharCnt": 0, "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2014/03/22/22/17/phone-292994_150.jpg", + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/06/09/20/12/caef117602943a9ae54ea91f8f438a4e.jpeg?w=360&h=270&q=65", "serId": null, "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-06-29 19:51:55", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-06-29 19:51:55", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 24, "nextPostId": 26, "serTtl": null, "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { "postId": 24, "postTtl": "빅데이터 처리 기술", - "postSbTtl": "정호 포스트 edit 테스트", + "postSbTtl": "빅데이터 처리 기술", "postInqrCnt": 0, "postRepCnt": 0, "postLikCnt": 0, "spacInclCharCnt": 0, "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2016/12/19/08/39/mobile-phone-1917737_150.jpg", + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/02/5b25fae0a43d80d12b4cd3990636804c.jpeg?w=360&h=270&q=65", "serId": null, "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-06-29 18:32:09", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-06-29 18:32:09", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 12, "nextPostId": 25, "serTtl": null, "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { "postId": 23, "postTtl": "블록체인 기술 개요", - "postSbTtl": "정호 포스트 테스트14", + "postSbTtl": "블록체인 기술 개요", "postInqrCnt": 0, "postRepCnt": 0, "postLikCnt": 0, "spacInclCharCnt": 0, "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2018/11/29/21/51/social-media-3846597_150.png", + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/06/22/17/48/61402e2724483712413946312.png?w=360&h=270&q=65", "serId": 1, "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-06-29 17:59:05", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-06-29 17:59:05", + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 22, "nextPostId": null, "serTtl": "잡설", "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { "postId": 22, "postTtl": "데이터 시각화 방법", - "postSbTtl": "정호 포스트 테스트11", + "postSbTtl": "데이터 시각화 방법", "postInqrCnt": 0, "postRepCnt": 0, "postLikCnt": 0, "spacInclCharCnt": 0, "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2016/11/07/00/00/maze-1804499_150.jpg", + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/07/01/08/33/f7594512ebfde6c80d3aa44d12500181.jpeg?w=360&h=270&q=65", "serId": 1, "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-06-29 14:34:25", + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": null, + "nowPostStudChgDtm": "2023-07-04 00:18:15", "befPostId": 21, "nextPostId": 23, "serTtl": "잡설", "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { - "postId": 3, - "postTtl": "데이터 분석 결과", - "postSbTtl": "멘탈 잡기 비법 대공개", + "postId": 21, + "postTtl": "모바일 앱 개발 팁", + "postSbTtl": "모바일 앱 개발 팁", "postInqrCnt": 0, "postRepCnt": 0, "postLikCnt": 0, - "spacInclCharCnt": 0, + "spacInclCharCnt": null, "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2014/03/22/22/17/phone-292994_150.jpg", + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/07/8b9125f3a5ba52114082d71597c66f2c.jpeg?w=360&h=270&q=65", "serId": 1, "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, "adoYn": false, - "postPblcDtm": "2023-06-22 13:29:07", + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, - "nowPostStusCd": "ST00410", - "nowPostStudChgDtm": "2023-06-22 13:29:07", - "befPostId": null, - "nextPostId": 13, + "nowPostStusCd": "ST00420", + "nowPostStudChgDtm": "2023-07-04 00:18:15", + "befPostId": 20, + "nextPostId": 22, "serTtl": "잡설", - "nowPostStusCdNm": "임시저장", - "styleText": null + "nowPostStusCdNm": "최초발행", + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { - "postId": 2, - "postTtl": "사용자 인터페이스 개선", - "postSbTtl": "아무거나 씁니다.", + "postId": 20, + "postTtl": "웹 디자인과 사용자 경험", + "postSbTtl": "웹 디자인과 사용자 경험", "postInqrCnt": 0, "postRepCnt": 0, "postLikCnt": 0, - "spacInclCharCnt": 0, + "spacInclCharCnt": null, "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2016/12/19/08/39/mobile-phone-1917737_150.jpg", + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/10/c9c37e9d5fa6df93437d957f29b4d2aa.jpeg?w=360&h=270&q=65", "serId": 1, "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, "adoYn": false, - "postPblcDtm": "2023-06-22 13:27:52", + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, - "nowPostStusCd": "ST00410", - "nowPostStudChgDtm": "2023-06-22 13:27:52", - "befPostId": null, - "nextPostId": null, + "nowPostStusCd": "ST00420", + "nowPostStudChgDtm": "2023-07-04 00:18:15", + "befPostId": 19, + "nextPostId": 21, "serTtl": "잡설", - "nowPostStusCdNm": "임시저장", - "styleText": null + "nowPostStusCdNm": "최초발행", + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { - "postId": 1, - "postTtl": "첫 번째 포스트", - "postSbTtl": "사랑은 ?다.", + "postId": 19, + "postTtl": "소프트웨어 테스팅 방법론", + "postSbTtl": "소프트웨어 테스팅 방법론", "postInqrCnt": 0, "postRepCnt": 0, "postLikCnt": 0, - "spacInclCharCnt": 0, - "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2018/11/29/21/51/social-media-3846597_150.png", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-06-16 14:00:30", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00410", - "nowPostStudChgDtm": "2023-06-16 14:00:30", - "befPostId": null, - "nextPostId": null, - "serTtl": "잡설", - "nowPostStusCdNm": "임시저장", - "styleText": null - }, - { - "postId": 14, - "postTtl": "데이터베이스 최적화 방법", - "postSbTtl": "정호 포스트 테스트 3", - "postInqrCnt": null, - "postRepCnt": null, - "postLikCnt": null, "spacInclCharCnt": null, "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2016/12/09/11/33/smartphone-1894723_150.jpg", + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/05/10d1430a8b5cc9b04304814d33b9e851.jpeg?w=360&h=270&q=65", "serId": 1, "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": null, + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": null, - "befPostId": 13, - "nextPostId": 15, + "nowPostStudChgDtm": "2023-07-04 00:18:15", + "befPostId": 17, + "nextPostId": 20, "serTtl": "잡설", "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null }, { - "postId": 15, - "postTtl": "컴퓨터 네트워킹 기초", - "postSbTtl": "정호 포스트 테스트4", - "postInqrCnt": null, - "postRepCnt": null, - "postLikCnt": null, + "postId": 17, + "postTtl": "사이버 보안 최신 동향", + "postSbTtl": "사이버 보안의 최신 동향", + "postInqrCnt": 0, + "postRepCnt": 0, + "postLikCnt": 0, "spacInclCharCnt": null, "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2017/01/18/08/25/social-media-1989152_150.jpg", + "postThumnPath": null, "serId": 1, "opubPlanId": null, "pchrgBlkPurcPnt": 1000, "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": null, + "adoYn": false, + "postPblcDtm": "2023-07-04 00:18:15", "chnlId": 7, "basicFontCd": "FT00110", "basicParagAlgnCd": "AL00110", "itdYn": true, "paragGapMargYn": true, "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": null, - "befPostId": 14, - "nextPostId": 16, + "nowPostStudChgDtm": "2023-07-04 00:18:15", + "befPostId": 16, + "nextPostId": 19, "serTtl": "잡설", "nowPostStusCdNm": "최초발행", - "styleText": null + "styleText": null, + "userNic": "genius", + "userImgPath": null } ], "channelUser": { @@ -366,7 +390,7 @@ "chnlImgPath": "https://blog.kakaocdn.net/dn/d8FDB8/btquA1JkNEk/3rimTHGPZXiuhMe93Uw751/img.png", "suberCnt": 0, "chnlPostCnt": 23, - "chnlLikCnt": null, + "chnlLikCnt": 0, "chnlOpenDtm": "2023-06-17 16:03:15", "crtId": null, "hmpgUrl": null, From 2082c157bf94a041c090d7298fa167f802673b3a Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 11:24:39 +0900 Subject: [PATCH 04/90] Added channel about router MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 라우터에 채널 어바웃을 추가했습니다. --- src/pages/general/channel/ChannelAbout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/general/channel/ChannelAbout.js b/src/pages/general/channel/ChannelAbout.js index 19828e3..c81ca16 100644 --- a/src/pages/general/channel/ChannelAbout.js +++ b/src/pages/general/channel/ChannelAbout.js @@ -1,5 +1,5 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; -import channelAboutData from '../../../tempData/channel/channelAbout.json'; +import ㅎ from '../../../tempData/channel/channelAbout.json'; export default function ChannelAbout() { return ( From c7c8b5bc35b9f9aa9dd0ae9ed58bf4661b3890fb Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 13:16:19 +0900 Subject: [PATCH 05/90] Add: add react pagination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 리액트 페이지네이션을 설치 --- src/pages/general/channel/ChannelAbout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/general/channel/ChannelAbout.js b/src/pages/general/channel/ChannelAbout.js index c81ca16..19828e3 100644 --- a/src/pages/general/channel/ChannelAbout.js +++ b/src/pages/general/channel/ChannelAbout.js @@ -1,5 +1,5 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; -import ㅎ from '../../../tempData/channel/channelAbout.json'; +import channelAboutData from '../../../tempData/channel/channelAbout.json'; export default function ChannelAbout() { return ( From 1c196b3f4dacad2b889ca0efdf37b45d5942ac74 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 22:06:31 +0900 Subject: [PATCH 06/90] =?UTF-8?q?update:=20api=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 모든 api를 연동해 실제 데이터로 바꿨다. --- package.json | 1 - src/appRoute.js | 16 +-- src/hooks/useApi.js | 1 + src/pages/general/channel/ChannelAbout.js | 118 +++++++++++++++++----- src/pages/general/channel/ChannelHome.js | 26 ++++- src/pages/general/channel/ChannelPosts.js | 50 +++++++-- yarn.lock | 9 +- 7 files changed, 171 insertions(+), 50 deletions(-) diff --git a/package.json b/package.json index 60e9973..8b82a09 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "react-dom": "^18.2.0", "react-icons": "^4.8.0", "react-modal": "^3.16.1", - "react-paginate": "^8.2.0", "react-router-dom": "^6.10.0", "react-scripts": "5.0.1", "react-tippy": "^1.4.0", diff --git a/src/appRoute.js b/src/appRoute.js index 0947b4a..35082fc 100644 --- a/src/appRoute.js +++ b/src/appRoute.js @@ -59,14 +59,14 @@ const urls = { //channel channelCreate: '/channel/create', // TODO: chnlUri 변수 수정 - // channel: '/channel/:chnlUri', - // channelPosts: '/channel/:chnlUri/posts', - // channelSeries: '/channel/:chnlUri/series', - // channelAbout: '/channel/:chnlUri/about', - channel: '/channel/buksan', - channelPosts: '/channel/buksan/posts', - channelSeries: '/channel/buksan/series', - channelAbout: '/channel/buksan/about', + channel: '/channel/:chnlUri', + channelPosts: '/channel/:chnlUri/posts', + channelSeries: '/channel/:chnlUri/series', + channelAbout: '/channel/:chnlUri/about', + // channel: '/channel/buksan', + // channelPosts: '/channel/buksan/posts', + // channelSeries: '/channel/buksan/series', + // channelAbout: '/channel/buksan/about', search: '/search', searchSeries: '/search/series', diff --git a/src/hooks/useApi.js b/src/hooks/useApi.js index 76fae39..df09f9d 100644 --- a/src/hooks/useApi.js +++ b/src/hooks/useApi.js @@ -7,6 +7,7 @@ export function useApiGet(url) { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); + console.dir(url); useEffect(() => { (async () => { diff --git a/src/pages/general/channel/ChannelAbout.js b/src/pages/general/channel/ChannelAbout.js index 19828e3..0046e6d 100644 --- a/src/pages/general/channel/ChannelAbout.js +++ b/src/pages/general/channel/ChannelAbout.js @@ -1,31 +1,103 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; -import channelAboutData from '../../../tempData/channel/channelAbout.json'; +import styled from 'styled-components'; +import { useParams } from 'react-router-dom'; +import { useApiGet } from '../../../hooks/useApi'; + +const ContentContainer = styled.div` + padding: 20px 0; +`; + +const AboutSectionContainer = styled.div` + padding: 32px 0; +`; + +const ChannelSection = styled(AboutSectionContainer)` + display: flex; + border-bottom: 1px solid rgba(0,0,0,.05); +`; + +const UserSection = styled(AboutSectionContainer)` + display: flex; + +`; + +const RecThumnail = styled.div` + width: 60px; + height: 60px; + background-color: #ffffff; + border-radius: 8px; + margin-right: 10px; + display: inline-block; + background-image: url(${props => props.imageUrl}); + background-size: cover; + border: 1px solid rgba(0,0,0,.05); + margin-right: 28px; +`; + +const SectionTitle = styled.h3` + font-size: 20px; +` + +const ChannelBottomContainer = styled.div` + display: flex; + list-style: none; +` + +const RoundThumnail = styled.div` + width: 60px; + height: 60px; + background-color: #ffffff; + border-radius: 60px; + margin-right: 10px; + display: inline-block; + background-image: url(${props => props.imageUrl}); + background-size: cover; + border: 1px solid rgba(0,0,0,.05); + margin-right: 28px; +`; export default function ChannelAbout() { + const { chnlUri } = useParams(); + const { data, isLoading, error } = useApiGet( + `/channel/${encodeURIComponent(chnlUri)}/about`, + [chnlUri] + ); + + if (isLoading) return; + if (error) return {`[${error.code}] ${error.message}`}; + + if (!data) { + return null; + } return ( -
채널 소개
-
-

채널 영역

-
-

{channelAboutData.data.channel.chnlTtl}

-
{channelAboutData.data.channel.chnlIntro}
-
{channelAboutData.data.channel.crtNic}
- -
{channelAboutData.data.channel.chnlOpenDtm}
- - -
- -
-

유저 영역

-
-

{channelAboutData.data.user.nic}

-

{channelAboutData.data.user.eid}

-

{channelAboutData.data.user.userIntro}

- -
+ + + +
+ {data.data.channel.chnlTtl} +

{data.data.channel.chnlIntro}

+ + +
  • (아이콘) 구독자 {data.data.channel.suberCnt}명
  • +
  • (아이콘) 포스트 {data.data.channel.chnlPostCnt}개
  • +
  • (아이콘) 좋아요 {data.data.channel.chnlLikCnt}개
  • +
  • (아이콘) 개설일 {data.data.channel.chnlOpenDtm}
  • +
    +
    + +
    + + + {/* TODO: user.userImgPath 값넣어서 위에줄 주석풀고 아랫줄 삭제 */} + +
    + {data.data.user.nic} +

    {data.data.user.userIntro}

    +
    +
    +
    ); -} +} \ No newline at end of file diff --git a/src/pages/general/channel/ChannelHome.js b/src/pages/general/channel/ChannelHome.js index c05bb8e..6926720 100644 --- a/src/pages/general/channel/ChannelHome.js +++ b/src/pages/general/channel/ChannelHome.js @@ -1,6 +1,9 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; import channelData from '../../../tempData/channel/channel.json'; import styled from 'styled-components'; +import useUserStore from '../../../stores/useUserStore'; +import { useParams } from 'react-router-dom'; +import { useApiGet } from '../../../hooks/useApi'; const SectionHeader = styled.div` padding: 0 0 10px; @@ -100,6 +103,21 @@ const WebtoonViewCountIcon = styled.img` ` export default function ChannelHome() { + const { user } = useUserStore(); + const { chnlUri } = useParams(); + const { data, isLoading, error } = useApiGet( + `/channel/${encodeURIComponent(chnlUri)}`, + [chnlUri] + ); + + if (isLoading) return; + if (error) return {`[${error.code}] ${error.message}`}; + + if (!data) { + return null; + } + + return ( @@ -107,7 +125,7 @@ export default function ChannelHome() { {">"} {/* - {channelData.data.channelPosts.map((post, index) => ( + {data.data.channelPosts.map((post, index) => (

    {post.postTtl}

    @@ -118,7 +136,7 @@ export default function ChannelHome() { ))} */} - {channelData.data.channelPosts.map((post, index) => ( + {data.data.channelPosts.map((post, index) => (
    @@ -126,7 +144,7 @@ export default function ChannelHome() { {post.postTtl}
    - {channelData.data.channelUser.nic} + {data.data.channelUser.nic} {post.postInqrCnt} @@ -153,7 +171,7 @@ export default function ChannelHome() { 시리즈 준비중.. {/*
      - {channelData.data.channelSerieses.map((series, index) => ( + {data.data.channelSerieses.map((series, index) => (
    • {series.serTtl}

      diff --git a/src/pages/general/channel/ChannelPosts.js b/src/pages/general/channel/ChannelPosts.js index 6ab98c5..040c9b3 100644 --- a/src/pages/general/channel/ChannelPosts.js +++ b/src/pages/general/channel/ChannelPosts.js @@ -5,6 +5,9 @@ import React, { useState, useEffect } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; import {faChevronRight} from '@fortawesome/free-solid-svg-icons'; +import useUserStore from '../../../stores/useUserStore'; +import { useLocation, useParams } from 'react-router-dom'; +import { useApiGet } from '../../../hooks/useApi'; const SectionHeader = styled.div` padding: 0 0 10px; @@ -135,12 +138,32 @@ const PaginationButton = styled.div` `; export default function ChannelPosts() { - const [currentPage, setCurrentPage] = useState(0); + const { user } = useUserStore(); + const { chnlUri } = useParams(); + const location = useLocation(); + const queryParams = new URLSearchParams(location.search); + const pageString = queryParams.get('page'); // This will be a string + const page = pageString !== null ? Number(pageString) - 1 : 0; + const [currentPage, setCurrentPage] = useState(page); + const [url, setUrl] = useState(`/channel/${encodeURIComponent(chnlUri)}/posts?page=${currentPage+1}`, + ); const [pageCount, setPageCount] = useState(0); + const [totalCount, setTotalCount] = useState(null); + const { data, isLoading, error } = useApiGet( + url, + [url] + ); - const totalCount = channelPostsData.data.channel.chnlPostCnt; const size = 12; + console.dir(data); + useEffect(() => { + // ...if data has loaded, then compute the totalCount + if (data) { + setTotalCount(data.data.channel.chnlPostCnt || 0); + } + }, [data]); + useEffect(() => { const calculatePageCount = () => { setPageCount(Math.ceil(totalCount / size)); @@ -149,6 +172,11 @@ export default function ChannelPosts() { calculatePageCount(); }, [totalCount, size]); + useEffect(() => { + setUrl(`/channel/${encodeURIComponent(chnlUri)}/posts?page=${currentPage+1}`); + }, [currentPage, chnlUri]); + + const handlePrevPage = () => { if (currentPage > 0) { setCurrentPage(currentPage - 1); @@ -161,14 +189,24 @@ export default function ChannelPosts() { } }; + if (isLoading) return
      Loading...
      ; + if (error) return {`[${error.code}] ${error.message}`}; + + if (!data) { + return null; + } + + + + return ( - {channelPostsData.data.channel.chnlPostCnt}개의 포스트 + {data.data.channel.chnlPostCnt}개의 포스트 최신순 | 인기순 - {channelPostsData.data.channelPosts.map((post, index) => ( + {data.data.channelPosts.map((post, index) => (
      @@ -176,7 +214,7 @@ export default function ChannelPosts() { {post.postTtl}
      - {channelPostsData.data.channelUser.nic} + {data.data.channelUser.nic} {post.postInqrCnt} @@ -197,7 +235,7 @@ export default function ChannelPosts() {
      // 임시로 currentPage를 보여주기 위한 코드 {currentPage} 현재페이지 {/* - {channelPostsData.data.channelPosts.map((post, index) => ( + {data.data.channelPosts.map((post, index) => (

      {post.postTtl}

      diff --git a/yarn.lock b/yarn.lock index 8a8ea28..180bd82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7890,7 +7890,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8078,13 +8078,6 @@ react-modal@^3.16.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" -react-paginate@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-8.2.0.tgz#947c3dcb444a6c16c1bcf8361871aa135baa3dcd" - integrity sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw== - dependencies: - prop-types "^15" - react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" From e2e544d3ea490a21ec9edfe76b85468677377575 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 5 Jul 2023 00:55:33 +0900 Subject: [PATCH 07/90] =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/general/channel/ChannelAbout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/general/channel/ChannelAbout.js b/src/pages/general/channel/ChannelAbout.js index 0046e6d..042ee32 100644 --- a/src/pages/general/channel/ChannelAbout.js +++ b/src/pages/general/channel/ChannelAbout.js @@ -100,4 +100,4 @@ export default function ChannelAbout() { ); -} \ No newline at end of file +} From 4e641ad934c1201710af8719818f56347169a2da Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 22:06:31 +0900 Subject: [PATCH 08/90] =?UTF-8?q?update:=20api=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 모든 api를 연동해 실제 데이터로 바꿨다. --- src/pages/general/channel/ChannelAbout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/general/channel/ChannelAbout.js b/src/pages/general/channel/ChannelAbout.js index 042ee32..0046e6d 100644 --- a/src/pages/general/channel/ChannelAbout.js +++ b/src/pages/general/channel/ChannelAbout.js @@ -100,4 +100,4 @@ export default function ChannelAbout() { ); -} +} \ No newline at end of file From f91ef79e213e1a382b5b751d0333a5f38ac4f3ce Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 5 Jul 2023 00:42:58 +0900 Subject: [PATCH 09/90] feat: Add channel home webtoon, webnovel tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 웹툰 탭 작업 2. 웹소설 탭 작업(포스트 리스트 컴포넌트 사용) 3. 채널홈 전체 api 연동 4. 채널 uri 라우터 변수 처리 --- src/appRoute.js | 21 +-- .../templates/general/ChannelTemplate.js | 50 ++++-- src/pages/general/channel/ChannelAbout.js | 2 +- src/pages/general/channel/ChannelHome.js | 74 +++------ src/pages/general/channel/ChannelSeries.js | 9 - src/pages/general/channel/ChannelWebnovel.js | 155 ++++++++++++++++++ .../{ChannelPosts.js => ChannelWebtoon.js} | 48 +----- 7 files changed, 225 insertions(+), 134 deletions(-) delete mode 100644 src/pages/general/channel/ChannelSeries.js create mode 100644 src/pages/general/channel/ChannelWebnovel.js rename src/pages/general/channel/{ChannelPosts.js => ChannelWebtoon.js} (80%) diff --git a/src/appRoute.js b/src/appRoute.js index 35082fc..bfa30ee 100644 --- a/src/appRoute.js +++ b/src/appRoute.js @@ -21,8 +21,8 @@ import ProfileSettings from './pages/general/accSettings/ProfileSettings'; import BlackedChannel from './pages/general/accSettings/BlackedChannel'; import ChannelCreate from './pages/general/ChannelCreate'; import ChannelHome from './pages/general/channel/ChannelHome'; -import ChannelPosts from './pages/general/channel/ChannelPosts'; -import ChannelSeries from './pages/general/channel/ChannelSeries'; +import ChannelWebtoon from './pages/general/channel/ChannelWebtoon'; +import ChannelWebnovel from './pages/general/channel/ChannelWebnovel'; import ChannelAbout from './pages/general/channel/ChannelAbout'; const urls = { @@ -58,15 +58,10 @@ const urls = { //channel channelCreate: '/channel/create', - // TODO: chnlUri 변수 수정 channel: '/channel/:chnlUri', - channelPosts: '/channel/:chnlUri/posts', - channelSeries: '/channel/:chnlUri/series', + channelWebtoon: '/channel/:chnlUri/webtoon', + channelWebnovel: '/channel/:chnlUri/webnovel', channelAbout: '/channel/:chnlUri/about', - // channel: '/channel/buksan', - // channelPosts: '/channel/buksan/posts', - // channelSeries: '/channel/buksan/series', - // channelAbout: '/channel/buksan/about', search: '/search', searchSeries: '/search/series', @@ -121,8 +116,8 @@ export const accSetLinks = [ export const channelLinks = [ { to: urls.channel, children: '홈' }, - { to: urls.channelPosts, children: '포스트' }, - { to: urls.channelSeries, children: '시리즈' }, + { to: urls.channelWebtoon, children: '웹툰' }, + { to: urls.channelWebnovel, children: '웹소설' }, { to: urls.channelAbout, children: '소개' }, ]; @@ -171,7 +166,7 @@ export const pageRoutes = [ //channel { id: 26, path: urls.channelCreate, element: }, { id: 27, path: urls.channel, element: }, - { id: 28, path: urls.channelPosts, element: }, - { id: 29, path: urls.channelSeries, element: }, + { id: 28, path: urls.channelWebtoon, element: }, + { id: 29, path: urls.channelWebnovel, element: }, { id: 30, path: urls.channelAbout, element: }, ]; diff --git a/src/components/templates/general/ChannelTemplate.js b/src/components/templates/general/ChannelTemplate.js index a2a5442..3e446c2 100644 --- a/src/components/templates/general/ChannelTemplate.js +++ b/src/components/templates/general/ChannelTemplate.js @@ -2,8 +2,11 @@ import NavMenu from '../../molecules/general/NavMenu'; import Nav from '../../organisms/general/Nav'; import Header from '../../organisms/general/Header'; import Main from '../../organisms/general/Main'; -import channelData from '../../../tempData/channel/channel.json'; import styled from 'styled-components'; +import { useApiGet } from '../../../hooks/useApi'; +import { useParams } from 'react-router-dom'; +import useUserStore from '../../../stores/useUserStore'; +import { useEffect, useState } from 'react'; const Thumbnail = styled.div` width: 80px; @@ -29,28 +32,41 @@ const ChannelTitle = styled.h2` `; export default function ChannelTemplate(p) { - console.log(channelData); - // TODO: channlUri 변수 처리 + const { chnlUri } = useParams(); + + const [url, setUrl] = useState(`/channel/${encodeURIComponent(chnlUri)}`); + const { data, isLoading, error } = useApiGet( + url, + [url] + ); + useEffect(() => { + setUrl(`/channel/${encodeURIComponent(chnlUri)}`); + }, [chnlUri]); + + const { user } = useUserStore(); + if (isLoading) return; + if (error) return {`[${error.code}] ${error.message}`}; + + if (!data) { + return null; + } + const mainNavLinks = [ { - // to: `/channel/${encodeURIComponent(p.nic)}`, - to: `/channel/buksan`, + to: `/channel/${encodeURIComponent(p.chnlUri)}`, children: '홈', end: true, }, { - // to: `/channel/${encodeURIComponent(p.nic)}/posts`, - to: `/channel/buksan/posts`, - children: '포스트', + to: `/channel/${encodeURIComponent(p.chnlUri)}/webtoon`, + children: '웹툰', }, { - // to: `/channel/${encodeURIComponent(p.nic)}/series`, - to: `/channel/buksan/series`, - children: '시리즈', + to: `/channel/${encodeURIComponent(p.chnlUri)}/webnovel`, + children: '웹소설', }, { - // to: `/channel/${encodeURIComponent(p.nic)}/about`, - to: `/channel/buksan/about`, + to: `/channel/${encodeURIComponent(p.chnlUri)}/about`, children: '소개', }, ]; @@ -60,12 +76,12 @@ export default function ChannelTemplate(p) {
      - + - 구독자 {channelData.data.channel.suberCnt}명 · 포스트{' '} - {channelData.data.channel.chnlPostCnt}개 + 구독자 {data.data.channel.suberCnt}명 · 포스트{' '} + {data.data.channel.chnlPostCnt}개 - {channelData.data.channel.chnlTtl} + {data.data.channel.chnlTtl} {/* // TODO: 구독하기 버튼 처리
      diff --git a/src/pages/general/channel/ChannelAbout.js b/src/pages/general/channel/ChannelAbout.js index 0046e6d..a5ac8c2 100644 --- a/src/pages/general/channel/ChannelAbout.js +++ b/src/pages/general/channel/ChannelAbout.js @@ -70,7 +70,7 @@ export default function ChannelAbout() { return null; } return ( - + diff --git a/src/pages/general/channel/ChannelHome.js b/src/pages/general/channel/ChannelHome.js index 6926720..af88322 100644 --- a/src/pages/general/channel/ChannelHome.js +++ b/src/pages/general/channel/ChannelHome.js @@ -1,9 +1,13 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; -import channelData from '../../../tempData/channel/channel.json'; +import data from '../../../tempData/channel/channel.json'; +// import data from '../../../tempData/channel/channelPosts.json'; import styled from 'styled-components'; import useUserStore from '../../../stores/useUserStore'; import { useParams } from 'react-router-dom'; import { useApiGet } from '../../../hooks/useApi'; +import PostItem from '../../../components/organisms/general/PostItem'; +import NoContent from '../../../components/molecules/error/NoContent'; +import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; const SectionHeader = styled.div` padding: 0 0 10px; @@ -15,31 +19,6 @@ const SectionHeader = styled.div` justify-content: space-between; `; -// const PostListContainer = styled.div` - -// `; - -// const PostListItem = styled.div` -// display: flex; -// margin: 10px; -// background-color: #ffffff; -// padding: 10px; -// justify-content: space-between; -// border-bottom: 1px solid rgba(0,0,0,.05); -// font-weightL 500; -// `; - -// const Thumbnail = styled.div` -// width: 144px; -// height: 108px; -// background-color: #ffffff; -// border-radius: 8px; -// margin-right: 10px; -// display: inline-block; -// background-image: url(${props => props.imageUrl}); -// background-size: cover; -// `; - const WebtoonListContainer = styled.div` display: flex; flex-wrap: wrap; @@ -119,23 +98,12 @@ export default function ChannelHome() { return ( - + - 포스트 + 웹툰 {">"} - {/* - {data.data.channelPosts.map((post, index) => ( - -
      -

      {post.postTtl}

      -

      포스트 내용이 들어갈 예정입니다.포스트 내용이 들어갈 예정입니다.포스트 내용이 들어갈 예정입니다.

      -
      - -
      - ))} -
      */} - + {data.data.channelPosts.map((post, index) => ( @@ -155,7 +123,7 @@ export default function ChannelHome() { {post.postLikCnt} - 21시간 전 + 21시간 전 @@ -169,15 +137,21 @@ export default function ChannelHome() { 시리즈 {">"} - 시리즈 준비중.. - {/*
        - {data.data.channelSerieses.map((series, index) => ( -
      • -

        {series.serTtl}

        - -
      • - ))} -
      */} + + {data.data.channelPosts.length !== 0 ? ( + data.data.channelPosts.map((post) => ) + ) : ( + <> + + 아직 발행한 포스트가 없습니다. + {data.data.channelUser.eid === user.eid && ( + 포스트 발행하기 + )} + + + )} + +
      ); diff --git a/src/pages/general/channel/ChannelSeries.js b/src/pages/general/channel/ChannelSeries.js deleted file mode 100644 index 288ca77..0000000 --- a/src/pages/general/channel/ChannelSeries.js +++ /dev/null @@ -1,9 +0,0 @@ -import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; - -export default function ChannelSeries() { - return ( - -
      시리즈 준비중 입니다...
      -
      - ); -} diff --git a/src/pages/general/channel/ChannelWebnovel.js b/src/pages/general/channel/ChannelWebnovel.js new file mode 100644 index 0000000..b0f9835 --- /dev/null +++ b/src/pages/general/channel/ChannelWebnovel.js @@ -0,0 +1,155 @@ +import styled from 'styled-components'; +import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; +import data from '../../../tempData/channel/channelPosts.json'; +import React, { useState, useEffect } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; +import {faChevronRight} from '@fortawesome/free-solid-svg-icons'; +import useUserStore from '../../../stores/useUserStore'; +import { useLocation, useParams } from 'react-router-dom'; +import { useApiGet } from '../../../hooks/useApi'; +import PostItem from '../../../components/organisms/general/PostItem'; +import NoContent from '../../../components/molecules/error/NoContent'; +import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; + +const SectionHeader = styled.div` + padding: 0 0 10px; + color: #121212; + font-size: 16px; + font-weight: 500; + border-bottom: 1px solid rgba(0,0,0,.05); + display: flex; + justify-content: space-between; +`; + +const SectionHeaderFilter = styled.div` + color: rgba(0,0,0,.47); + font-size: 14px; + font-weight: 500; +`; + +// Pagination style +const PaginationContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + margin-top: 20px; +`; + +const PaginationButton = styled.div` + min-width: 30px; + height: 30px; + padding: 4px; + margin: 0 2px; + font-weight: 500; + font-size: 14px; + //width: 30px; + display: flex; + justify-content: center; + align-items: center; + border: ${({ isSelected }) => (isSelected ? '1px solid #ccc' : '')}; + border-radius: 4px; + cursor: pointer; + background-color: white; +`; + +export default function ChannelWebnovel() { + // TODO: Webnovel 쪽 데이터 api 연동 필요!!! (지금은 webtoon쪽 코드 복사한 데이터임) + const { user } = useUserStore(); + const { chnlUri } = useParams(); + const location = useLocation(); + const queryParams = new URLSearchParams(location.search); + const pageString = queryParams.get('page'); // This will be a string + const page = pageString !== null ? Number(pageString) - 1 : 0; + const [currentPage, setCurrentPage] = useState(page); + const [url, setUrl] = useState(`/channel/${encodeURIComponent(chnlUri)}/posts?page=${currentPage+1}`, + ); + const [pageCount, setPageCount] = useState(0); + const [totalCount, setTotalCount] = useState(null); + const { data, isLoading, error } = useApiGet( + url, + [url] + ); + + const size = 12; + + useEffect(() => { + if (data) { + setTotalCount(data.data.channel.chnlPostCnt || 0); + } + }, [data]); + + useEffect(() => { + const calculatePageCount = () => { + setPageCount(Math.ceil(totalCount / size)); + }; + + calculatePageCount(); + }, [totalCount, size]); + + useEffect(() => { + setUrl(`/channel/${encodeURIComponent(chnlUri)}/posts?page=${currentPage+1}`); + }, [currentPage, chnlUri]); + + + const handlePrevPage = () => { + if (currentPage > 0) { + setCurrentPage(currentPage - 1); + } + }; + + const handleNextPage = () => { + if (currentPage < pageCount - 1) { + setCurrentPage(currentPage + 1); + } + }; + + if (isLoading) return
      Loading...
      ; + if (error) return {`[${error.code}] ${error.message}`}; + + if (!data) { + return null; + } + + + + + return ( + + + {data.data.channel.chnlPostCnt}개의 포스트 + 최신순 | 인기순 + + {data.data.channelPosts.length !== 0 ? ( + data.data.channelPosts.map((post) => ) + ) : ( + <> + + 아직 발행한 포스트가 없습니다. + {data.data.channelUser.eid === user.eid && ( + 포스트 발행하기 + )} + + + )} + + + + + + {Array.from({ length: pageCount }).map((_, index) => ( + setCurrentPage(index)} + > + {index + 1} + + ))} + + + + + + ); +} \ No newline at end of file diff --git a/src/pages/general/channel/ChannelPosts.js b/src/pages/general/channel/ChannelWebtoon.js similarity index 80% rename from src/pages/general/channel/ChannelPosts.js rename to src/pages/general/channel/ChannelWebtoon.js index 040c9b3..eb6ffdf 100644 --- a/src/pages/general/channel/ChannelPosts.js +++ b/src/pages/general/channel/ChannelWebtoon.js @@ -1,6 +1,6 @@ import styled from 'styled-components'; import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; -import channelPostsData from '../../../tempData/channel/channelPosts.json'; +import data from '../../../tempData/channel/channelPosts.json'; import React, { useState, useEffect } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; @@ -26,30 +26,6 @@ const SectionHeaderFilter = styled.div` `; -// const PostListContainer = styled.div` -// `; - -// const PostListItem = styled.div` -// display: flex; -// margin: 10px; -// background-color: #ffffff; -// padding: 10px; -// justify-content: space-between; -// border-bottom: 1px solid rgba(0,0,0,.05); -// font-weightL 500; -// `; - -// const Thumbnail = styled.div` -// width: 144px; -// height: 108px; -// background-color: #ffffff; -// border-radius: 8px; -// margin-right: 10px; -// display: inline-block; -// background-image: url(${props => props.imageUrl}); -// background-size: cover; -// `; - const WebtoonListContainer = styled.div` display: flex; flex-wrap: wrap; @@ -104,7 +80,6 @@ const WebtoonCount = styled.span` margin-left: 8px; color: rgba(0,0,0,.47); font-size: 12px; - `; const WebtoonViewCountIcon = styled.img` @@ -137,7 +112,7 @@ const PaginationButton = styled.div` background-color: white; `; -export default function ChannelPosts() { +export default function ChannelWebtoon() { const { user } = useUserStore(); const { chnlUri } = useParams(); const location = useLocation(); @@ -145,8 +120,7 @@ export default function ChannelPosts() { const pageString = queryParams.get('page'); // This will be a string const page = pageString !== null ? Number(pageString) - 1 : 0; const [currentPage, setCurrentPage] = useState(page); - const [url, setUrl] = useState(`/channel/${encodeURIComponent(chnlUri)}/posts?page=${currentPage+1}`, - ); + const [url, setUrl] = useState(`/channel/${encodeURIComponent(chnlUri)}/posts?page=${currentPage+1}`); const [pageCount, setPageCount] = useState(0); const [totalCount, setTotalCount] = useState(null); const { data, isLoading, error } = useApiGet( @@ -156,9 +130,7 @@ export default function ChannelPosts() { const size = 12; - console.dir(data); useEffect(() => { - // ...if data has loaded, then compute the totalCount if (data) { setTotalCount(data.data.channel.chnlPostCnt || 0); } @@ -200,7 +172,7 @@ export default function ChannelPosts() { return ( - + {data.data.channel.chnlPostCnt}개의 포스트 최신순 | 인기순 @@ -233,18 +205,6 @@ export default function ChannelPosts() { ))} - // 임시로 currentPage를 보여주기 위한 코드 {currentPage} 현재페이지 - {/* - {data.data.channelPosts.map((post, index) => ( - -
      -

      {post.postTtl}

      -

      포스트 내용이 들어갈 예정입니다.포스트 내용이 들어갈 예정입니다.포스트 내용이 들어갈 예정입니다.

      -
      - -
      - ))} -
      */} From 33ffacb73a8e3c852e80dec1ba4ac9a0e1dd6f94 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 5 Jul 2023 00:51:44 +0900 Subject: [PATCH 10/90] delete: deleted test data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 안 쓰는 테스트 데이터 지움 --- src/appRoute.js | 1 - src/pages/general/channel/ChannelHome.js | 2 - src/pages/general/channel/ChannelWebnovel.js | 1 - src/pages/general/channel/ChannelWebtoon.js | 1 - src/tempData/channel/channel.json | 268 ------------ src/tempData/channel/channelAbout.json | 33 -- src/tempData/channel/channelPosts.json | 407 ------------------- 7 files changed, 713 deletions(-) delete mode 100644 src/tempData/channel/channel.json delete mode 100644 src/tempData/channel/channelAbout.json delete mode 100644 src/tempData/channel/channelPosts.json diff --git a/src/appRoute.js b/src/appRoute.js index bfa30ee..4333438 100644 --- a/src/appRoute.js +++ b/src/appRoute.js @@ -86,7 +86,6 @@ export const homeLinks = [ { to: urls.home, children: '추천' }, { to: urls.webtoon, children: '웹툰' }, { to: urls.webnovel, children: '웹소설' }, - { to: urls.home, children: '잘생긴 정호 메뉴'}, ]; export const subsLinks = [ diff --git a/src/pages/general/channel/ChannelHome.js b/src/pages/general/channel/ChannelHome.js index af88322..3176d1a 100644 --- a/src/pages/general/channel/ChannelHome.js +++ b/src/pages/general/channel/ChannelHome.js @@ -1,6 +1,4 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; -import data from '../../../tempData/channel/channel.json'; -// import data from '../../../tempData/channel/channelPosts.json'; import styled from 'styled-components'; import useUserStore from '../../../stores/useUserStore'; import { useParams } from 'react-router-dom'; diff --git a/src/pages/general/channel/ChannelWebnovel.js b/src/pages/general/channel/ChannelWebnovel.js index b0f9835..3333a39 100644 --- a/src/pages/general/channel/ChannelWebnovel.js +++ b/src/pages/general/channel/ChannelWebnovel.js @@ -1,6 +1,5 @@ import styled from 'styled-components'; import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; -import data from '../../../tempData/channel/channelPosts.json'; import React, { useState, useEffect } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/pages/general/channel/ChannelWebtoon.js b/src/pages/general/channel/ChannelWebtoon.js index eb6ffdf..b9f8bb1 100644 --- a/src/pages/general/channel/ChannelWebtoon.js +++ b/src/pages/general/channel/ChannelWebtoon.js @@ -1,6 +1,5 @@ import styled from 'styled-components'; import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; -import data from '../../../tempData/channel/channelPosts.json'; import React, { useState, useEffect } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/tempData/channel/channel.json b/src/tempData/channel/channel.json deleted file mode 100644 index 898be6b..0000000 --- a/src/tempData/channel/channel.json +++ /dev/null @@ -1,268 +0,0 @@ -{ - "data": { - "channel": { - "chnlId": 7, - "chnlTtl": "산왕잡기", - "chnlIntro": "포기 ㄴㄴ해.", - "chnlUri": "buksan", - "chnlSbTtl": null, - "chnlImgPath": "https://blog.kakaocdn.net/dn/d8FDB8/btquA1JkNEk/3rimTHGPZXiuhMe93Uw751/img.png", - "suberCnt": 0, - "chnlPostCnt": 23, - "chnlLikCnt": 0, - "chnlOpenDtm": "2023-06-17 16:03:15", - "crtId": null, - "hmpgUrl": null, - "instaUrl": null, - "githUrl": null, - "ytbUrl": null, - "twchUrl": null, - "chnlStusCd": null, - "chnlStusChgrId": null, - "chnlStusChgDtm": "2023-06-17 16:03:15", - "crtNic": "genius" - }, - "channelUser": { - "eid": "iamloved5959@gmail.com", - "nic": "genius", - "userIntro": "genius한 아이디입니다.", - "userImgPath": null, - "msgAlowYn": true - }, - "channelPosts": [ - { - "postId": 20, - "postTtl": "웹 디자인과 사용자 경험", - "postSbTtl": "웹 디자인과 사용자 경험", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": null, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/10/c9c37e9d5fa6df93437d957f29b4d2aa.jpeg?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 19, - "nextPostId": 21, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 22, - "postTtl": "데이터 시각화 방법", - "postSbTtl": "데이터 시각화 방법", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": 0, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/07/01/08/33/f7594512ebfde6c80d3aa44d12500181.jpeg?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 21, - "nextPostId": 23, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 21, - "postTtl": "모바일 앱 개발 팁", - "postSbTtl": "모바일 앱 개발 팁", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": null, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/07/8b9125f3a5ba52114082d71597c66f2c.jpeg?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 20, - "nextPostId": 22, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 23, - "postTtl": "블록체인 기술 개요", - "postSbTtl": "블록체인 기술 개요", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": 0, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/06/22/17/48/61402e2724483712413946312.png?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 22, - "nextPostId": null, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 19, - "postTtl": "소프트웨어 테스팅 방법론", - "postSbTtl": "소프트웨어 테스팅 방법론", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": null, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/05/10d1430a8b5cc9b04304814d33b9e851.jpeg?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 17, - "nextPostId": 20, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 17, - "postTtl": "사이버 보안 최신 동향", - "postSbTtl": "사이버 보안의 최신 동향", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": null, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/06/17/13/25/6f191cc1c17147691b8e8aac6adae34f.jpeg?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 16, - "nextPostId": 19, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - } - ], - "channelSerieses": [ - { - "serId": 1, - "serThumnPath": "https://cdn.pixabay.com/photo/2014/08/08/20/55/worried-girl-413690_150.jpg", - "serTtl": "잡설", - "serDesc": "잡다한 이야기를 적습니다.", - "serOpenDtm": "2023-06-08 14:29:22", - "serInqrCnt": 0, - "serLikCnt": 0, - "serRepCnt": 0, - "serPostCnt": 1, - "chnlDsgntSerOdr": 1, - "serStusChgDtm": "2023-06-08 14:29:22", - "serStusCdNm": "단편", - "userNic": "genius" - }, - { - "serId": 5, - "serThumnPath": "https://cdn.pixabay.com/photo/2017/05/07/19/46/desperate-2293377_150.jpg", - "serTtl": "정호 시리즈 테스트5 edit", - "serDesc": "시리즈 edit", - "serOpenDtm": "2023-06-29 21:48:01", - "serInqrCnt": 0, - "serLikCnt": 0, - "serRepCnt": 0, - "serPostCnt": 0, - "chnlDsgntSerOdr": 1, - "serStusChgDtm": "2023-06-29 21:48:01", - "serStusCdNm": "단편", - "userNic": "genius" - }, - { - "serId": 6, - "serThumnPath": "https://cdn.pixabay.com/photo/2014/09/07/22/03/bubble-gum-438404_150.jpg", - "serTtl": "정호 시리즈 테스트1", - "serDesc": "정호 시리즈 테스트1", - "serOpenDtm": "2023-06-30 17:07:02", - "serInqrCnt": 0, - "serLikCnt": 0, - "serRepCnt": 0, - "serPostCnt": 0, - "chnlDsgntSerOdr": 1, - "serStusChgDtm": "2023-06-30 17:07:02", - "serStusCdNm": "단편", - "userNic": "genius" - } - ] - } -} \ No newline at end of file diff --git a/src/tempData/channel/channelAbout.json b/src/tempData/channel/channelAbout.json deleted file mode 100644 index 57eea67..0000000 --- a/src/tempData/channel/channelAbout.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "data": { - "channel": { - "chnlId": 7, - "chnlTtl": "산왕잡기", - "chnlIntro": "포기 ㄴㄴ해.", - "chnlUri": "buksan", - "chnlSbTtl": null, - "chnlImgPath": "https://blog.kakaocdn.net/dn/d8FDB8/btquA1JkNEk/3rimTHGPZXiuhMe93Uw751/img.png", - "suberCnt": null, - "chnlPostCnt": null, - "chnlLikCnt": null, - "chnlOpenDtm": "2023-06-17 16:03:15", - "crtId": null, - "hmpgUrl": null, - "instaUrl": null, - "githUrl": null, - "ytbUrl": null, - "twchUrl": null, - "chnlStusCd": null, - "chnlStusChgrId": null, - "chnlStusChgDtm": "2023-06-17 16:03:15", - "crtNic": "genius" - }, - "user": { - "eid": "iamloved5959@gmail.com", - "nic": "genius", - "userIntro": "genius한 아이디입니다.", - "userImgPath": null, - "msgAlowYn": true - } - } -} \ No newline at end of file diff --git a/src/tempData/channel/channelPosts.json b/src/tempData/channel/channelPosts.json deleted file mode 100644 index 47ef777..0000000 --- a/src/tempData/channel/channelPosts.json +++ /dev/null @@ -1,407 +0,0 @@ -{ - "data": { - "channelPosts": [ - { - "postId": 16, - "postTtl": "클라우드 컴퓨팅의 이점", - "postSbTtl": "클라우드 컴퓨팅의 이점", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": null, - "postPchrgYn": true, - "postThumnPath": "https://cdn.pixabay.com/photo/2014/08/24/19/01/apps-426559_150.jpg", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 15, - "nextPostId": 17, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 28, - "postTtl": "자바의 정석 정리3", - "postSbTtl": "자바의 정석 개념 정리3", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": 0, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/06/02/16/17/5d54c9cf7f49003d5909145452d30af1.jpeg?w=360&h=270&q=65", - "serId": null, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 26, - "nextPostId": null, - "serTtl": null, - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 27, - "postTtl": "자바의 정석 정리2", - "postSbTtl": "자바의 정석 개념 정리2", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": 0, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/00/1a6473c680c055e69d6f605cbb7145dc.jpeg?w=360&h=270&q=65", - "serId": 5, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": null, - "nextPostId": null, - "serTtl": "정호 시리즈 테스트5 edit", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 26, - "postTtl": "자바의 정석 정리1", - "postSbTtl": "자바의 정석 개념 정리1", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": 0, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/06/09/20/09/6bf2cac77ffd3324b1f47820e36da56d.jpeg?w=360&h=270&q=65", - "serId": null, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 25, - "nextPostId": 28, - "serTtl": null, - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 25, - "postTtl": "컴퓨터 그래픽스 개발 기법", - "postSbTtl": "컴퓨터 그래픽스 개발 기법", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": 0, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/06/09/20/12/caef117602943a9ae54ea91f8f438a4e.jpeg?w=360&h=270&q=65", - "serId": null, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 24, - "nextPostId": 26, - "serTtl": null, - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 24, - "postTtl": "빅데이터 처리 기술", - "postSbTtl": "빅데이터 처리 기술", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": 0, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/02/5b25fae0a43d80d12b4cd3990636804c.jpeg?w=360&h=270&q=65", - "serId": null, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 12, - "nextPostId": 25, - "serTtl": null, - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 23, - "postTtl": "블록체인 기술 개요", - "postSbTtl": "블록체인 기술 개요", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": 0, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/06/22/17/48/61402e2724483712413946312.png?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 22, - "nextPostId": null, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 22, - "postTtl": "데이터 시각화 방법", - "postSbTtl": "데이터 시각화 방법", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": 0, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/07/01/08/33/f7594512ebfde6c80d3aa44d12500181.jpeg?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 21, - "nextPostId": 23, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 21, - "postTtl": "모바일 앱 개발 팁", - "postSbTtl": "모바일 앱 개발 팁", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": null, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/07/8b9125f3a5ba52114082d71597c66f2c.jpeg?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 20, - "nextPostId": 22, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 20, - "postTtl": "웹 디자인과 사용자 경험", - "postSbTtl": "웹 디자인과 사용자 경험", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": null, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/10/c9c37e9d5fa6df93437d957f29b4d2aa.jpeg?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 19, - "nextPostId": 21, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 19, - "postTtl": "소프트웨어 테스팅 방법론", - "postSbTtl": "소프트웨어 테스팅 방법론", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": null, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/05/10d1430a8b5cc9b04304814d33b9e851.jpeg?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 17, - "nextPostId": 20, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - }, - { - "postId": 17, - "postTtl": "사이버 보안 최신 동향", - "postSbTtl": "사이버 보안의 최신 동향", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": null, - "postPchrgYn": true, - "postThumnPath": null, - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": false, - "postPblcDtm": "2023-07-04 00:18:15", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-04 00:18:15", - "befPostId": 16, - "nextPostId": 19, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "userNic": "genius", - "userImgPath": null - } - ], - "channelUser": { - "eid": "iamloved5959@gmail.com", - "nic": "genius", - "userIntro": "genius한 아이디입니다.", - "userImgPath": null, - "msgAlowYn": true - }, - "channel": { - "chnlId": 7, - "chnlTtl": "산왕잡기", - "chnlIntro": "포기 ㄴㄴ해.", - "chnlUri": "buksan", - "chnlSbTtl": null, - "chnlImgPath": "https://blog.kakaocdn.net/dn/d8FDB8/btquA1JkNEk/3rimTHGPZXiuhMe93Uw751/img.png", - "suberCnt": 0, - "chnlPostCnt": 23, - "chnlLikCnt": 0, - "chnlOpenDtm": "2023-06-17 16:03:15", - "crtId": null, - "hmpgUrl": null, - "instaUrl": null, - "githUrl": null, - "ytbUrl": null, - "twchUrl": null, - "chnlStusCd": null, - "chnlStusChgrId": null, - "chnlStusChgDtm": "2023-06-17 16:03:15", - "crtNic": "genius" - } - } -} \ No newline at end of file From e0fe69a899214ff088f6df4117532f7948841869 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 13:16:19 +0900 Subject: [PATCH 11/90] Add: add react pagination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 리액트 페이지네이션을 설치 --- package.json | 1 + yarn.lock | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8b82a09..60e9973 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.8.0", "react-modal": "^3.16.1", + "react-paginate": "^8.2.0", "react-router-dom": "^6.10.0", "react-scripts": "5.0.1", "react-tippy": "^1.4.0", diff --git a/yarn.lock b/yarn.lock index 180bd82..8a8ea28 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7890,7 +7890,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8078,6 +8078,13 @@ react-modal@^3.16.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" +react-paginate@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-8.2.0.tgz#947c3dcb444a6c16c1bcf8361871aa135baa3dcd" + integrity sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw== + dependencies: + prop-types "^15" + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" From 79827007ca574826a01d1941b31c3477d49aadb2 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 22:06:31 +0900 Subject: [PATCH 12/90] =?UTF-8?q?update:=20api=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 모든 api를 연동해 실제 데이터로 바꿨다. --- package.json | 1 - yarn.lock | 9 +-------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/package.json b/package.json index 60e9973..8b82a09 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "react-dom": "^18.2.0", "react-icons": "^4.8.0", "react-modal": "^3.16.1", - "react-paginate": "^8.2.0", "react-router-dom": "^6.10.0", "react-scripts": "5.0.1", "react-tippy": "^1.4.0", diff --git a/yarn.lock b/yarn.lock index 8a8ea28..180bd82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7890,7 +7890,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8078,13 +8078,6 @@ react-modal@^3.16.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" -react-paginate@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-8.2.0.tgz#947c3dcb444a6c16c1bcf8361871aa135baa3dcd" - integrity sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw== - dependencies: - prop-types "^15" - react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" From 5ee99196c3da59c6396e9b2751e611b3a9efa775 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 5 Jul 2023 11:27:11 +0900 Subject: [PATCH 13/90] update: change channelPosts to webtoons, webnovels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. channelPosts를 webtoons와 webnovels로 나눔. 2. 그에 따른 전체 코드 수정 --- src/pages/general/channel/ChannelHome.js | 8 ++++---- src/pages/general/channel/ChannelWebnovel.js | 14 +++++++------- src/pages/general/channel/ChannelWebtoon.js | 12 +++++++----- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/pages/general/channel/ChannelHome.js b/src/pages/general/channel/ChannelHome.js index 3176d1a..df6643d 100644 --- a/src/pages/general/channel/ChannelHome.js +++ b/src/pages/general/channel/ChannelHome.js @@ -102,7 +102,7 @@ export default function ChannelHome() { {">"}
      - {data.data.channelPosts.map((post, index) => ( + {data.data.webtoons.map((post, index) => (
      @@ -132,12 +132,12 @@ export default function ChannelHome() { - 시리즈 + 웹소설 {">"} - {data.data.channelPosts.length !== 0 ? ( - data.data.channelPosts.map((post) => ) + {data.data.webnovels.length !== 0 ? ( + data.data.webnovels.map((post) => ) ) : ( <> diff --git a/src/pages/general/channel/ChannelWebnovel.js b/src/pages/general/channel/ChannelWebnovel.js index 3333a39..50c44fb 100644 --- a/src/pages/general/channel/ChannelWebnovel.js +++ b/src/pages/general/channel/ChannelWebnovel.js @@ -53,7 +53,6 @@ const PaginationButton = styled.div` `; export default function ChannelWebnovel() { - // TODO: Webnovel 쪽 데이터 api 연동 필요!!! (지금은 webtoon쪽 코드 복사한 데이터임) const { user } = useUserStore(); const { chnlUri } = useParams(); const location = useLocation(); @@ -61,7 +60,8 @@ export default function ChannelWebnovel() { const pageString = queryParams.get('page'); // This will be a string const page = pageString !== null ? Number(pageString) - 1 : 0; const [currentPage, setCurrentPage] = useState(page); - const [url, setUrl] = useState(`/channel/${encodeURIComponent(chnlUri)}/posts?page=${currentPage+1}`, + const postType = "webnovel"; + const [url, setUrl] = useState(`/channel/${encodeURIComponent(chnlUri)}/posts/${postType}?page=${currentPage+1}` ); const [pageCount, setPageCount] = useState(0); const [totalCount, setTotalCount] = useState(null); @@ -74,7 +74,7 @@ export default function ChannelWebnovel() { useEffect(() => { if (data) { - setTotalCount(data.data.channel.chnlPostCnt || 0); + setTotalCount(data.data.channel.chnlWebnovelCnt || 0); } }, [data]); @@ -87,7 +87,7 @@ export default function ChannelWebnovel() { }, [totalCount, size]); useEffect(() => { - setUrl(`/channel/${encodeURIComponent(chnlUri)}/posts?page=${currentPage+1}`); + setUrl(`/channel/${encodeURIComponent(chnlUri)}/posts/${postType}?page=${currentPage+1}`); }, [currentPage, chnlUri]); @@ -116,11 +116,11 @@ export default function ChannelWebnovel() { return ( - {data.data.channel.chnlPostCnt}개의 포스트 + {data.data.channel.chnlWebnovelCnt}개의 포스트 최신순 | 인기순 - {data.data.channelPosts.length !== 0 ? ( - data.data.channelPosts.map((post) => ) + {data.data.webnovels.length !== 0 ? ( + data.data.webnovels.map((post) => ) ) : ( <> diff --git a/src/pages/general/channel/ChannelWebtoon.js b/src/pages/general/channel/ChannelWebtoon.js index b9f8bb1..68b0159 100644 --- a/src/pages/general/channel/ChannelWebtoon.js +++ b/src/pages/general/channel/ChannelWebtoon.js @@ -7,6 +7,7 @@ import {faChevronRight} from '@fortawesome/free-solid-svg-icons'; import useUserStore from '../../../stores/useUserStore'; import { useLocation, useParams } from 'react-router-dom'; import { useApiGet } from '../../../hooks/useApi'; +import { post } from 'axios'; const SectionHeader = styled.div` padding: 0 0 10px; @@ -119,7 +120,8 @@ export default function ChannelWebtoon() { const pageString = queryParams.get('page'); // This will be a string const page = pageString !== null ? Number(pageString) - 1 : 0; const [currentPage, setCurrentPage] = useState(page); - const [url, setUrl] = useState(`/channel/${encodeURIComponent(chnlUri)}/posts?page=${currentPage+1}`); + const postType = "webtoon"; + const [url, setUrl] = useState(`/channel/${encodeURIComponent(chnlUri)}/posts/${postType}?page=${currentPage+1}`); const [pageCount, setPageCount] = useState(0); const [totalCount, setTotalCount] = useState(null); const { data, isLoading, error } = useApiGet( @@ -131,7 +133,7 @@ export default function ChannelWebtoon() { useEffect(() => { if (data) { - setTotalCount(data.data.channel.chnlPostCnt || 0); + setTotalCount(data.data.channel.chnlWebtoonCnt || 0); } }, [data]); @@ -144,7 +146,7 @@ export default function ChannelWebtoon() { }, [totalCount, size]); useEffect(() => { - setUrl(`/channel/${encodeURIComponent(chnlUri)}/posts?page=${currentPage+1}`); + setUrl(`/channel/${encodeURIComponent(chnlUri)}/posts/${postType}?page=${currentPage+1}`); }, [currentPage, chnlUri]); @@ -173,11 +175,11 @@ export default function ChannelWebtoon() { return ( - {data.data.channel.chnlPostCnt}개의 포스트 + {data.data.channel.chnlWebtoonCnt}개의 포스트 최신순 | 인기순 - {data.data.channelPosts.map((post, index) => ( + {data.data.webtoons.map((post, index) => (
      From 2e5ebb117a05d2473f32b4cda6e21161f28950a3 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 5 Jul 2023 11:28:33 +0900 Subject: [PATCH 14/90] style: update style in ChannelAbout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. ChannelAbout 컴포넌트에 아이콘 적용 및 스타일 개선 --- package.json | 1 + src/pages/general/channel/ChannelAbout.js | 50 ++++++++++++++++++++--- yarn.lock | 7 ++++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 8b82a09..681cdb7 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.4.0", + "@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.2.0", "@testing-library/jest-dom": "^5.14.1", diff --git a/src/pages/general/channel/ChannelAbout.js b/src/pages/general/channel/ChannelAbout.js index a5ac8c2..446ae0b 100644 --- a/src/pages/general/channel/ChannelAbout.js +++ b/src/pages/general/channel/ChannelAbout.js @@ -1,7 +1,13 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; import styled from 'styled-components'; -import { useParams } from 'react-router-dom'; +import { Link, useParams } from 'react-router-dom'; import { useApiGet } from '../../../hooks/useApi'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faClone } from '@fortawesome/free-regular-svg-icons' +import MetaDataSC from '../../../components/atoms/User/MetaDataSC'; +import { AiOutlineEye, AiOutlineHeart } from 'react-icons/ai'; +import { countDate } from '../../../components/molecules/user/dateConversion'; +import { faCalendarAlt, faUser } from '@fortawesome/free-solid-svg-icons'; const ContentContainer = styled.div` padding: 20px 0; @@ -41,6 +47,12 @@ const SectionTitle = styled.h3` const ChannelBottomContainer = styled.div` display: flex; list-style: none; + color: rgba(0,0,0,.47); + margin: 20px 0 0; + li { + margin: 10px; // Adds 10px of space on all sides of the list items + } + ` const RoundThumnail = styled.div` @@ -55,6 +67,22 @@ const RoundThumnail = styled.div` border: 1px solid rgba(0,0,0,.05); margin-right: 28px; `; +const UserBannerSC = styled.div` + display: flex; + align-items: center; + gap: 10px; + padding-right: 20px; + padding-top: 20px; + + .nextImg { + flex: 1; + } + + a { + display: flex; + align-items: center; + } +`; export default function ChannelAbout() { const { chnlUri } = useParams(); @@ -79,10 +107,22 @@ export default function ChannelAbout() {

      {data.data.channel.chnlIntro}

      -
    • (아이콘) 구독자 {data.data.channel.suberCnt}명
    • -
    • (아이콘) 포스트 {data.data.channel.chnlPostCnt}개
    • -
    • (아이콘) 좋아요 {data.data.channel.chnlLikCnt}개
    • -
    • (아이콘) 개설일 {data.data.channel.chnlOpenDtm}
    • + {/**/} + {/* */} + {/* {data.data.channel.suberCnt}*/} + {/**/} + {/**/} + {/* */} + {/**/} + {/**/} + {/* */} + {/* {data.data.channel.chnlLikCnt}*/} + {/**/} + {/*{countDate(data.data.channel.chnlOpenDtm)}*/} +
    • 구독자 {data.data.channel.suberCnt}명
    • +
    • 포스트 {data.data.channel.chnlPostCnt}개
    • +
    • 좋아요 {data.data.channel.chnlLikCnt}개
    • +
    • 개설일 {countDate(data.data.channel.chnlOpenDtm)}
    • diff --git a/yarn.lock b/yarn.lock index 180bd82..47c3c91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1288,6 +1288,13 @@ dependencies: "@fortawesome/fontawesome-common-types" "6.4.0" +"@fortawesome/free-regular-svg-icons@^6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.0.tgz#cacc53bd8d832d46feead412d9ea9ce80a55e13a" + integrity sha512-ZfycI7D0KWPZtf7wtMFnQxs8qjBXArRzczABuMQqecA/nXohquJ5J/RCR77PmY5qGWkxAZDxpnUFVXKwtY/jPw== + dependencies: + "@fortawesome/fontawesome-common-types" "6.4.0" + "@fortawesome/free-solid-svg-icons@^6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.0.tgz#48c0e790847fa56299e2f26b82b39663b8ad7119" From 2c27405fc54a84eff090cfe1551ff115e6377754 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 13:16:19 +0900 Subject: [PATCH 15/90] Add: add react pagination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 리액트 페이지네이션을 설치 --- package.json | 1 + yarn.lock | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 681cdb7..eab9112 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.8.0", "react-modal": "^3.16.1", + "react-paginate": "^8.2.0", "react-router-dom": "^6.10.0", "react-scripts": "5.0.1", "react-tippy": "^1.4.0", diff --git a/yarn.lock b/yarn.lock index 47c3c91..351e182 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7897,7 +7897,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8085,6 +8085,13 @@ react-modal@^3.16.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" +react-paginate@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-8.2.0.tgz#947c3dcb444a6c16c1bcf8361871aa135baa3dcd" + integrity sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw== + dependencies: + prop-types "^15" + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" From 804851f4fc11946c75a04a28230fbcb832c006fd Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 22:06:31 +0900 Subject: [PATCH 16/90] =?UTF-8?q?update:=20api=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 모든 api를 연동해 실제 데이터로 바꿨다. --- package.json | 1 - yarn.lock | 9 +-------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/package.json b/package.json index eab9112..681cdb7 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "react-dom": "^18.2.0", "react-icons": "^4.8.0", "react-modal": "^3.16.1", - "react-paginate": "^8.2.0", "react-router-dom": "^6.10.0", "react-scripts": "5.0.1", "react-tippy": "^1.4.0", diff --git a/yarn.lock b/yarn.lock index 351e182..47c3c91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7897,7 +7897,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8085,13 +8085,6 @@ react-modal@^3.16.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" -react-paginate@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-8.2.0.tgz#947c3dcb444a6c16c1bcf8361871aa135baa3dcd" - integrity sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw== - dependencies: - prop-types "^15" - react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" From 4935b0822109e4354fee8782644f95eb08527fd2 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 5 Jul 2023 00:42:58 +0900 Subject: [PATCH 17/90] feat: Add channel home webtoon, webnovel tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 웹툰 탭 작업 2. 웹소설 탭 작업(포스트 리스트 컴포넌트 사용) 3. 채널홈 전체 api 연동 4. 채널 uri 라우터 변수 처리 --- src/pages/general/channel/ChannelHome.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/general/channel/ChannelHome.js b/src/pages/general/channel/ChannelHome.js index df6643d..82e2a94 100644 --- a/src/pages/general/channel/ChannelHome.js +++ b/src/pages/general/channel/ChannelHome.js @@ -148,9 +148,6 @@ export default function ChannelHome() {
      )} - -
      - ); } From aa96992e874e36cbb586d53a7c05137fd4cd2763 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 5 Jul 2023 12:13:17 +0900 Subject: [PATCH 18/90] feat: Added link button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 채널홈에서 웹툰과 웹소설 탭으로 가는 링크 버튼 추가 --- src/pages/general/channel/ChannelHome.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/general/channel/ChannelHome.js b/src/pages/general/channel/ChannelHome.js index 82e2a94..f5949fd 100644 --- a/src/pages/general/channel/ChannelHome.js +++ b/src/pages/general/channel/ChannelHome.js @@ -1,11 +1,12 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; import styled from 'styled-components'; import useUserStore from '../../../stores/useUserStore'; -import { useParams } from 'react-router-dom'; +import { NavLink, useParams } from 'react-router-dom'; import { useApiGet } from '../../../hooks/useApi'; import PostItem from '../../../components/organisms/general/PostItem'; import NoContent from '../../../components/molecules/error/NoContent'; import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; +import Nav from '../../../components/organisms/general/Nav'; const SectionHeader = styled.div` padding: 0 0 10px; @@ -99,7 +100,7 @@ export default function ChannelHome() { 웹툰 - {">"} + {">"} {data.data.webtoons.map((post, index) => ( @@ -133,7 +134,7 @@ export default function ChannelHome() { 웹소설 - {">"} + {">"} {data.data.webnovels.length !== 0 ? ( From b9b245b99bd56c1101448299ba9d3193d8464ea1 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 13:16:19 +0900 Subject: [PATCH 19/90] Add: add react pagination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 리액트 페이지네이션을 설치 --- package.json | 1 + yarn.lock | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 681cdb7..eab9112 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.8.0", "react-modal": "^3.16.1", + "react-paginate": "^8.2.0", "react-router-dom": "^6.10.0", "react-scripts": "5.0.1", "react-tippy": "^1.4.0", diff --git a/yarn.lock b/yarn.lock index 47c3c91..351e182 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7897,7 +7897,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8085,6 +8085,13 @@ react-modal@^3.16.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" +react-paginate@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-8.2.0.tgz#947c3dcb444a6c16c1bcf8361871aa135baa3dcd" + integrity sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw== + dependencies: + prop-types "^15" + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" From f2439d9709af781e595557bf5c1d15a7737aba38 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Tue, 4 Jul 2023 22:06:31 +0900 Subject: [PATCH 20/90] =?UTF-8?q?update:=20api=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 모든 api를 연동해 실제 데이터로 바꿨다. --- package.json | 1 - yarn.lock | 9 +-------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/package.json b/package.json index eab9112..681cdb7 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "react-dom": "^18.2.0", "react-icons": "^4.8.0", "react-modal": "^3.16.1", - "react-paginate": "^8.2.0", "react-router-dom": "^6.10.0", "react-scripts": "5.0.1", "react-tippy": "^1.4.0", diff --git a/yarn.lock b/yarn.lock index 351e182..47c3c91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7897,7 +7897,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8085,13 +8085,6 @@ react-modal@^3.16.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" -react-paginate@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-8.2.0.tgz#947c3dcb444a6c16c1bcf8361871aa135baa3dcd" - integrity sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw== - dependencies: - prop-types "^15" - react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" From 9a8bb78531b8c984e5a3d1abf6229935010c82c5 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 19 Jul 2023 09:53:30 +0900 Subject: [PATCH 21/90] =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/general/channel/ChannelHome.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/general/channel/ChannelHome.js b/src/pages/general/channel/ChannelHome.js index f5949fd..aa7aec4 100644 --- a/src/pages/general/channel/ChannelHome.js +++ b/src/pages/general/channel/ChannelHome.js @@ -136,7 +136,6 @@ export default function ChannelHome() { 웹소설 {">"} - {data.data.webnovels.length !== 0 ? ( data.data.webnovels.map((post) => ) ) : ( From 3c885d7e9f3471dbb3a3c2d465575d6d8924f8ae Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 5 Jul 2023 21:13:05 +0900 Subject: [PATCH 22/90] feat: Added editor feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 리액트 퀼과 퀼을 설치해 에디터를 구현했습니다. --- package.json | 2 + src/appRoute.js | 7 + .../templates/general/ChannelTemplate.js | 10 +- .../templates/general/CreateTemplate.js | 38 +++++ src/pages/general/channel/ChannelAbout.js | 4 +- src/pages/general/channel/ChannelHome.js | 5 +- src/pages/general/channel/ChannelWebnovel.js | 4 +- src/pages/general/channel/ChannelWebtoon.js | 4 +- src/pages/general/create/PostCreate.js | 138 ++++++++++++++++++ src/pages/general/home/Home.js | 3 + yarn.lock | 98 ++++++++++++- 11 files changed, 293 insertions(+), 20 deletions(-) create mode 100644 src/components/templates/general/CreateTemplate.js create mode 100644 src/pages/general/create/PostCreate.js diff --git a/package.json b/package.json index 681cdb7..e147d4c 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,12 @@ "axios": "^1.3.6", "date-fns": "^2.30.0", "json-server": "^0.17.3", + "quill-image-resize": "^3.0.9", "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^4.8.0", "react-modal": "^3.16.1", + "react-quill": "^2.0.0", "react-router-dom": "^6.10.0", "react-scripts": "5.0.1", "react-tippy": "^1.4.0", diff --git a/src/appRoute.js b/src/appRoute.js index 4333438..5713ad7 100644 --- a/src/appRoute.js +++ b/src/appRoute.js @@ -24,6 +24,7 @@ import ChannelHome from './pages/general/channel/ChannelHome'; import ChannelWebtoon from './pages/general/channel/ChannelWebtoon'; import ChannelWebnovel from './pages/general/channel/ChannelWebnovel'; import ChannelAbout from './pages/general/channel/ChannelAbout'; +import PostCreate from './pages/general/create/PostCreate'; const urls = { login: '/login', @@ -71,6 +72,9 @@ const urls = { profile: `/profile/:nic`, profilePost: `/profile/:nic/post`, profileSeries: `/profile/:nic/series`, + + // crate + postCreate: `/post/create`, }; // Links @@ -168,4 +172,7 @@ export const pageRoutes = [ { id: 28, path: urls.channelWebtoon, element: }, { id: 29, path: urls.channelWebnovel, element: }, { id: 30, path: urls.channelAbout, element: }, + + // create + { id: 31, path: urls.postCreate, element: }, ]; diff --git a/src/components/templates/general/ChannelTemplate.js b/src/components/templates/general/ChannelTemplate.js index 3e446c2..873e008 100644 --- a/src/components/templates/general/ChannelTemplate.js +++ b/src/components/templates/general/ChannelTemplate.js @@ -53,20 +53,20 @@ export default function ChannelTemplate(p) { const mainNavLinks = [ { - to: `/channel/${encodeURIComponent(p.chnlUri)}`, + to: `/channel/${encodeURIComponent(chnlUri)}`, children: '홈', end: true, }, { - to: `/channel/${encodeURIComponent(p.chnlUri)}/webtoon`, + to: `/channel/${encodeURIComponent(chnlUri)}/webtoon`, children: '웹툰', }, { - to: `/channel/${encodeURIComponent(p.chnlUri)}/webnovel`, + to: `/channel/${encodeURIComponent(chnlUri)}/webnovel`, children: '웹소설', }, { - to: `/channel/${encodeURIComponent(p.chnlUri)}/about`, + to: `/channel/${encodeURIComponent(chnlUri)}/about`, children: '소개', }, ]; @@ -82,7 +82,7 @@ export default function ChannelTemplate(p) { {data.data.channel.chnlPostCnt}개 {data.data.channel.chnlTtl} - {/* + {/* // TODO: 구독하기 버튼 처리
      구독하기 버튼 diff --git a/src/components/templates/general/CreateTemplate.js b/src/components/templates/general/CreateTemplate.js new file mode 100644 index 0000000..555c1b6 --- /dev/null +++ b/src/components/templates/general/CreateTemplate.js @@ -0,0 +1,38 @@ +import NavMenu from '../../molecules/general/NavMenu'; +import Nav from '../../organisms/general/Nav'; +import Header from '../../organisms/general/Header'; +import Main from '../../organisms/general/Main'; +import styled from 'styled-components'; +import { useApiGet } from '../../../hooks/useApi'; +import { useParams } from 'react-router-dom'; +import useUserStore from '../../../stores/useUserStore'; +import { useEffect, useState } from 'react'; + +export default function CreateTemplate(p) { + // const { chnlUri } = useParams(); + // + // const [url, setUrl] = useState(`/channel/${encodeURIComponent(chnlUri)}`); + // const { data, isLoading, error } = useApiGet( + // url, + // [url] + // ); + // useEffect(() => { + // setUrl(`/channel/${encodeURIComponent(chnlUri)}`); + // }, [chnlUri]); + // + // const { user } = useUserStore(); + // if (isLoading) return; + // if (error) return {`[${error.code}] ${error.message}`}; + // + // if (!data) { + // return null; + // } + + return ( + <> +
      + +
      {p.children}
      + + ); +} diff --git a/src/pages/general/channel/ChannelAbout.js b/src/pages/general/channel/ChannelAbout.js index 446ae0b..f20d045 100644 --- a/src/pages/general/channel/ChannelAbout.js +++ b/src/pages/general/channel/ChannelAbout.js @@ -98,7 +98,7 @@ export default function ChannelAbout() { return null; } return ( - + @@ -140,4 +140,4 @@ export default function ChannelAbout() { ); -} \ No newline at end of file +} diff --git a/src/pages/general/channel/ChannelHome.js b/src/pages/general/channel/ChannelHome.js index aa7aec4..e7a5f11 100644 --- a/src/pages/general/channel/ChannelHome.js +++ b/src/pages/general/channel/ChannelHome.js @@ -6,7 +6,6 @@ import { useApiGet } from '../../../hooks/useApi'; import PostItem from '../../../components/organisms/general/PostItem'; import NoContent from '../../../components/molecules/error/NoContent'; import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; -import Nav from '../../../components/organisms/general/Nav'; const SectionHeader = styled.div` padding: 0 0 10px; @@ -97,7 +96,7 @@ export default function ChannelHome() { return ( - + 웹툰 {">"} @@ -124,8 +123,6 @@ export default function ChannelHome() { 21시간 전 - - ))} diff --git a/src/pages/general/channel/ChannelWebnovel.js b/src/pages/general/channel/ChannelWebnovel.js index 50c44fb..dda904b 100644 --- a/src/pages/general/channel/ChannelWebnovel.js +++ b/src/pages/general/channel/ChannelWebnovel.js @@ -114,7 +114,7 @@ export default function ChannelWebnovel() { return ( - + {data.data.channel.chnlWebnovelCnt}개의 포스트 최신순 | 인기순 @@ -151,4 +151,4 @@ export default function ChannelWebnovel() { ); -} \ No newline at end of file +} diff --git a/src/pages/general/channel/ChannelWebtoon.js b/src/pages/general/channel/ChannelWebtoon.js index 68b0159..758ab51 100644 --- a/src/pages/general/channel/ChannelWebtoon.js +++ b/src/pages/general/channel/ChannelWebtoon.js @@ -173,7 +173,7 @@ export default function ChannelWebtoon() { return ( - + {data.data.channel.chnlWebtoonCnt}개의 포스트 최신순 | 인기순 @@ -226,4 +226,4 @@ export default function ChannelWebtoon() { ); -} \ No newline at end of file +} diff --git a/src/pages/general/create/PostCreate.js b/src/pages/general/create/PostCreate.js new file mode 100644 index 0000000..018ce32 --- /dev/null +++ b/src/pages/general/create/PostCreate.js @@ -0,0 +1,138 @@ +import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; +import styled from 'styled-components'; +import useUserStore from '../../../stores/useUserStore'; +import { useParams } from 'react-router-dom'; +import { useApiGet } from '../../../hooks/useApi'; +import PostItem from '../../../components/organisms/general/PostItem'; +import NoContent from '../../../components/molecules/error/NoContent'; +import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; +import CreateTemplate from '../../../components/templates/general/CreateTemplate'; +import React, { useState } from 'react'; +import ReactQuill, {Quill} from 'react-quill'; +import 'react-quill/dist/quill.snow.css'; +import TextInputSC from '../../../components/atoms/Input/TextInputSC'; +import ImageResize from 'quill-image-resize'; + +Quill.register('modules/imageResize', ImageResize); + +export const SubmitButton = styled.button` + display: block; + width: fit-content; + padding: 7px 15px; + font-size: 15px; + border-radius: 4px; + font-weight: normal; + color: white; + background-color: #3478FF; + border: 1px solid #3478FF; +`; + +export default function PostCreate() { + // const { user } = useUserStore(); + // const { chnlUri } = useParams(); + // const { data, isLoading, error } = useApiGet( + // `/channel/${encodeURIComponent(chnlUri)}`, + // [chnlUri] + // ); + // + // if (isLoading) return; + // if (error) return {`[${error.code}] ${error.message}`}; + // + // if (!data) { + // return null; + // } + + const [title, setTitle] = useState(''); + const [content, setContent] = useState(''); + const [subTitle, setSubTitle] = useState(''); + // const modules = { + // toolbar: [ + // ['bold', 'italic', 'underline', 'strike'], // 텍스트 스타일 + // [{ list: 'ordered' }, { list: 'bullet' }], // 리스트 + // [{ indent: '-1' }, { indent: '+1' }], // 들여쓰기 + // [{ align: [] }], // 정렬 + // ['link', 'image'], // 링크, 이미지 + // ['clean'], // 포맷 지우기 + // ], + // }; + + const modules = { + toolbar: [ + [{ 'header': [1, 2, false] }], + [{ 'size': ['small', false, 'large', 'huge'] }], // 새로운 글자 크기 조절 옵션 + ['bold', 'italic', 'underline','strike', 'blockquote'], + [{ 'align': [] }], // 새로운 텍스트 정렬 옵션 + [{'list': 'ordered'}, {'list': 'bullet'}, {'indent': '-1'}, {'indent': '+1'}], + ['link', 'image'], + ['clean'] + ], + imageResize: {} + } + const formats = [ + 'header', + 'bold', 'italic', 'underline', 'strike', 'blockquote', + 'align', 'list', 'bullet', 'indent', + 'link', 'image', + 'size' + ] + + const handleTitleChange = (event) => { + setTitle(event.target.value); + }; + + const handleSubTitleChange = (event) =>{ + setSubTitle(event.target.value); + } + + const handleEditorChange = (value) => { + setContent(value); + }; + + const handleFormSubmit = (event) => { + event.preventDefault(); + // 여기에서 포스트를 작성하거나 전송하는 로직을 추가할 수 있습니다 + console.log('제목:', title); + console.log('부제목', subTitle); + console.log('내용:', content); + }; + + + return ( + +
      +
      + {/**/} + + + +
      + + + +
      + +
      + + 게시 + +
      +
      + ); +} diff --git a/src/pages/general/home/Home.js b/src/pages/general/home/Home.js index 350b301..1f14477 100644 --- a/src/pages/general/home/Home.js +++ b/src/pages/general/home/Home.js @@ -19,6 +19,9 @@ export default function Home() {

      BUKSAN 채널 바로가기

      + +

      POST CREATE 바로가기

      +
      diff --git a/yarn.lock b/yarn.lock index 47c3c91..8d89af6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2125,6 +2125,13 @@ resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== +"@types/quill@^1.3.10": + version "1.3.10" + resolved "https://registry.yarnpkg.com/@types/quill/-/quill-1.3.10.tgz#dc1f7b6587f7ee94bdf5291bc92289f6f0497613" + integrity sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw== + dependencies: + parchment "^1.1.2" + "@types/range-parser@*": version "1.2.4" resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" @@ -3249,6 +3256,11 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" +clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== + co@^4.6.0: version "4.6.0" resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" @@ -3745,6 +3757,18 @@ dedent@^0.7.0: resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +deep-equal@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + deep-equal@^2.0.5: version "2.2.0" resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz" @@ -4569,6 +4593,11 @@ etag@~1.8.1: resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +eventemitter3@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba" + integrity sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg== + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" @@ -4670,11 +4699,21 @@ express@^4.17.1, express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" +extend@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" + integrity sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig== + fast-glob@^3.2.12, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz" @@ -5409,7 +5448,7 @@ ipaddr.js@^2.0.1: resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz" integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== -is-arguments@^1.1.1: +is-arguments@^1.0.4, is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== @@ -5551,7 +5590,7 @@ is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== -is-regex@^1.1.4: +is-regex@^1.0.4, is-regex@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== @@ -6572,7 +6611,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@4, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: +lodash@4, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -6914,7 +6953,7 @@ object-inspect@^1.12.3, object-inspect@^1.9.0: resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== -object-is@^1.1.5: +object-is@^1.0.1, object-is@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== @@ -7110,6 +7149,11 @@ param-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" +parchment@^1.1.2, parchment@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/parchment/-/parchment-1.1.4.tgz#aeded7ab938fe921d4c34bc339ce1168bc2ffde5" + integrity sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" @@ -7956,6 +8000,36 @@ quick-lru@^5.1.1: resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== +quill-delta@^3.6.2: + version "3.6.3" + resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-3.6.3.tgz#b19fd2b89412301c60e1ff213d8d860eac0f1032" + integrity sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg== + dependencies: + deep-equal "^1.0.1" + extend "^3.0.2" + fast-diff "1.1.2" + +quill-image-resize@^3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/quill-image-resize/-/quill-image-resize-3.0.9.tgz#5677fb6257560bff951153ddbdb836758e49f804" + integrity sha512-5Dk0nixhbFsCwSWtPU9qqqtfM2gURfaP+pbBhQvAoMJoF4p99xbAibfAI3gsZJkbWUodoK2iAPf1V5oTSpv9ww== + dependencies: + lodash "^4.17.4" + quill "^1.2.2" + raw-loader "^0.5.1" + +quill@^1.2.2, quill@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/quill/-/quill-1.3.7.tgz#da5b2f3a2c470e932340cdbf3668c9f21f9286e8" + integrity sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g== + dependencies: + clone "^2.1.1" + deep-equal "^1.0.1" + eventemitter3 "^2.0.3" + extend "^3.0.2" + parchment "^1.1.4" + quill-delta "^3.6.2" + raf@^3.4.1: version "3.4.1" resolved "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz" @@ -7995,6 +8069,11 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +raw-loader@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" + integrity sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q== + react-app-polyfill@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz" @@ -8085,6 +8164,15 @@ react-modal@^3.16.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" +react-quill@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/react-quill/-/react-quill-2.0.0.tgz#67a0100f58f96a246af240c9fa6841b363b3e017" + integrity sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg== + dependencies: + "@types/quill" "^1.3.10" + lodash "^4.17.4" + quill "^1.3.7" + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" @@ -8254,7 +8342,7 @@ regex-parser@^2.2.11: resolved "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz" integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== -regexp.prototype.flags@^1.4.3: +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: version "1.5.0" resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz" integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== From d8733b598de64b65d73956f825e3faf830dbc986 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Thu, 6 Jul 2023 10:21:46 +0900 Subject: [PATCH 23/90] update: Update input image feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 포스트 썸네일 이미지 업로드방식을 텍스트 인풋에서 accept로 바꿈 2. 아직 기능 개발중 --- .../molecules/post/PostRadioButton.js | 55 ++++++++++++ .../templates/general/ChannelTemplate.js | 14 ++-- src/pages/general/create/PostCreate.js | 84 ++++++++++++++++--- 3 files changed, 134 insertions(+), 19 deletions(-) create mode 100644 src/components/molecules/post/PostRadioButton.js diff --git a/src/components/molecules/post/PostRadioButton.js b/src/components/molecules/post/PostRadioButton.js new file mode 100644 index 0000000..0eeb5ee --- /dev/null +++ b/src/components/molecules/post/PostRadioButton.js @@ -0,0 +1,55 @@ +import React, { useState } from 'react'; +import styled from 'styled-components'; + +const RadioButtonContainer = styled.div` + display: flex; + gap: 2px; +`; + +const RadioButtonLabel = styled.label` + display: inline-block; + background-color: ${({ checked }) => (checked ? '#3478FF' : '#fff')}; + color: ${({ checked }) => (checked ? '#fff' : 'rgba(0, 0, 0, 0.3)')}; + border: 2px solid #eee; + border-radius: 8px; + padding: 8px 30px; + cursor: pointer; + transition: background-color 0.3s, color 0.3s; + + &:hover { + background-color: #3478FF; + color: #fff; + } +`; + +const RadioButtonInput = styled.input` + display: none; +` +const PostRadioButton = ({ options, selectedOption, handleOptionChange }) => { + // const [selectedOption, setSelectedOption] = useState(''); + + // const handleOptionChange = (event) => { + // setSelectedOption(event.target.value); + // }; + + return ( + + {options.map((option) => ( + + + {option.label} + + ))} + + ); +}; + +export default PostRadioButton; diff --git a/src/components/templates/general/ChannelTemplate.js b/src/components/templates/general/ChannelTemplate.js index 873e008..dcee3eb 100644 --- a/src/components/templates/general/ChannelTemplate.js +++ b/src/components/templates/general/ChannelTemplate.js @@ -7,6 +7,7 @@ import { useApiGet } from '../../../hooks/useApi'; import { useParams } from 'react-router-dom'; import useUserStore from '../../../stores/useUserStore'; import { useEffect, useState } from 'react'; +import BtnLinkSC from '../../atoms/Link/BtnLinkSC'; const Thumbnail = styled.div` width: 80px; @@ -82,11 +83,12 @@ export default function ChannelTemplate(p) { {data.data.channel.chnlPostCnt}개 {data.data.channel.chnlTtl} - {/* - // TODO: 구독하기 버튼 처리 -
      - 구독하기 버튼 -
      */} + +
      + {/*// TODO: 구독하기 버튼 처리*/} + 구독하기 + 포스트 발행하기 +
      @@ -94,4 +96,4 @@ export default function ChannelTemplate(p) {
      ); -} +} \ No newline at end of file diff --git a/src/pages/general/create/PostCreate.js b/src/pages/general/create/PostCreate.js index 018ce32..087ee63 100644 --- a/src/pages/general/create/PostCreate.js +++ b/src/pages/general/create/PostCreate.js @@ -12,13 +12,16 @@ import ReactQuill, {Quill} from 'react-quill'; import 'react-quill/dist/quill.snow.css'; import TextInputSC from '../../../components/atoms/Input/TextInputSC'; import ImageResize from 'quill-image-resize'; +import PostRadioButton from '../../../components/molecules/post/PostRadioButton'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faImage, faUser } from '@fortawesome/free-solid-svg-icons'; +import PostImg from '../../../components/atoms/Post/PostImgSC'; Quill.register('modules/imageResize', ImageResize); - export const SubmitButton = styled.button` display: block; width: fit-content; - padding: 7px 15px; + padding: 8px 30px; font-size: 15px; border-radius: 4px; font-weight: normal; @@ -27,6 +30,8 @@ export const SubmitButton = styled.button` border: 1px solid #3478FF; `; + + export default function PostCreate() { // const { user } = useUserStore(); // const { chnlUri } = useParams(); @@ -45,6 +50,8 @@ export default function PostCreate() { const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const [subTitle, setSubTitle] = useState(''); + const [thumbnailImageUrl, setThumbnailImageUrl] = useState(null); + const [postType, setPostType] = useState(''); // const modules = { // toolbar: [ // ['bold', 'italic', 'underline', 'strike'], // 텍스트 스타일 @@ -84,29 +91,55 @@ export default function PostCreate() { setSubTitle(event.target.value); } + const handleThumbnailImageUrlChange = (event) =>{ + setThumbnailImageUrl(event.target.value); + }; + + const handleOptionChange = (event) => { + setPostType(event.target.value); + }; + const handleEditorChange = (value) => { setContent(value); }; const handleFormSubmit = (event) => { event.preventDefault(); - // 여기에서 포스트를 작성하거나 전송하는 로직을 추가할 수 있습니다 - console.log('제목:', title); - console.log('부제목', subTitle); - console.log('내용:', content); + + // 선택한 postType 값과 기타 필요한 데이터를 API로 전송합니다. + const postData = { + postType: postType, + postTtl: title, + postSbTtl: subTitle, + postContent: content, + postThumnPath: thumbnailImageUrl, + }; + + // API 호출 등의 로직을 추가합니다. + // 예: axios.post('/api/posts', postData) + console.log('전송할 데이터:', postData); }; + const options = [ + { label: '웹툰', value: '웹툰' }, + { label: '웹소설', value: '웹소설' }, + + ]; + return (
      - {/**/} +

      포스트 작성하기

      + +

      포스트 타입

      + +
      -
      +
      + +
      + + + + + + +
      + +
      +
      게시 +
      From aadee947bb00d118c8d6405140ae1ddec6d1d2b6 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Fri, 7 Jul 2023 10:46:58 +0900 Subject: [PATCH 24/90] feat: Added uploading image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 게시판에 이미지 업로드 기능 구현. 2. 썸네일 이미지 업로드 기능 구현. --- src/pages/general/create/PostCreate.js | 197 ++++++++++++++++++------- 1 file changed, 147 insertions(+), 50 deletions(-) diff --git a/src/pages/general/create/PostCreate.js b/src/pages/general/create/PostCreate.js index 087ee63..7748fca 100644 --- a/src/pages/general/create/PostCreate.js +++ b/src/pages/general/create/PostCreate.js @@ -2,12 +2,12 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTempla import styled from 'styled-components'; import useUserStore from '../../../stores/useUserStore'; import { useParams } from 'react-router-dom'; -import { useApiGet } from '../../../hooks/useApi'; +import { useApiGet, useApiPost } from '../../../hooks/useApi'; import PostItem from '../../../components/organisms/general/PostItem'; import NoContent from '../../../components/molecules/error/NoContent'; import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; import CreateTemplate from '../../../components/templates/general/CreateTemplate'; -import React, { useState } from 'react'; +import React, { useMemo, useRef, useState } from 'react'; import ReactQuill, {Quill} from 'react-quill'; import 'react-quill/dist/quill.snow.css'; import TextInputSC from '../../../components/atoms/Input/TextInputSC'; @@ -30,6 +30,26 @@ export const SubmitButton = styled.button` border: 1px solid #3478FF; `; +export const PostImgInputSC = styled.input` + display: inline-block; + padding: 8px 30px; + font-size: 15px; + border-radius: 4px; + font-weight: normal; + color: white; + background-color: #3478FF; + border: 1px solid #3478FF; + cursor: pointer; + margin-left: 10px; // 이미지와 버튼 사이에 간격을 주기 위함 +`; + +export const ImageContainerSC = styled.div` + display: flex; + align-items: center; + margin-bottom: 20px; +`; + + export default function PostCreate() { @@ -52,6 +72,85 @@ export default function PostCreate() { const [subTitle, setSubTitle] = useState(''); const [thumbnailImageUrl, setThumbnailImageUrl] = useState(null); const [postType, setPostType] = useState(''); + + const [imgFile, setImgFile] = useState(""); + const imgRef = useRef(); + const quillRef = useRef(); + const { + res: uploadRes, + error: uploadErr, + setError: setUploadErr, + postData: upload, + } = useApiPost(`/uploadFiles`, new FormData()); + + const imageHandler = () => { + console.log('에디터에서 이미지 버튼을 클릭하면 이 핸들러가 시작됩니다!'); + // + // 1. 이미지를 저장할 input type=file DOM을 만든다. + const input = document.createElement('input'); + // // 속성 써주기 + input.setAttribute('type', 'file'); + input.setAttribute('accept', 'image/*'); + input.click(); // 에디터 이미지버튼을 클릭하면 이 input이 클릭된다. + // // input이 클릭되면 파일 선택창이 나타난다. + // + // // input에 변화가 생긴다면 = 이미지를 선택 + input.addEventListener('change', async () => { + console.log('온체인지'); + const files = input.files; + // multer에 맞는 형식으로 데이터 만들어준다. + const formData = new FormData(); + Array.prototype.forEach.call(files, function(file) { + formData.append('multipartFiles',file); + }); + + // 백엔드 라우터에 이미지를 보낸다. + try { + upload(formData); + + // const result = await axios.post('http://localhost:4050/img', formData); + + console.log('성공 시, 백엔드가 보내주는 데이터', uploadRes.data.url); + let IMG_URL = {}; + IMG_URL.add(uploadRes.data.url); + // // 이 URL을 img 태그의 src에 넣은 요소를 현재 에디터의 커서에 넣어주면 에디터 내에서 이미지가 나타난다 + // // src가 base64가 아닌 짧은 URL이기 때문에 데이터베이스에 에디터의 전체 글 내용을 저장할 수있게된다 + // // 이미지는 꼭 로컬 백엔드 uploads 폴더가 아닌 다른 곳에 저장해 URL로 사용하면된다. + // + // // 이미지 태그를 에디터에 써주기 - 여러 방법이 있다. + const editor = quillRef.current.getEditor(); // 에디터 객체 가져오기 + // // 1. 에디터 root의 innerHTML을 수정해주기 + // // editor의 root는 에디터 컨텐츠들이 담겨있다. 거기에 img태그를 추가해준다. + // // 이미지를 업로드하면 -> 멀터에서 이미지 경로 URL을 받아와 -> 이미지 요소로 만들어 에디터 안에 넣어준다. + editor.root.innerHTML = + editor.root.innerHTML + `
      `; // 현재 있는 내용들 뒤에 써줘야한다. + // + // // 2. 현재 에디터 커서 위치값을 가져온다 + const range = editor.getSelection(); + // // 가져온 위치에 이미지를 삽입한다 + editor.insertEmbed(range.index, 'image', IMG_URL); + } catch (error) { + console.log('실패했어요ㅠ'); + } + }); + }; + +// 이미지 업로드 input의 onChange + const saveImgFile = () => { + if(imgRef.current.files.length > 0) { + const file = imgRef.current.files[0]; + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onloadend = () => { + setImgFile(reader.result); + console.log(imgFile); + }; + } else { + console.warn('No file selected'); + } + }; + +// 업로드 된 이미지 미리보기 // const modules = { // toolbar: [ // ['bold', 'italic', 'underline', 'strike'], // 텍스트 스타일 @@ -63,18 +162,26 @@ export default function PostCreate() { // ], // }; - const modules = { - toolbar: [ - [{ 'header': [1, 2, false] }], - [{ 'size': ['small', false, 'large', 'huge'] }], // 새로운 글자 크기 조절 옵션 - ['bold', 'italic', 'underline','strike', 'blockquote'], - [{ 'align': [] }], // 새로운 텍스트 정렬 옵션 - [{'list': 'ordered'}, {'list': 'bullet'}, {'indent': '-1'}, {'indent': '+1'}], - ['link', 'image'], - ['clean'] - ], - imageResize: {} - } + const modules = useMemo(() => { + return { + toolbar: { + container: [ + [{ 'header': [1, 2, false] }], + [{ 'size': ['small', false, 'large', 'huge'] }], // 새로운 글자 크기 조절 옵션 + ['bold', 'italic', 'underline', 'strike', 'blockquote'], + [{ 'align': [] }], // 새로운 텍스트 정렬 옵션 + [{ 'list': 'ordered' }, { 'list': 'bullet' }, { 'indent': '-1' }, { 'indent': '+1' }], + ['link', 'image'], + ['clean'] + ], + handlers: { + image: imageHandler, + }, + imageResize: {} + }, + } + },[]); + const formats = [ 'header', 'bold', 'italic', 'underline', 'strike', 'blockquote', @@ -83,6 +190,8 @@ export default function PostCreate() { 'size' ] + + const handleTitleChange = (event) => { setTitle(event.target.value); }; @@ -91,9 +200,6 @@ export default function PostCreate() { setSubTitle(event.target.value); } - const handleThumbnailImageUrlChange = (event) =>{ - setThumbnailImageUrl(event.target.value); - }; const handleOptionChange = (event) => { setPostType(event.target.value); @@ -123,32 +229,32 @@ export default function PostCreate() { const options = [ { label: '웹툰', value: '웹툰' }, { label: '웹소설', value: '웹소설' }, - ]; - return (

      포스트 작성하기

      + + imgRef.current.click()} /> + +

      썸네일을 선택해 주세요!

      +
      -

      포스트 타입

      +
      +

      포스트 타입

      -
      - - -
      -
      - - - - - - + + {/* */} + {/* */}
      - +
      From 6ea7cfd252b81477f3f8d41fd68a17bd05f8ec75 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Fri, 7 Jul 2023 15:13:54 +0900 Subject: [PATCH 25/90] fix: fixed uploding image response url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 이미지 업로드후 resonse 응답으로 오는 데이터를 고침 --- src/pages/general/create/PostCreate.js | 104 +++++++++++++++---------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/src/pages/general/create/PostCreate.js b/src/pages/general/create/PostCreate.js index 7748fca..837dea2 100644 --- a/src/pages/general/create/PostCreate.js +++ b/src/pages/general/create/PostCreate.js @@ -76,12 +76,25 @@ export default function PostCreate() { const [imgFile, setImgFile] = useState(""); const imgRef = useRef(); const quillRef = useRef(); + const { + res: postRes, + error: postErr, + setError: setPostErr, + postData: post, + } = useApiPost(`post/create`, { + postType: postType, + postTtl: title, + postSbTtl: subTitle, + postContent: content, + postThumnPath: thumbnailImageUrl, + }); + const { res: uploadRes, error: uploadErr, setError: setUploadErr, postData: upload, - } = useApiPost(`/uploadFiles`, new FormData()); + } = useApiPost(`file/uploadFiles`, new FormData()); const imageHandler = () => { console.log('에디터에서 이미지 버튼을 클릭하면 이 핸들러가 시작됩니다!'); @@ -111,31 +124,33 @@ export default function PostCreate() { // const result = await axios.post('http://localhost:4050/img', formData); console.log('성공 시, 백엔드가 보내주는 데이터', uploadRes.data.url); - let IMG_URL = {}; - IMG_URL.add(uploadRes.data.url); - // // 이 URL을 img 태그의 src에 넣은 요소를 현재 에디터의 커서에 넣어주면 에디터 내에서 이미지가 나타난다 - // // src가 base64가 아닌 짧은 URL이기 때문에 데이터베이스에 에디터의 전체 글 내용을 저장할 수있게된다 - // // 이미지는 꼭 로컬 백엔드 uploads 폴더가 아닌 다른 곳에 저장해 URL로 사용하면된다. - // - // // 이미지 태그를 에디터에 써주기 - 여러 방법이 있다. + // // 이 URL을 img 태그의 src에 넣은 요소를 현재 에디터의 커서에 넣어주면 에디터 내에서 이미지가 나타난다 + // // src가 base64가 아닌 짧은 URL이기 때문에 데이터베이스에 에디터의 전체 글 내용을 저장할 수있게된다 + // // 이미지는 꼭 로컬 백엔드 uploads 폴더가 아닌 다른 곳에 저장해 URL로 사용하면된다. + // + // // 이미지 태그를 에디터에 써주기 - 여러 방법이 있다. const editor = quillRef.current.getEditor(); // 에디터 객체 가져오기 - // // 1. 에디터 root의 innerHTML을 수정해주기 - // // editor의 root는 에디터 컨텐츠들이 담겨있다. 거기에 img태그를 추가해준다. - // // 이미지를 업로드하면 -> 멀터에서 이미지 경로 URL을 받아와 -> 이미지 요소로 만들어 에디터 안에 넣어준다. - editor.root.innerHTML = - editor.root.innerHTML + `
      `; // 현재 있는 내용들 뒤에 써줘야한다. - // - // // 2. 현재 에디터 커서 위치값을 가져온다 - const range = editor.getSelection(); - // // 가져온 위치에 이미지를 삽입한다 - editor.insertEmbed(range.index, 'image', IMG_URL); + // // 1. 에디터 root의 innerHTML을 수정해주기 + // // editor의 root는 에디터 컨텐츠들이 담겨있다. 거기에 img태그를 추가해준다. + // // 이미지를 업로드하면 -> 멀터에서 이미지 경로 URL을 받아와 -> 이미지 요소로 만들어 에디터 안에 넣어준다. + uploadRes.data.data.urls.forEach((IMG_URL) => { + editor.root.innerHTML = + editor.root.innerHTML + `
      `; + // 현재 있는 내용들 뒤에 써줘야한다. + // + // // 2. 현재 에디터 커서 위치값을 가져온다 + const range = editor.getSelection(); + editor.insertEmbed(range.index, 'image', IMG_URL); + // // 가져온 위치에 이미지를 삽입한다 + } + ) } catch (error) { console.log('실패했어요ㅠ'); } }); }; -// 이미지 업로드 input의 onChange + // 이미지 업로드 input의 onChange const saveImgFile = () => { if(imgRef.current.files.length > 0) { const file = imgRef.current.files[0]; @@ -150,7 +165,7 @@ export default function PostCreate() { } }; -// 업로드 된 이미지 미리보기 + // 업로드 된 이미지 미리보기 // const modules = { // toolbar: [ // ['bold', 'italic', 'underline', 'strike'], // 텍스트 스타일 @@ -162,7 +177,7 @@ export default function PostCreate() { // ], // }; - const modules = useMemo(() => { + const modules = useMemo(() => { return { toolbar: { container: [ @@ -182,13 +197,13 @@ export default function PostCreate() { } },[]); - const formats = [ - 'header', - 'bold', 'italic', 'underline', 'strike', 'blockquote', - 'align', 'list', 'bullet', 'indent', - 'link', 'image', - 'size' - ] + const formats = [ + 'header', + 'bold', 'italic', 'underline', 'strike', 'blockquote', + 'align', 'list', 'bullet', 'indent', + 'link', 'image', + 'size' + ] @@ -237,7 +252,7 @@ export default function PostCreate() {

      포스트 작성하기

      - imgRef.current.click()} /> + imgRef.current.click()} /> + +
      +
      + -
      - {/* */} - {/* */} + {/* */} + {/* */}
      @@ -284,7 +308,7 @@ export default function PostCreate() {
      - 게시 + 게시
      From a5ef82991bdb4863338e215ebe6f51e1272a2ffd Mon Sep 17 00:00:00 2001 From: dancingKim Date: Fri, 7 Jul 2023 21:59:36 +0900 Subject: [PATCH 26/90] fix: fix imaage url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 이미지 url에 process.env.PUBLIC_URL을 붙임. 2. 임시코드임 --- src/pages/general/create/PostCreate.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/pages/general/create/PostCreate.js b/src/pages/general/create/PostCreate.js index 837dea2..ee2625d 100644 --- a/src/pages/general/create/PostCreate.js +++ b/src/pages/general/create/PostCreate.js @@ -72,10 +72,11 @@ export default function PostCreate() { const [subTitle, setSubTitle] = useState(''); const [thumbnailImageUrl, setThumbnailImageUrl] = useState(null); const [postType, setPostType] = useState(''); - + const [postId, setPostId] = useState(null); const [imgFile, setImgFile] = useState(""); const imgRef = useRef(); const quillRef = useRef(); + const imageData = new FormData(); const { res: postRes, error: postErr, @@ -94,7 +95,7 @@ export default function PostCreate() { error: uploadErr, setError: setUploadErr, postData: upload, - } = useApiPost(`file/uploadFiles`, new FormData()); + } = useApiPost(`file/uploadFiles?postId=${postId}`, new FormData()); const imageHandler = () => { console.log('에디터에서 이미지 버튼을 클릭하면 이 핸들러가 시작됩니다!'); @@ -112,14 +113,12 @@ export default function PostCreate() { console.log('온체인지'); const files = input.files; // multer에 맞는 형식으로 데이터 만들어준다. - const formData = new FormData(); Array.prototype.forEach.call(files, function(file) { formData.append('multipartFiles',file); }); // 백엔드 라우터에 이미지를 보낸다. try { - upload(formData); // const result = await axios.post('http://localhost:4050/img', formData); @@ -235,10 +234,13 @@ export default function PostCreate() { postContent: content, postThumnPath: thumbnailImageUrl, }; - // API 호출 등의 로직을 추가합니다. - // 예: axios.post('/api/posts', postData) + console.log('전송할 데이터:', postData); + post(postData); + + setPostId(postRes.data.data.postId); + upload(imageData); }; const options = [ From c943b8ae1c8a2c2a05b3a5c81b17151ad641d5b6 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Fri, 7 Jul 2023 22:18:27 +0900 Subject: [PATCH 27/90] feat: Added post view page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 포스트 뷰 페이지 만듦 --- src/appRoute.js | 7 ++ .../templates/general/CreateTemplate.js | 24 ---- .../templates/general/ViewTemplate.js | 13 ++ src/pages/general/create/PostCreate.js | 2 +- src/pages/general/view/PostView.js | 117 ++++++++++++++++++ src/postData.json | 37 ++++++ 6 files changed, 175 insertions(+), 25 deletions(-) create mode 100644 src/components/templates/general/ViewTemplate.js create mode 100644 src/pages/general/view/PostView.js create mode 100644 src/postData.json diff --git a/src/appRoute.js b/src/appRoute.js index 5713ad7..034972d 100644 --- a/src/appRoute.js +++ b/src/appRoute.js @@ -25,6 +25,7 @@ import ChannelWebtoon from './pages/general/channel/ChannelWebtoon'; import ChannelWebnovel from './pages/general/channel/ChannelWebnovel'; import ChannelAbout from './pages/general/channel/ChannelAbout'; import PostCreate from './pages/general/create/PostCreate'; +import PostView from './pages/general/view/PostView'; const urls = { login: '/login', @@ -75,6 +76,9 @@ const urls = { // crate postCreate: `/post/create`, + + // view + postView: '/post/view/:postId', }; // Links @@ -175,4 +179,7 @@ export const pageRoutes = [ // create { id: 31, path: urls.postCreate, element: }, + + // view + { id: 32, path: urls.postView, element: }, ]; diff --git a/src/components/templates/general/CreateTemplate.js b/src/components/templates/general/CreateTemplate.js index 555c1b6..4df08b8 100644 --- a/src/components/templates/general/CreateTemplate.js +++ b/src/components/templates/general/CreateTemplate.js @@ -1,32 +1,8 @@ -import NavMenu from '../../molecules/general/NavMenu'; import Nav from '../../organisms/general/Nav'; import Header from '../../organisms/general/Header'; import Main from '../../organisms/general/Main'; -import styled from 'styled-components'; -import { useApiGet } from '../../../hooks/useApi'; -import { useParams } from 'react-router-dom'; -import useUserStore from '../../../stores/useUserStore'; -import { useEffect, useState } from 'react'; export default function CreateTemplate(p) { - // const { chnlUri } = useParams(); - // - // const [url, setUrl] = useState(`/channel/${encodeURIComponent(chnlUri)}`); - // const { data, isLoading, error } = useApiGet( - // url, - // [url] - // ); - // useEffect(() => { - // setUrl(`/channel/${encodeURIComponent(chnlUri)}`); - // }, [chnlUri]); - // - // const { user } = useUserStore(); - // if (isLoading) return; - // if (error) return {`[${error.code}] ${error.message}`}; - // - // if (!data) { - // return null; - // } return ( <> diff --git a/src/components/templates/general/ViewTemplate.js b/src/components/templates/general/ViewTemplate.js new file mode 100644 index 0000000..e29ea96 --- /dev/null +++ b/src/components/templates/general/ViewTemplate.js @@ -0,0 +1,13 @@ +import Nav from '../../organisms/general/Nav'; +import Header from '../../organisms/general/Header'; +import Main from '../../organisms/general/Main'; + +export default function ViewTemplate(p) { + return ( + <> +
      + +
      {p.children}
      + + ); +} diff --git a/src/pages/general/create/PostCreate.js b/src/pages/general/create/PostCreate.js index ee2625d..a83c21e 100644 --- a/src/pages/general/create/PostCreate.js +++ b/src/pages/general/create/PostCreate.js @@ -114,7 +114,7 @@ export default function PostCreate() { const files = input.files; // multer에 맞는 형식으로 데이터 만들어준다. Array.prototype.forEach.call(files, function(file) { - formData.append('multipartFiles',file); + imageData.append('multipartFiles',file); }); // 백엔드 라우터에 이미지를 보낸다. diff --git a/src/pages/general/view/PostView.js b/src/pages/general/view/PostView.js new file mode 100644 index 0000000..cb11699 --- /dev/null +++ b/src/pages/general/view/PostView.js @@ -0,0 +1,117 @@ +import styled from 'styled-components'; +import useUserStore from '../../../stores/useUserStore'; +import { useParams } from 'react-router-dom'; +import { useApiGet } from '../../../hooks/useApi'; +import CreateTemplate from '../../../components/templates/general/CreateTemplate'; +import React, { useState } from 'react'; +import ReactQuill, {Quill} from 'react-quill'; +import 'react-quill/dist/quill.snow.css'; +import ImageResize from 'quill-image-resize'; +import ViewTemplate from '../../../components/templates/general/ViewTemplate'; +// 임시 데이터 +// TODO: writer 값 실제 api data 값 대처 +import data from '../../../postData.json'; +import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; + + +Quill.register('modules/imageResize', ImageResize); +export const SubmitButton = styled.button` + display: block; + width: fit-content; + padding: 8px 30px; + font-size: 15px; + border-radius: 4px; + font-weight: normal; + color: white; + background-color: #3478FF; + border: 1px solid #3478FF; +`; + +const RoundThumnail = styled.div` + width: 40px; + height: 40px; + background-color: #ffffff; + border-radius: 60px; + margin-right: 10px; + display: inline-block; + background-image: url(${props => props.imageUrl}); + background-size: cover; + border: 1px solid rgba(0,0,0,.05); + margin-right: 28px; +`; + +const PostViewHeader = styled.div` + padding: 40px 0 24px 0; + border-bottom: 1px solid rgba(0,0,0,.05); + +` + +const PostViewHeaderBottom = styled.div` + display: flex; + margin-top: 32px; +` + +const PostInfo = styled.div` + color: rgba(0,0,0,.47); + font-size: 14px; +` + +const PostViewBody = styled.div` + +` + +const PostEditButtonContainer = styled.div` + display: flex; + justify-content: end; +` + + +export default function PostView() { + // const { user } = useUserStore(); + const { chnlUri, postId } = useParams(); + // const { data, isLoading, error } = useApiGet( + // `/channel/${encodeURIComponent(chnlUri)}`, + // [chnlUri] + // ); + // + // if (isLoading) return; + // if (error) return {`[${error.code}] ${error.message}`}; + // + // if (!data) { + // return null; + // } + + + const myPostId = encodeURIComponent(postId); + + + + + return ( + +
      + +

      {data.postTtl}

      + +
      + +
      +
      + {data.writer.nic} + + {data.postPblcDtm} / 조회 {data.postInqrCnt} + +
      +
      + + + 포스트 수정하기 + +
      + + + +
      +
      + ); +} diff --git a/src/postData.json b/src/postData.json new file mode 100644 index 0000000..27d4366 --- /dev/null +++ b/src/postData.json @@ -0,0 +1,37 @@ +{ + "postId": 20, + "postTtl": "웹 디자인과 사용자 경험", + "postSbTtl": "웹 디자인과 사용자 경험", + "postInqrCnt": 0, + "postRepCnt": 0, + "postLikCnt": 0, + "spacInclCharCnt": null, + "postPchrgYn": true, + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/10/c9c37e9d5fa6df93437d957f29b4d2aa.jpeg?w=360&h=270&q=65", + "serId": 1, + "opubPlanId": null, + "pchrgBlkPurcPnt": 1000, + "ntceSettYn": true, + "adoYn": true, + "postPblcDtm": "2023-07-03 22:10:33", + "chnlId": 7, + "basicFontCd": "FT00110", + "basicParagAlgnCd": "AL00110", + "itdYn": true, + "paragGapMargYn": true, + "nowPostStusCd": "ST00420", + "nowPostStudChgDtm": "2023-07-03 22:10:40", + "befPostId": 19, + "nextPostId": 21, + "serTtl": "잡설", + "nowPostStusCdNm": "최초발행", + "styleText": null, + "writer": { + "eid": "iamloved5959@gmail.com", + "nic": "genius", + "userIntro": "genius한 아이디입니다.", + "userImgPath": "https://blog.kakaocdn.net/dn/d8FDB8/btquA1JkNEk/3rimTHGPZXiuhMe93Uw751/img.png", + "msgAlowYn": true + }, + "postContent": "

      퍼스널브랜딩으로 하는 인스타그램으로 수익화내보세요♡


      인스타그램 이제 그냥 인증 및 기록용으로는 그만! 

      좀더 체계적이고 자아를 찾고 자신을 브랜딩하며

      수익화까지 내는 인스타 활동은 어떠실까요?

      아직 잠재적인 능력을 찾지 못하시거나 

      방향성, 컨셉을 찾지 못한 분

      세일즈하시거나 자영업하시면서

      자신을 알리고 싶으신 분이라면 함께하실게요. 

      나 자신을 더 빛내며 인스타 퍼스널 브랜딩 성장 프로젝트, 함께 해보시겠어요?

      변화가 전혀 없으시다면 100% 환불해드리겠습니다!

      11일간의 여정에 적극적으로 참여하실분만 꼭 신청해주세요 :)




      이런 분께 추천합니다! \uD83D\uDE4B\uD83C\uDFFB‍



      인스타 초보
      (세일즈인 & 자영업자 & 
      1인기업가라면 필수신청하세요)
      인스타그램으로 
      좋아하고 잘하는일로 
      수익 내고 싶은 분
       
      SNS마케팅으로 
      개인 플랫폼 만들고
      싶은 분
       

      매력적인피드 세팅 
      밎 피드 계정 정리가 
      필요하신 분





      자세한 일차별 커리큘럼 \uD83D\uDCDA


      \uD83C\uDF38매일미션으로 신청자분들과 협의후 강의가진행 , 11일간 미션제공으로 활동이 진행됩니다.

      - 10월17일 OT 챌린지 활동 비법 

      - 요일 투표 후 인스타그램 전략 강의 2회

      (인스타 퍼스널브랜딩 & sns세일즈 관련 & 인스타활용팁)


      - 전략적인 본인을 브랜딩화시키는 11일간 미션제공

      - 모든참여자 개인활동 짧은피드백

      - 인스타그램 수익화내는기반 꿀팁

      - 팔로우 늘리기 / 찐친만들기

      - 개인브랜딩화시키기

      - 개인의자아찾기 (내면의 잠재적인 능력찾기)

      - 체험단 리스트제공(완주하시는분)

      - 공구 수익활동 사후제공(완주하신분)

      - 계정활성화 

      - 비즈니스홍보마케팅

      - 매력있는 피드세팅

      - 피드 상위 노출 활동

      ✅ 인증방식  
      챌토링 인증은 필수로 진행합니다.

      매우 심플하게 활동 커리큘럼 세팅!
      댓글 피로도 최소화!
      매일 미션으로 인증 링크를 공유하며 피드백까지 세팅이 되어있습니다.
      (기존 참여자분들의 만족도가 매우 높았습니다.)






      잠깐! 혹시 Zoom이 있나요? \uD83C\uDFA5

      • 이 챌린지는 Zoom이 있나요?            챌린지 기간 동안 Zoom(줌)을 진행합니다.
      • 몇 번이나 있나요?                              3회  
      • 녹화본은 제공되나요?                         ZOOM(줌) 녹화본 제공합니다.
      •  ZOOM(줌) OT 시작은 언제인가요?   챌린지 신청자분들의 소중한의견을담아투표로진행됩니다. 





      리더 소개 \uD83E\uDDD1\uD83C\uDFFB‍\uD83C\uDFEB


      " 수현쌤"은 누구인가요?


      언제나 잔잔하고 꾸준하고 디테일하고 정성스럽게 활동하고있는 

      인스타 세일즈마케팅강사 수현 멘토 인사드립니다.

      MKYU로인해 폭풍성장한 1인이며 많은 영향력과 역량으로 성장했고 귀한결과를 많이냈습니다



      사랑 받은 만큼 귀한 역량을 이제는 저도 선한 영향력으로 더 나눠야할때가 온 것같고

      같이의 가치를 전하는 mkyu의 메세지 많은분들께 전하고싶습니다.


      또한 많은분들과 소통해보니 자신의 잠재적인 재능을 못찾으셨기에

      이번에 나 사랑하기 인스타그램 퍼스널브랜딩 챌토링 1기와

      기존 함께한분들의 챌린지를 성공리에 마쳐 또다시 정성껏 준비했습니다


      정성을 다해 챌린지를 준비하여 많은분들의 성장에도움을 드릴것을 약속드립니다♡





      경력 및 이력


      * 세일즈경력 17년차, 1월부터 514챌린지 함께한 시조새 디세리쏭블리쌤입니다.

      \uD83C\uDF31mkyu열정대학생 수강후 폭풍성장한 1인이며 개인브랜딩마케팅으로 많은성과를냈습니다.

      - 디지털세일즈리더 SNS세일즈 클래스운영

      - SNS세일즈 및 전략마케팅 실전 쪽집게 과외

      - SNS세일즈마케팅 전국상위매출 1위달성

      - 월매출 4억이상 / 개인순이익 월1천이상 .5천기록

      - 개인비즈니스 전국 상위실적

      - MKYU서울 동남부방장으로활동

      - 뉴스정보커뮤니티. 디세리커뮤니티 활동

      - 61.6만게시물 _기타 해시태그 상위노출다수

      - 개인컨셉 자아찾기 1인브랜딩 성장



      인스타그램에서 #디세리2주챌린지를검색해주세요

      " +} From 95b646b64c4deffd2e778b381b4e08349c6e32ea Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 19 Jul 2023 09:56:36 +0900 Subject: [PATCH 28/90] =?UTF-8?q?=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appRoute.js | 8 +- src/hooks/useApi.js | 25 +++ src/pages/general/create/PostCreate.js | 229 ++++++++++++++++--------- src/postData.json | 37 ---- 4 files changed, 177 insertions(+), 122 deletions(-) delete mode 100644 src/postData.json diff --git a/src/appRoute.js b/src/appRoute.js index 034972d..9ca2103 100644 --- a/src/appRoute.js +++ b/src/appRoute.js @@ -59,7 +59,7 @@ const urls = { setBlacklist: '/account/settings/blacklist', //channel - channelCreate: '/channel/create', + channelCreate: '/:chnlUri/channel/create', channel: '/channel/:chnlUri', channelWebtoon: '/channel/:chnlUri/webtoon', channelWebnovel: '/channel/:chnlUri/webnovel', @@ -74,11 +74,11 @@ const urls = { profilePost: `/profile/:nic/post`, profileSeries: `/profile/:nic/series`, - // crate - postCreate: `/post/create`, + // create + postCreate: `/:chnlUri/post/create`, // view - postView: '/post/view/:postId', + postView: '/:chnlUri/post/:postId', }; // Links diff --git a/src/hooks/useApi.js b/src/hooks/useApi.js index df09f9d..298fe76 100644 --- a/src/hooks/useApi.js +++ b/src/hooks/useApi.js @@ -214,3 +214,28 @@ export function useApiFormPut(url) { return { isUpdating, putData, error, res, setRes, setError }; } +export function useConditionalApiGet(url, postId) { + const [data, setData] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (postId !== null) { + (async () => { + setIsLoading(true); + try { + const res = await api.get(url); + console.log('get 성공', res); + setData(res.data); + } catch (err) { + console.log('get 실패', err); + setError(err); + } finally { + setIsLoading(false); + } + })(); + } + }, [url, postId]); + + return { data, isLoading, error }; +} \ No newline at end of file diff --git a/src/pages/general/create/PostCreate.js b/src/pages/general/create/PostCreate.js index a83c21e..ac770ce 100644 --- a/src/pages/general/create/PostCreate.js +++ b/src/pages/general/create/PostCreate.js @@ -1,13 +1,13 @@ import ChannelTemplate from '../../../components/templates/general/ChannelTemplate'; import styled from 'styled-components'; import useUserStore from '../../../stores/useUserStore'; -import { useParams } from 'react-router-dom'; -import { useApiGet, useApiPost } from '../../../hooks/useApi'; +import { useLocation, useParams } from 'react-router-dom'; +import { useApiGet, useApiPost, useConditionalApiGet } from '../../../hooks/useApi'; import PostItem from '../../../components/organisms/general/PostItem'; import NoContent from '../../../components/molecules/error/NoContent'; import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; import CreateTemplate from '../../../components/templates/general/CreateTemplate'; -import React, { useMemo, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import ReactQuill, {Quill} from 'react-quill'; import 'react-quill/dist/quill.snow.css'; import TextInputSC from '../../../components/atoms/Input/TextInputSC'; @@ -16,6 +16,7 @@ import PostRadioButton from '../../../components/molecules/post/PostRadioButton' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faImage, faUser } from '@fortawesome/free-solid-svg-icons'; import PostImg from '../../../components/atoms/Post/PostImgSC'; +import { useNavigate } from 'react-router'; Quill.register('modules/imageResize', ImageResize); export const SubmitButton = styled.button` @@ -53,30 +54,23 @@ export const ImageContainerSC = styled.div` export default function PostCreate() { - // const { user } = useUserStore(); - // const { chnlUri } = useParams(); - // const { data, isLoading, error } = useApiGet( - // `/channel/${encodeURIComponent(chnlUri)}`, - // [chnlUri] - // ); - // - // if (isLoading) return; - // if (error) return {`[${error.code}] ${error.message}`}; - // - // if (!data) { - // return null; - // } - + const { chnlUri } = useParams(); + const navigate = useNavigate(); + const location = useLocation(); + const queryParams = new URLSearchParams(location.search); const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const [subTitle, setSubTitle] = useState(''); - const [thumbnailImageUrl, setThumbnailImageUrl] = useState(null); + const [thumbnailImageUrl, setThumbnailImageUrl] = useState(''); const [postType, setPostType] = useState(''); const [postId, setPostId] = useState(null); + // const imageUrls = ["정호"]; + const [imageUrls, setImageUrls] = useState([]); const [imgFile, setImgFile] = useState(""); const imgRef = useRef(); const quillRef = useRef(); const imageData = new FormData(); + const thumnData = new FormData(); const { res: postRes, error: postErr, @@ -88,6 +82,20 @@ export default function PostCreate() { postSbTtl: subTitle, postContent: content, postThumnPath: thumbnailImageUrl, + imageUrls: imageUrls, + }); + const { + res: postEditRes, + error: postEditErr, + setError: setPostEditErr, + postData: postEdit, + } = useApiPost(`post/${postId}/edit`, { + postType: postType, + postTtl: title, + postSbTtl: subTitle, + postContent: content, + postThumnPath: thumbnailImageUrl, + imageUrls: imageUrls, }); const { @@ -95,7 +103,17 @@ export default function PostCreate() { error: uploadErr, setError: setUploadErr, postData: upload, - } = useApiPost(`file/uploadFiles?postId=${postId}`, new FormData()); + } = useApiPost(`file/uploadFiles`, {imageData: imageData}); + const { + res: uploadThumRes, + error: uploadThumnErr, + setError: Err, + postData: uploadThumn, + } = useApiPost(`file/uploadThumn`, {thumnData: thumnData}); + + + const { data, isLoading, error } = useConditionalApiGet(`/post/${postId}`, postId); + const imageHandler = () => { console.log('에디터에서 이미지 버튼을 클릭하면 이 핸들러가 시작됩니다!'); @@ -106,8 +124,6 @@ export default function PostCreate() { input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); input.click(); // 에디터 이미지버튼을 클릭하면 이 input이 클릭된다. - // // input이 클릭되면 파일 선택창이 나타난다. - // // // input에 변화가 생긴다면 = 이미지를 선택 input.addEventListener('change', async () => { console.log('온체인지'); @@ -115,42 +131,15 @@ export default function PostCreate() { // multer에 맞는 형식으로 데이터 만들어준다. Array.prototype.forEach.call(files, function(file) { imageData.append('multipartFiles',file); + console.log('multipartFiles', file); }); - // 백엔드 라우터에 이미지를 보낸다. - try { - - // const result = await axios.post('http://localhost:4050/img', formData); - - console.log('성공 시, 백엔드가 보내주는 데이터', uploadRes.data.url); - // // 이 URL을 img 태그의 src에 넣은 요소를 현재 에디터의 커서에 넣어주면 에디터 내에서 이미지가 나타난다 - // // src가 base64가 아닌 짧은 URL이기 때문에 데이터베이스에 에디터의 전체 글 내용을 저장할 수있게된다 - // // 이미지는 꼭 로컬 백엔드 uploads 폴더가 아닌 다른 곳에 저장해 URL로 사용하면된다. - // - // // 이미지 태그를 에디터에 써주기 - 여러 방법이 있다. - const editor = quillRef.current.getEditor(); // 에디터 객체 가져오기 - // // 1. 에디터 root의 innerHTML을 수정해주기 - // // editor의 root는 에디터 컨텐츠들이 담겨있다. 거기에 img태그를 추가해준다. - // // 이미지를 업로드하면 -> 멀터에서 이미지 경로 URL을 받아와 -> 이미지 요소로 만들어 에디터 안에 넣어준다. - uploadRes.data.data.urls.forEach((IMG_URL) => { - editor.root.innerHTML = - editor.root.innerHTML + `
      `; - // 현재 있는 내용들 뒤에 써줘야한다. - // - // // 2. 현재 에디터 커서 위치값을 가져온다 - const range = editor.getSelection(); - editor.insertEmbed(range.index, 'image', IMG_URL); - // // 가져온 위치에 이미지를 삽입한다 - } - ) - } catch (error) { - console.log('실패했어요ㅠ'); - } + await upload(imageData); }); }; // 이미지 업로드 input의 onChange - const saveImgFile = () => { + const saveImgFile = async () => { if(imgRef.current.files.length > 0) { const file = imgRef.current.files[0]; const reader = new FileReader(); @@ -158,24 +147,17 @@ export default function PostCreate() { reader.onloadend = () => { setImgFile(reader.result); console.log(imgFile); + }; + console.log("file",file); + thumnData.append("file", file); + console.log("thumnData", thumnData); + await uploadThumn(thumnData); } else { console.warn('No file selected'); } }; - // 업로드 된 이미지 미리보기 - // const modules = { - // toolbar: [ - // ['bold', 'italic', 'underline', 'strike'], // 텍스트 스타일 - // [{ list: 'ordered' }, { list: 'bullet' }], // 리스트 - // [{ indent: '-1' }, { indent: '+1' }], // 들여쓰기 - // [{ align: [] }], // 정렬 - // ['link', 'image'], // 링크, 이미지 - // ['clean'], // 포맷 지우기 - // ], - // }; - const modules = useMemo(() => { return { toolbar: { @@ -203,6 +185,58 @@ export default function PostCreate() { 'link', 'image', 'size' ] + useEffect(() =>{ + console.log(queryParams.get('postId')) + setPostId(queryParams.get('postId')); + console.log(postId); + }, []) + + + useEffect(() =>{ + if (uploadThumRes !== null){ + setThumbnailImageUrl(process.env.REACT_APP_PUBLIC_URL+uploadThumRes.data.url); + } + }, [uploadThumRes]) + + useEffect(() => { + if (uploadRes !== null) { + console.dir(uploadRes); + // 그리고 다른 작업들... + console.log(process.env.REACT_APP_PUBLIC_URL) + + const editor = quillRef.current.getEditor(); // 에디터 객체 가져오기 + // // 1. 에디터 root의 innerHTML을 수정해주기 + // // editor의 root는 에디터 컨텐츠들이 담겨있다. 거기에 img태그를 추가해준다. + // // 이미지를 업로드하면 -> 멀터에서 이미지 경로 URL을 받아와 -> 이미지 요소로 만들어 에디터 안에 넣어준다. + uploadRes.data.urls.forEach((imgUrl) => { + // editor.root.innerHTML = + // editor.root.innerHTML + `
      `; + // 현재 있는 내용들 뒤에 써줘야한다. + + // 2. 현재 에디터 커서 위치값을 가져온다. + console.log("editor"); + console.dir(editor); + + let range = editor.getSelection(); + editor.insertEmbed(range.index, 'image', process.env.REACT_APP_PUBLIC_URL+imgUrl); + // // 가져온 위치에 이미지를 삽입한다 + // imageUrls.push(process.env.REACT_APP_PUBLIC_URL+imgUrl); + setImageUrls((prevImageUrls) => [...prevImageUrls, process.env.REACT_APP_PUBLIC_URL+imgUrl]); + + console.dir(imageUrls); + + } + ) + + } + }, [uploadRes]); + + useEffect(() =>{ + if(postRes != null){ + console.dir(postRes); + } + }) @@ -223,24 +257,54 @@ export default function PostCreate() { setContent(value); }; - const handleFormSubmit = (event) => { + const handleFormSubmit = async (event) => { event.preventDefault(); + + const editor = quillRef.current.getEditor(); // 에디터 객체 가져오기 + console.log("quill", editor); + const delta = editor.getContents(); // 현재 문서의 내용을 가져오기 + console.log("delta", delta); + setContent(JSON.stringify(delta)); // Quill 에디터의 내용을 Delta 형식의 JSON 문자열로 설정 + console.log("postContent", content); + + + // ops 배열을 순회하며 이미지 연산자를 찾습니다. + const imagePathes = delta.ops + .filter(op => typeof op.insert === 'object' && op.insert.image) + .map(op => op.insert.image); + + // 이미지 URL 출력 + console.log(imagePathes); // 선택한 postType 값과 기타 필요한 데이터를 API로 전송합니다. + console.dir(imagePathes); + setImageUrls(imagePathes); + console.log("submitImage", imageUrls); const postData = { + chnlUri: chnlUri, postType: postType, postTtl: title, postSbTtl: subTitle, postContent: content, postThumnPath: thumbnailImageUrl, + imageUrls: imageUrls }; // API 호출 등의 로직을 추가합니다. console.log('전송할 데이터:', postData); - post(postData); + if (postId !== null){ + // 포스트 수정을 위한 postApi + await postEdit(postData); + + } else { + // 포스트 생성을 위한 postApi + await post(postData); + } + + navigate(`/channel/${chnlUri}`); + + - setPostId(postRes.data.data.postId); - upload(imageData); }; const options = [ @@ -248,6 +312,20 @@ export default function PostCreate() { { label: '웹소설', value: '웹소설' }, ]; + useEffect(() => { + if (data && data.post) { + setTitle(data.post.postTtl || ''); + setContent(data.post.postContent || ''); + setSubTitle(data.post.postSbTtl || ''); + setThumbnailImageUrl(data.post.postThumnPath || ''); + setPostType(data.post.postType || ''); + setImageUrls([...data.post.imageUrls]) + } + }, [data]); + + if (isLoading) return; + if (error) return {`[${error.code}] ${error.message}`}; + return (
      @@ -292,25 +370,14 @@ export default function PostCreate() { onKeyDown={handleSubTitleChange} /> -
      - - {/* */} - {/* */} -
      + {/*
      */} + {/*
      */}
      -
      - 게시 + {postId !==null ? "수정" : "발행"}
      diff --git a/src/postData.json b/src/postData.json deleted file mode 100644 index 27d4366..0000000 --- a/src/postData.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "postId": 20, - "postTtl": "웹 디자인과 사용자 경험", - "postSbTtl": "웹 디자인과 사용자 경험", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": null, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/10/c9c37e9d5fa6df93437d957f29b4d2aa.jpeg?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-07-03 22:10:33", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-03 22:10:40", - "befPostId": 19, - "nextPostId": 21, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "writer": { - "eid": "iamloved5959@gmail.com", - "nic": "genius", - "userIntro": "genius한 아이디입니다.", - "userImgPath": "https://blog.kakaocdn.net/dn/d8FDB8/btquA1JkNEk/3rimTHGPZXiuhMe93Uw751/img.png", - "msgAlowYn": true - }, - "postContent": "

      퍼스널브랜딩으로 하는 인스타그램으로 수익화내보세요♡


      인스타그램 이제 그냥 인증 및 기록용으로는 그만! 

      좀더 체계적이고 자아를 찾고 자신을 브랜딩하며

      수익화까지 내는 인스타 활동은 어떠실까요?

      아직 잠재적인 능력을 찾지 못하시거나 

      방향성, 컨셉을 찾지 못한 분

      세일즈하시거나 자영업하시면서

      자신을 알리고 싶으신 분이라면 함께하실게요. 

      나 자신을 더 빛내며 인스타 퍼스널 브랜딩 성장 프로젝트, 함께 해보시겠어요?

      변화가 전혀 없으시다면 100% 환불해드리겠습니다!

      11일간의 여정에 적극적으로 참여하실분만 꼭 신청해주세요 :)




      이런 분께 추천합니다! \uD83D\uDE4B\uD83C\uDFFB‍



      인스타 초보
      (세일즈인 & 자영업자 & 
      1인기업가라면 필수신청하세요)
      인스타그램으로 
      좋아하고 잘하는일로 
      수익 내고 싶은 분
       
      SNS마케팅으로 
      개인 플랫폼 만들고
      싶은 분
       

      매력적인피드 세팅 
      밎 피드 계정 정리가 
      필요하신 분





      자세한 일차별 커리큘럼 \uD83D\uDCDA


      \uD83C\uDF38매일미션으로 신청자분들과 협의후 강의가진행 , 11일간 미션제공으로 활동이 진행됩니다.

      - 10월17일 OT 챌린지 활동 비법 

      - 요일 투표 후 인스타그램 전략 강의 2회

      (인스타 퍼스널브랜딩 & sns세일즈 관련 & 인스타활용팁)


      - 전략적인 본인을 브랜딩화시키는 11일간 미션제공

      - 모든참여자 개인활동 짧은피드백

      - 인스타그램 수익화내는기반 꿀팁

      - 팔로우 늘리기 / 찐친만들기

      - 개인브랜딩화시키기

      - 개인의자아찾기 (내면의 잠재적인 능력찾기)

      - 체험단 리스트제공(완주하시는분)

      - 공구 수익활동 사후제공(완주하신분)

      - 계정활성화 

      - 비즈니스홍보마케팅

      - 매력있는 피드세팅

      - 피드 상위 노출 활동

      ✅ 인증방식  
      챌토링 인증은 필수로 진행합니다.

      매우 심플하게 활동 커리큘럼 세팅!
      댓글 피로도 최소화!
      매일 미션으로 인증 링크를 공유하며 피드백까지 세팅이 되어있습니다.
      (기존 참여자분들의 만족도가 매우 높았습니다.)






      잠깐! 혹시 Zoom이 있나요? \uD83C\uDFA5

      • 이 챌린지는 Zoom이 있나요?            챌린지 기간 동안 Zoom(줌)을 진행합니다.
      • 몇 번이나 있나요?                              3회  
      • 녹화본은 제공되나요?                         ZOOM(줌) 녹화본 제공합니다.
      •  ZOOM(줌) OT 시작은 언제인가요?   챌린지 신청자분들의 소중한의견을담아투표로진행됩니다. 





      리더 소개 \uD83E\uDDD1\uD83C\uDFFB‍\uD83C\uDFEB


      " 수현쌤"은 누구인가요?


      언제나 잔잔하고 꾸준하고 디테일하고 정성스럽게 활동하고있는 

      인스타 세일즈마케팅강사 수현 멘토 인사드립니다.

      MKYU로인해 폭풍성장한 1인이며 많은 영향력과 역량으로 성장했고 귀한결과를 많이냈습니다



      사랑 받은 만큼 귀한 역량을 이제는 저도 선한 영향력으로 더 나눠야할때가 온 것같고

      같이의 가치를 전하는 mkyu의 메세지 많은분들께 전하고싶습니다.


      또한 많은분들과 소통해보니 자신의 잠재적인 재능을 못찾으셨기에

      이번에 나 사랑하기 인스타그램 퍼스널브랜딩 챌토링 1기와

      기존 함께한분들의 챌린지를 성공리에 마쳐 또다시 정성껏 준비했습니다


      정성을 다해 챌린지를 준비하여 많은분들의 성장에도움을 드릴것을 약속드립니다♡





      경력 및 이력


      * 세일즈경력 17년차, 1월부터 514챌린지 함께한 시조새 디세리쏭블리쌤입니다.

      \uD83C\uDF31mkyu열정대학생 수강후 폭풍성장한 1인이며 개인브랜딩마케팅으로 많은성과를냈습니다.

      - 디지털세일즈리더 SNS세일즈 클래스운영

      - SNS세일즈 및 전략마케팅 실전 쪽집게 과외

      - SNS세일즈마케팅 전국상위매출 1위달성

      - 월매출 4억이상 / 개인순이익 월1천이상 .5천기록

      - 개인비즈니스 전국 상위실적

      - MKYU서울 동남부방장으로활동

      - 뉴스정보커뮤니티. 디세리커뮤니티 활동

      - 61.6만게시물 _기타 해시태그 상위노출다수

      - 개인컨셉 자아찾기 1인브랜딩 성장



      인스타그램에서 #디세리2주챌린지를검색해주세요

      " -} From 39a58994fbbd94f3e014050278139e8a2aa38bcb Mon Sep 17 00:00:00 2001 From: dancingKim Date: Mon, 10 Jul 2023 01:22:01 +0900 Subject: [PATCH 29/90] feat: Added black subscribe button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 검은 구독 버튼 추가 --- .../atoms/Button/ConfirmBlackBtnSC.js | 19 ++++ src/components/atoms/Channel/SubsBlackBtn.js | 11 +++ .../molecules/channel/SubscribeBlackBtn.js | 89 +++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/components/atoms/Button/ConfirmBlackBtnSC.js create mode 100644 src/components/atoms/Channel/SubsBlackBtn.js create mode 100644 src/components/molecules/channel/SubscribeBlackBtn.js diff --git a/src/components/atoms/Button/ConfirmBlackBtnSC.js b/src/components/atoms/Button/ConfirmBlackBtnSC.js new file mode 100644 index 0000000..92ef410 --- /dev/null +++ b/src/components/atoms/Button/ConfirmBlackBtnSC.js @@ -0,0 +1,19 @@ +import styled from 'styled-components'; +import { btnStyle } from '../../styled/utils'; + +const ConfirmBlackBtnSC = styled.button` + ${btnStyle}; + + color: white; + background-color: ${(p) => p.theme.color.blackA80}; + border: 1px solid ${(p) => p.theme.color.blackA80}; + opacity: ${(p) => (p.disabled ? 0.3 : 1)}; + + &:hover { + color: white; + background-color: ${(p) => p.theme.color.blackA30}; + border-color: ${(p) => p.theme.color.blackA30}; + } +`; + +export default ConfirmBlackBtnSC; diff --git a/src/components/atoms/Channel/SubsBlackBtn.js b/src/components/atoms/Channel/SubsBlackBtn.js new file mode 100644 index 0000000..1d21d3f --- /dev/null +++ b/src/components/atoms/Channel/SubsBlackBtn.js @@ -0,0 +1,11 @@ +import styled from 'styled-components'; +import ConfirmBtnSC from '../Button/ConfirmBtnSC'; +import ConfirmBlackBtnSC from '../Button/ConfirmBlackBtnSC'; + +const SubsBtnSC = styled(ConfirmBlackBtnSC)` + padding: 4px 8px; +`; + +export default function SubsBlackBtn(props) { + return + 구독; +} diff --git a/src/components/molecules/channel/SubscribeBlackBtn.js b/src/components/molecules/channel/SubscribeBlackBtn.js new file mode 100644 index 0000000..27eb771 --- /dev/null +++ b/src/components/molecules/channel/SubscribeBlackBtn.js @@ -0,0 +1,89 @@ +import { useState } from 'react'; +import SubsBtn from '../../atoms/Channel/SubsBtn'; +import UnsubsBtn from '../../atoms/Channel/UnsubsBtn'; +import { useApiPost, useApiDelete } from '../../../hooks/useApi'; +import useModal from '../modal/useModal'; +import ConfirmBox from '../modal/ConfirmBox'; +import { useEffect } from 'react'; +import useChannelList from '../../../stores/useChannelList'; +import SubsBlackBtn from '../../atoms/Channel/SubsBlackBtn'; + +export default function SubscribeBlackBtn({ isSubsed, chnlId }) { + const [isSubscribed, setIsSubscribed] = useState(isSubsed); + const { isOpen, openModal, closeModal } = useModal(); + const { + res: subsRes, + error: subsErr, + setError: setSubsErr, + postData: subscribe, + } = useApiPost(`/subscriptions`, { chnlId: chnlId }); + const { + res: unsubsRes, + error: unsubsErr, + setError: setUnsubsErr, + deleteData: unsubscribe, + } = useApiDelete(`/subscriptions/cancle?chnlId=${chnlId}`); + const { removeChannel } = useChannelList(); + const currentPath = window.location.pathname; + + useEffect(() => { + if (subsRes) { + setIsSubscribed(true); + } + }, [subsRes]); + + useEffect(() => { + if (unsubsRes) { + if (!currentPath.includes('/profile')) { + removeChannel(chnlId); + } + setIsSubscribed(false); + } + }, [unsubsRes, currentPath]); + + const handleSubscribe = () => { + console.log('구독 실행'); + + subscribe({ + chnlId: chnlId, + }); + }; + + const handleUnsubscribe = () => { + openModal(); + }; + + // 'Confirm' 버튼이 클릭되었을 때 호출될 콜백 함수 + const handleConfirm = () => { + console.log('구독 취소 실행'); + closeModal(); + unsubscribe(); + }; + + // 'Cancel' 버튼이 클릭되었을 때 호출될 콜백 함수 + const handleCancel = () => { + console.log('구독 취소 취소'); + closeModal(); + }; + + return ( +
      + {isSubscribed ? ( + <> + 구독 중 + {isOpen && ( + + )} + + ) : ( + + 구독123 + )} +
      + ); +} From 681bd958f352270bcba8071fd56a60bebbd0b005 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Mon, 10 Jul 2023 01:24:58 +0900 Subject: [PATCH 30/90] feat: Added post view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. post를 보는 기능 추가 --- src/pages/general/view/PostView.js | 57 ++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/src/pages/general/view/PostView.js b/src/pages/general/view/PostView.js index cb11699..7974f2d 100644 --- a/src/pages/general/view/PostView.js +++ b/src/pages/general/view/PostView.js @@ -10,8 +10,8 @@ import ImageResize from 'quill-image-resize'; import ViewTemplate from '../../../components/templates/general/ViewTemplate'; // 임시 데이터 // TODO: writer 값 실제 api data 값 대처 -import data from '../../../postData.json'; import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; +import { useNavigate } from 'react-router'; Quill.register('modules/imageResize', ImageResize); @@ -65,50 +65,69 @@ const PostEditButtonContainer = styled.div` justify-content: end; ` +const ChannelInfo = styled.div` + font-size: 40px; + font-weight: 600; +` + +const PostSbTtl = styled.div` + color: dimgray; + font-size: 18px; +` + export default function PostView() { // const { user } = useUserStore(); const { chnlUri, postId } = useParams(); - // const { data, isLoading, error } = useApiGet( - // `/channel/${encodeURIComponent(chnlUri)}`, - // [chnlUri] - // ); - // - // if (isLoading) return; - // if (error) return {`[${error.code}] ${error.message}`}; - // - // if (!data) { - // return null; - // } - + const { data, isLoading, error } = useApiGet( + `/post/${postId}`, + [chnlUri, postId] + ); + const navigate = useNavigate(); - const myPostId = encodeURIComponent(postId); + if (isLoading) return; + if (error) return {`[${error.code}] ${error.message}`}; + if (!data) { + return null; + } + const goProfile = (nic) =>{ + navigate(`/profile/${nic}`); + } + const goChannel = (chnlUri) => { + navigate(`/channel/${chnlUri}`); + } return ( + goChannel(data.channel.chnlUri)}> + {data.channel.chnlTtl} 채널 +
      -

      {data.postTtl}

      +

      {data.post.postTtl}

      + + {data.post.postSbTtl} +
      - + goProfile(data.writer.nic)}>
      {data.writer.nic} - {data.postPblcDtm} / 조회 {data.postInqrCnt} + {data.post.postPblcDtm} / 조회 {data.post.postInqrCnt}
      - 포스트 수정하기 + 수정하기
      - +
      From 9f528e61304b803a8030ae8f6c008f267905cd69 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Mon, 10 Jul 2023 01:26:38 +0900 Subject: [PATCH 31/90] feat: Added link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 컴포넌트를 누를 시 다른 페이지로 이동하는 기능 추가 --- .../templates/general/ChannelTemplate.js | 17 +++++++++++---- src/pages/general/channel/ChannelAbout.js | 21 +++++++------------ src/pages/general/channel/ChannelHome.js | 11 ++++++++-- src/pages/general/channel/ChannelWebnovel.js | 2 +- src/pages/general/channel/ChannelWebtoon.js | 10 ++++++++- 5 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/components/templates/general/ChannelTemplate.js b/src/components/templates/general/ChannelTemplate.js index dcee3eb..66b831b 100644 --- a/src/components/templates/general/ChannelTemplate.js +++ b/src/components/templates/general/ChannelTemplate.js @@ -8,6 +8,8 @@ import { useParams } from 'react-router-dom'; import useUserStore from '../../../stores/useUserStore'; import { useEffect, useState } from 'react'; import BtnLinkSC from '../../atoms/Link/BtnLinkSC'; +import SubscribeBtn from '../../molecules/channel/SubscribeBtn'; +import SubscribeBlackBtn from '../../molecules/channel/SubscribeBlackBtn'; const Thumbnail = styled.div` width: 80px; @@ -45,6 +47,8 @@ export default function ChannelTemplate(p) { }, [chnlUri]); const { user } = useUserStore(); + const {data: subsData, isLoading: isSubsLoading, error: subsError} = useApiGet(`/channel/${encodeURIComponent(chnlUri)}/subs/${encodeURIComponent(user.nic)}`,[chnlUri]) + if (isLoading) return; if (error) return {`[${error.code}] ${error.message}`}; @@ -52,6 +56,9 @@ export default function ChannelTemplate(p) { return null; } + const isOwner = user.nic === data.data.channelUser.nic; + + const mainNavLinks = [ { to: `/channel/${encodeURIComponent(chnlUri)}`, @@ -75,7 +82,6 @@ export default function ChannelTemplate(p) { return ( <>
      -
      @@ -85,9 +91,12 @@ export default function ChannelTemplate(p) { {data.data.channel.chnlTtl}
      - {/*// TODO: 구독하기 버튼 처리*/} - 구독하기 - 포스트 발행하기 + {isOwner ? + 포스트 발행하기: + // 구독하기 + + } +
      From 4298b3a3b17a6b4e82ff66dbaa52de107ae38aa2 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Mon, 10 Jul 2023 15:33:54 +0900 Subject: [PATCH 76/90] =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/general/create/PostCreate.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/general/create/PostCreate.js b/src/pages/general/create/PostCreate.js index 5e180bf..58d6ebc 100644 --- a/src/pages/general/create/PostCreate.js +++ b/src/pages/general/create/PostCreate.js @@ -58,6 +58,7 @@ export default function PostCreate() { const [postType, setPostType] = useState(''); const [postId, setPostId] = useState(null); const [imageUrls, setImageUrls] = useState([]); + const [imgFile, setImgFile] = useState(""); const imgRef = useRef(); const quillRef = useRef(); @@ -91,6 +92,7 @@ export default function PostCreate() { imageUrls: imageUrls, }); + const { res: uploadRes, error: uploadErr, From bdc2260a496a79ed52ad4fc498b6ef70a71fd5a4 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 19 Jul 2023 18:18:26 +0900 Subject: [PATCH 77/90] =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appRoute.js | 1 + src/pages/general/create/PostCreate.js | 1 - src/pages/general/view/PostView.js | 2 ++ src/postData.json | 37 ++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/postData.json diff --git a/src/appRoute.js b/src/appRoute.js index d4e0f04..9dae9f6 100644 --- a/src/appRoute.js +++ b/src/appRoute.js @@ -80,6 +80,7 @@ const urls = { // view postView: '/:chnlUri/post/:postId', + }; // Links diff --git a/src/pages/general/create/PostCreate.js b/src/pages/general/create/PostCreate.js index 58d6ebc..308d117 100644 --- a/src/pages/general/create/PostCreate.js +++ b/src/pages/general/create/PostCreate.js @@ -118,7 +118,6 @@ export default function PostCreate() { // multer에 맞는 형식으로 데이터 만들어준다. Array.prototype.forEach.call(files, function(file) { imageData.append('multipartFiles',file); - console.log('multipartFiles', file); }); await upload(imageData); diff --git a/src/pages/general/view/PostView.js b/src/pages/general/view/PostView.js index 6a7b32c..86adc32 100644 --- a/src/pages/general/view/PostView.js +++ b/src/pages/general/view/PostView.js @@ -117,11 +117,13 @@ export default function PostView() {
      goProfile(data.writer.nic)}> +
      {data.writer.nic} {data.post.postPblcDtm} / 조회 {data.post.postInqrCnt} +
      diff --git a/src/postData.json b/src/postData.json new file mode 100644 index 0000000..27d4366 --- /dev/null +++ b/src/postData.json @@ -0,0 +1,37 @@ +{ + "postId": 20, + "postTtl": "웹 디자인과 사용자 경험", + "postSbTtl": "웹 디자인과 사용자 경험", + "postInqrCnt": 0, + "postRepCnt": 0, + "postLikCnt": 0, + "spacInclCharCnt": null, + "postPchrgYn": true, + "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/10/c9c37e9d5fa6df93437d957f29b4d2aa.jpeg?w=360&h=270&q=65", + "serId": 1, + "opubPlanId": null, + "pchrgBlkPurcPnt": 1000, + "ntceSettYn": true, + "adoYn": true, + "postPblcDtm": "2023-07-03 22:10:33", + "chnlId": 7, + "basicFontCd": "FT00110", + "basicParagAlgnCd": "AL00110", + "itdYn": true, + "paragGapMargYn": true, + "nowPostStusCd": "ST00420", + "nowPostStudChgDtm": "2023-07-03 22:10:40", + "befPostId": 19, + "nextPostId": 21, + "serTtl": "잡설", + "nowPostStusCdNm": "최초발행", + "styleText": null, + "writer": { + "eid": "iamloved5959@gmail.com", + "nic": "genius", + "userIntro": "genius한 아이디입니다.", + "userImgPath": "https://blog.kakaocdn.net/dn/d8FDB8/btquA1JkNEk/3rimTHGPZXiuhMe93Uw751/img.png", + "msgAlowYn": true + }, + "postContent": "

      퍼스널브랜딩으로 하는 인스타그램으로 수익화내보세요♡


      인스타그램 이제 그냥 인증 및 기록용으로는 그만! 

      좀더 체계적이고 자아를 찾고 자신을 브랜딩하며

      수익화까지 내는 인스타 활동은 어떠실까요?

      아직 잠재적인 능력을 찾지 못하시거나 

      방향성, 컨셉을 찾지 못한 분

      세일즈하시거나 자영업하시면서

      자신을 알리고 싶으신 분이라면 함께하실게요. 

      나 자신을 더 빛내며 인스타 퍼스널 브랜딩 성장 프로젝트, 함께 해보시겠어요?

      변화가 전혀 없으시다면 100% 환불해드리겠습니다!

      11일간의 여정에 적극적으로 참여하실분만 꼭 신청해주세요 :)




      이런 분께 추천합니다! \uD83D\uDE4B\uD83C\uDFFB‍



      인스타 초보
      (세일즈인 & 자영업자 & 
      1인기업가라면 필수신청하세요)
      인스타그램으로 
      좋아하고 잘하는일로 
      수익 내고 싶은 분
       
      SNS마케팅으로 
      개인 플랫폼 만들고
      싶은 분
       

      매력적인피드 세팅 
      밎 피드 계정 정리가 
      필요하신 분





      자세한 일차별 커리큘럼 \uD83D\uDCDA


      \uD83C\uDF38매일미션으로 신청자분들과 협의후 강의가진행 , 11일간 미션제공으로 활동이 진행됩니다.

      - 10월17일 OT 챌린지 활동 비법 

      - 요일 투표 후 인스타그램 전략 강의 2회

      (인스타 퍼스널브랜딩 & sns세일즈 관련 & 인스타활용팁)


      - 전략적인 본인을 브랜딩화시키는 11일간 미션제공

      - 모든참여자 개인활동 짧은피드백

      - 인스타그램 수익화내는기반 꿀팁

      - 팔로우 늘리기 / 찐친만들기

      - 개인브랜딩화시키기

      - 개인의자아찾기 (내면의 잠재적인 능력찾기)

      - 체험단 리스트제공(완주하시는분)

      - 공구 수익활동 사후제공(완주하신분)

      - 계정활성화 

      - 비즈니스홍보마케팅

      - 매력있는 피드세팅

      - 피드 상위 노출 활동

      ✅ 인증방식  
      챌토링 인증은 필수로 진행합니다.

      매우 심플하게 활동 커리큘럼 세팅!
      댓글 피로도 최소화!
      매일 미션으로 인증 링크를 공유하며 피드백까지 세팅이 되어있습니다.
      (기존 참여자분들의 만족도가 매우 높았습니다.)






      잠깐! 혹시 Zoom이 있나요? \uD83C\uDFA5

      • 이 챌린지는 Zoom이 있나요?            챌린지 기간 동안 Zoom(줌)을 진행합니다.
      • 몇 번이나 있나요?                              3회  
      • 녹화본은 제공되나요?                         ZOOM(줌) 녹화본 제공합니다.
      •  ZOOM(줌) OT 시작은 언제인가요?   챌린지 신청자분들의 소중한의견을담아투표로진행됩니다. 





      리더 소개 \uD83E\uDDD1\uD83C\uDFFB‍\uD83C\uDFEB


      " 수현쌤"은 누구인가요?


      언제나 잔잔하고 꾸준하고 디테일하고 정성스럽게 활동하고있는 

      인스타 세일즈마케팅강사 수현 멘토 인사드립니다.

      MKYU로인해 폭풍성장한 1인이며 많은 영향력과 역량으로 성장했고 귀한결과를 많이냈습니다



      사랑 받은 만큼 귀한 역량을 이제는 저도 선한 영향력으로 더 나눠야할때가 온 것같고

      같이의 가치를 전하는 mkyu의 메세지 많은분들께 전하고싶습니다.


      또한 많은분들과 소통해보니 자신의 잠재적인 재능을 못찾으셨기에

      이번에 나 사랑하기 인스타그램 퍼스널브랜딩 챌토링 1기와

      기존 함께한분들의 챌린지를 성공리에 마쳐 또다시 정성껏 준비했습니다


      정성을 다해 챌린지를 준비하여 많은분들의 성장에도움을 드릴것을 약속드립니다♡





      경력 및 이력


      * 세일즈경력 17년차, 1월부터 514챌린지 함께한 시조새 디세리쏭블리쌤입니다.

      \uD83C\uDF31mkyu열정대학생 수강후 폭풍성장한 1인이며 개인브랜딩마케팅으로 많은성과를냈습니다.

      - 디지털세일즈리더 SNS세일즈 클래스운영

      - SNS세일즈 및 전략마케팅 실전 쪽집게 과외

      - SNS세일즈마케팅 전국상위매출 1위달성

      - 월매출 4억이상 / 개인순이익 월1천이상 .5천기록

      - 개인비즈니스 전국 상위실적

      - MKYU서울 동남부방장으로활동

      - 뉴스정보커뮤니티. 디세리커뮤니티 활동

      - 61.6만게시물 _기타 해시태그 상위노출다수

      - 개인컨셉 자아찾기 1인브랜딩 성장



      인스타그램에서 #디세리2주챌린지를검색해주세요

      " +} From dea6a9423babd9aae707bb128ed78e80846a55a0 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 19 Jul 2023 18:19:22 +0900 Subject: [PATCH 78/90] =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/general/create/PostCreate.js | 22 ++++++++++----- src/postData.json | 37 -------------------------- 2 files changed, 15 insertions(+), 44 deletions(-) delete mode 100644 src/postData.json diff --git a/src/pages/general/create/PostCreate.js b/src/pages/general/create/PostCreate.js index 308d117..6663c73 100644 --- a/src/pages/general/create/PostCreate.js +++ b/src/pages/general/create/PostCreate.js @@ -8,7 +8,6 @@ import 'react-quill/dist/quill.snow.css'; import TextInputSC from '../../../components/atoms/Input/TextInputSC'; import ImageResize from 'quill-image-resize'; import PostRadioButton from '../../../components/molecules/post/PostRadioButton'; - import PostImg from '../../../components/atoms/Post/PostImgSC'; import { useNavigate } from 'react-router'; @@ -58,7 +57,6 @@ export default function PostCreate() { const [postType, setPostType] = useState(''); const [postId, setPostId] = useState(null); const [imageUrls, setImageUrls] = useState([]); - const [imgFile, setImgFile] = useState(""); const imgRef = useRef(); const quillRef = useRef(); @@ -78,6 +76,19 @@ export default function PostCreate() { imageUrls: imageUrls, }); + const { + res: postRes, + error: postErr, + setError: setPostErr, + postData: post, + } = useApiPost(`post/create`, { + postType: postType, + postTtl: title, + postSbTtl: subTitle, + postContent: content, + postThumnPath: thumbnailImageUrl, + imageUrls: imageUrls, + }); const { res: postEditRes, error: postEditErr, @@ -92,7 +103,6 @@ export default function PostCreate() { imageUrls: imageUrls, }); - const { res: uploadRes, error: uploadErr, @@ -106,7 +116,6 @@ export default function PostCreate() { postData: uploadThumn, } = useApiPost(`file/uploadThumn`, {thumnData: thumnData}); const { data, isLoading, error } = useConditionalApiGet(`/post/${postId}`, postId); - const imageHandler = () => { const input = document.createElement('input'); @@ -118,6 +127,7 @@ export default function PostCreate() { // multer에 맞는 형식으로 데이터 만들어준다. Array.prototype.forEach.call(files, function(file) { imageData.append('multipartFiles',file); + console.log('multipartFiles', file); }); await upload(imageData); @@ -127,7 +137,6 @@ export default function PostCreate() { // 이미지 업로드 input의 onChange const saveImgFile = async () => { - if(imgRef.current.files.length > 0) { const file = imgRef.current.files[0]; const reader = new FileReader(); @@ -135,6 +144,7 @@ export default function PostCreate() { reader.onloadend = () => { setImgFile(reader.result); console.log(imgFile); + }; console.log("file",file); thumnData.append("file", file); @@ -145,7 +155,6 @@ export default function PostCreate() { } }; - const modules = useMemo(() => { return { @@ -180,7 +189,6 @@ export default function PostCreate() { }, []) - useEffect(() =>{ if (uploadThumRes !== null){ // setThumbnailImageUrl(process.env.REACT_APP_PUBLIC_URL+uploadThumRes.data.url); diff --git a/src/postData.json b/src/postData.json deleted file mode 100644 index 27d4366..0000000 --- a/src/postData.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "postId": 20, - "postTtl": "웹 디자인과 사용자 경험", - "postSbTtl": "웹 디자인과 사용자 경험", - "postInqrCnt": 0, - "postRepCnt": 0, - "postLikCnt": 0, - "spacInclCharCnt": null, - "postPchrgYn": true, - "postThumnPath": "https://d3mcojo3jv0dbr.cloudfront.net/2023/04/24/14/10/c9c37e9d5fa6df93437d957f29b4d2aa.jpeg?w=360&h=270&q=65", - "serId": 1, - "opubPlanId": null, - "pchrgBlkPurcPnt": 1000, - "ntceSettYn": true, - "adoYn": true, - "postPblcDtm": "2023-07-03 22:10:33", - "chnlId": 7, - "basicFontCd": "FT00110", - "basicParagAlgnCd": "AL00110", - "itdYn": true, - "paragGapMargYn": true, - "nowPostStusCd": "ST00420", - "nowPostStudChgDtm": "2023-07-03 22:10:40", - "befPostId": 19, - "nextPostId": 21, - "serTtl": "잡설", - "nowPostStusCdNm": "최초발행", - "styleText": null, - "writer": { - "eid": "iamloved5959@gmail.com", - "nic": "genius", - "userIntro": "genius한 아이디입니다.", - "userImgPath": "https://blog.kakaocdn.net/dn/d8FDB8/btquA1JkNEk/3rimTHGPZXiuhMe93Uw751/img.png", - "msgAlowYn": true - }, - "postContent": "

      퍼스널브랜딩으로 하는 인스타그램으로 수익화내보세요♡


      인스타그램 이제 그냥 인증 및 기록용으로는 그만! 

      좀더 체계적이고 자아를 찾고 자신을 브랜딩하며

      수익화까지 내는 인스타 활동은 어떠실까요?

      아직 잠재적인 능력을 찾지 못하시거나 

      방향성, 컨셉을 찾지 못한 분

      세일즈하시거나 자영업하시면서

      자신을 알리고 싶으신 분이라면 함께하실게요. 

      나 자신을 더 빛내며 인스타 퍼스널 브랜딩 성장 프로젝트, 함께 해보시겠어요?

      변화가 전혀 없으시다면 100% 환불해드리겠습니다!

      11일간의 여정에 적극적으로 참여하실분만 꼭 신청해주세요 :)




      이런 분께 추천합니다! \uD83D\uDE4B\uD83C\uDFFB‍



      인스타 초보
      (세일즈인 & 자영업자 & 
      1인기업가라면 필수신청하세요)
      인스타그램으로 
      좋아하고 잘하는일로 
      수익 내고 싶은 분
       
      SNS마케팅으로 
      개인 플랫폼 만들고
      싶은 분
       

      매력적인피드 세팅 
      밎 피드 계정 정리가 
      필요하신 분





      자세한 일차별 커리큘럼 \uD83D\uDCDA


      \uD83C\uDF38매일미션으로 신청자분들과 협의후 강의가진행 , 11일간 미션제공으로 활동이 진행됩니다.

      - 10월17일 OT 챌린지 활동 비법 

      - 요일 투표 후 인스타그램 전략 강의 2회

      (인스타 퍼스널브랜딩 & sns세일즈 관련 & 인스타활용팁)


      - 전략적인 본인을 브랜딩화시키는 11일간 미션제공

      - 모든참여자 개인활동 짧은피드백

      - 인스타그램 수익화내는기반 꿀팁

      - 팔로우 늘리기 / 찐친만들기

      - 개인브랜딩화시키기

      - 개인의자아찾기 (내면의 잠재적인 능력찾기)

      - 체험단 리스트제공(완주하시는분)

      - 공구 수익활동 사후제공(완주하신분)

      - 계정활성화 

      - 비즈니스홍보마케팅

      - 매력있는 피드세팅

      - 피드 상위 노출 활동

      ✅ 인증방식  
      챌토링 인증은 필수로 진행합니다.

      매우 심플하게 활동 커리큘럼 세팅!
      댓글 피로도 최소화!
      매일 미션으로 인증 링크를 공유하며 피드백까지 세팅이 되어있습니다.
      (기존 참여자분들의 만족도가 매우 높았습니다.)






      잠깐! 혹시 Zoom이 있나요? \uD83C\uDFA5

      • 이 챌린지는 Zoom이 있나요?            챌린지 기간 동안 Zoom(줌)을 진행합니다.
      • 몇 번이나 있나요?                              3회  
      • 녹화본은 제공되나요?                         ZOOM(줌) 녹화본 제공합니다.
      •  ZOOM(줌) OT 시작은 언제인가요?   챌린지 신청자분들의 소중한의견을담아투표로진행됩니다. 





      리더 소개 \uD83E\uDDD1\uD83C\uDFFB‍\uD83C\uDFEB


      " 수현쌤"은 누구인가요?


      언제나 잔잔하고 꾸준하고 디테일하고 정성스럽게 활동하고있는 

      인스타 세일즈마케팅강사 수현 멘토 인사드립니다.

      MKYU로인해 폭풍성장한 1인이며 많은 영향력과 역량으로 성장했고 귀한결과를 많이냈습니다



      사랑 받은 만큼 귀한 역량을 이제는 저도 선한 영향력으로 더 나눠야할때가 온 것같고

      같이의 가치를 전하는 mkyu의 메세지 많은분들께 전하고싶습니다.


      또한 많은분들과 소통해보니 자신의 잠재적인 재능을 못찾으셨기에

      이번에 나 사랑하기 인스타그램 퍼스널브랜딩 챌토링 1기와

      기존 함께한분들의 챌린지를 성공리에 마쳐 또다시 정성껏 준비했습니다


      정성을 다해 챌린지를 준비하여 많은분들의 성장에도움을 드릴것을 약속드립니다♡





      경력 및 이력


      * 세일즈경력 17년차, 1월부터 514챌린지 함께한 시조새 디세리쏭블리쌤입니다.

      \uD83C\uDF31mkyu열정대학생 수강후 폭풍성장한 1인이며 개인브랜딩마케팅으로 많은성과를냈습니다.

      - 디지털세일즈리더 SNS세일즈 클래스운영

      - SNS세일즈 및 전략마케팅 실전 쪽집게 과외

      - SNS세일즈마케팅 전국상위매출 1위달성

      - 월매출 4억이상 / 개인순이익 월1천이상 .5천기록

      - 개인비즈니스 전국 상위실적

      - MKYU서울 동남부방장으로활동

      - 뉴스정보커뮤니티. 디세리커뮤니티 활동

      - 61.6만게시물 _기타 해시태그 상위노출다수

      - 개인컨셉 자아찾기 1인브랜딩 성장



      인스타그램에서 #디세리2주챌린지를검색해주세요

      " -} From ac2f2c56c3694ea600de363e90cb6ca678901bcd Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 19 Jul 2023 18:20:11 +0900 Subject: [PATCH 79/90] =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/general/view/PostView.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/pages/general/view/PostView.js b/src/pages/general/view/PostView.js index 86adc32..180a7de 100644 --- a/src/pages/general/view/PostView.js +++ b/src/pages/general/view/PostView.js @@ -7,7 +7,6 @@ import 'react-quill/dist/quill.snow.css'; import ViewTemplate from '../../../components/templates/general/ViewTemplate'; import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; import { useNavigate } from 'react-router'; -import LikeBtn from '../../../components/molecules/post/LikeBtn'; export const SubmitButton = styled.button` display: block; @@ -77,12 +76,6 @@ export default function PostView() { `/post/${postId}`, [chnlUri, postId] ); - console.log(user); - - // const {data: likeData, isLoading: likeIsLoading, error: likeErr} = useApiGet( - // `/post/${postId}/like/${user.nic}`,[] - // ); - const navigate = useNavigate(); if (isLoading) return; @@ -117,13 +110,11 @@ export default function PostView() {
      goProfile(data.writer.nic)}> -
      {data.writer.nic} {data.post.postPblcDtm} / 조회 {data.post.postInqrCnt} -
      From ce5115a47d05407744e7a38181271b46dab3f41f Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 19 Jul 2023 18:20:52 +0900 Subject: [PATCH 80/90] =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/general/view/PostView.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pages/general/view/PostView.js b/src/pages/general/view/PostView.js index 180a7de..6a7b32c 100644 --- a/src/pages/general/view/PostView.js +++ b/src/pages/general/view/PostView.js @@ -7,6 +7,7 @@ import 'react-quill/dist/quill.snow.css'; import ViewTemplate from '../../../components/templates/general/ViewTemplate'; import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; import { useNavigate } from 'react-router'; +import LikeBtn from '../../../components/molecules/post/LikeBtn'; export const SubmitButton = styled.button` display: block; @@ -76,6 +77,12 @@ export default function PostView() { `/post/${postId}`, [chnlUri, postId] ); + console.log(user); + + // const {data: likeData, isLoading: likeIsLoading, error: likeErr} = useApiGet( + // `/post/${postId}/like/${user.nic}`,[] + // ); + const navigate = useNavigate(); if (isLoading) return; From b1d28d2da92ddde0dd258ee885d89c21690a51a0 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 19 Jul 2023 18:22:46 +0900 Subject: [PATCH 81/90] =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appRoute.js | 2 +- src/pages/general/channel/ChannelHome.js | 2 +- src/pages/general/channel/ChannelWebnovel.js | 2 +- src/pages/general/create/PostCreate.js | 16 +--------------- 4 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/appRoute.js b/src/appRoute.js index 9dae9f6..838f460 100644 --- a/src/appRoute.js +++ b/src/appRoute.js @@ -79,8 +79,8 @@ const urls = { postCreate: `/:chnlUri/post/create`, // view - postView: '/:chnlUri/post/:postId', + postView: '/post/:postId', }; // Links diff --git a/src/pages/general/channel/ChannelHome.js b/src/pages/general/channel/ChannelHome.js index 1093e65..a78c634 100644 --- a/src/pages/general/channel/ChannelHome.js +++ b/src/pages/general/channel/ChannelHome.js @@ -141,7 +141,7 @@ export default function ChannelHome() { {">"} {data.data.webnovels.length !== 0 ? ( - data.data.webnovels.map((post) => ) + data.data.webnovels.map((post) => ) ) : ( <> diff --git a/src/pages/general/channel/ChannelWebnovel.js b/src/pages/general/channel/ChannelWebnovel.js index d5c3413..fc95133 100644 --- a/src/pages/general/channel/ChannelWebnovel.js +++ b/src/pages/general/channel/ChannelWebnovel.js @@ -154,4 +154,4 @@ export default function ChannelWebnovel() { ) : ''} ); -} +} \ No newline at end of file diff --git a/src/pages/general/create/PostCreate.js b/src/pages/general/create/PostCreate.js index 6663c73..30ef205 100644 --- a/src/pages/general/create/PostCreate.js +++ b/src/pages/general/create/PostCreate.js @@ -10,6 +10,7 @@ import ImageResize from 'quill-image-resize'; import PostRadioButton from '../../../components/molecules/post/PostRadioButton'; import PostImg from '../../../components/atoms/Post/PostImgSC'; import { useNavigate } from 'react-router'; +import useUserStore from '../../../stores/useUserStore'; export const SubmitButton = styled.button` display: block; @@ -76,19 +77,6 @@ export default function PostCreate() { imageUrls: imageUrls, }); - const { - res: postRes, - error: postErr, - setError: setPostErr, - postData: post, - } = useApiPost(`post/create`, { - postType: postType, - postTtl: title, - postSbTtl: subTitle, - postContent: content, - postThumnPath: thumbnailImageUrl, - imageUrls: imageUrls, - }); const { res: postEditRes, error: postEditErr, @@ -127,7 +115,6 @@ export default function PostCreate() { // multer에 맞는 형식으로 데이터 만들어준다. Array.prototype.forEach.call(files, function(file) { imageData.append('multipartFiles',file); - console.log('multipartFiles', file); }); await upload(imageData); @@ -336,7 +323,6 @@ export default function PostCreate() { onChange={handleSubTitleChange} onKeyDown={handleSubTitleChange} /> -
      From 821f7660d0530d0755eaa45158347a3148a0caae Mon Sep 17 00:00:00 2001 From: dancingKim Date: Mon, 10 Jul 2023 15:44:19 +0900 Subject: [PATCH 82/90] =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appRoute.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/appRoute.js b/src/appRoute.js index 838f460..055d4ed 100644 --- a/src/appRoute.js +++ b/src/appRoute.js @@ -79,7 +79,6 @@ const urls = { postCreate: `/:chnlUri/post/create`, // view - postView: '/post/:postId', }; From 4d5325727b87b5890ba5903c1cb5dca02e1ad229 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 19 Jul 2023 18:35:57 +0900 Subject: [PATCH 83/90] =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appRoute.js | 2 +- src/pages/general/search/SearchWebnovel.js | 3 +-- src/pages/general/search/SearchWebtoon.js | 5 +---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/appRoute.js b/src/appRoute.js index 055d4ed..d9a2718 100644 --- a/src/appRoute.js +++ b/src/appRoute.js @@ -60,7 +60,7 @@ const urls = { setBlacklist: '/account/settings/blacklist', //channel - channelCreate: '/:chnlUri/channel/create', + channelCreate: '/channel/create', channel: '/channel/:chnlUri', channelWebtoon: '/channel/:chnlUri/webtoon', channelWebnovel: '/channel/:chnlUri/webnovel', diff --git a/src/pages/general/search/SearchWebnovel.js b/src/pages/general/search/SearchWebnovel.js index 0c02f68..1c55d64 100644 --- a/src/pages/general/search/SearchWebnovel.js +++ b/src/pages/general/search/SearchWebnovel.js @@ -151,7 +151,6 @@ export default function SearchWebnovel() {
      )} - ); -} +} \ No newline at end of file diff --git a/src/pages/general/search/SearchWebtoon.js b/src/pages/general/search/SearchWebtoon.js index 1c0d667..92acc47 100644 --- a/src/pages/general/search/SearchWebtoon.js +++ b/src/pages/general/search/SearchWebtoon.js @@ -26,7 +26,6 @@ const SectionHeaderFilter = styled.div` font-weight: 500; `; - const WebtoonListContainer = styled.div` display: flex; flex-wrap: wrap; @@ -248,8 +247,6 @@ export default function SearchWebtoon() { ) } - - ); -} +} \ No newline at end of file From 8738528dbbbd728f1efbd4072b68b53043616f37 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 19 Jul 2023 18:37:58 +0900 Subject: [PATCH 84/90] =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/general/home/Home.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/pages/general/home/Home.js b/src/pages/general/home/Home.js index 5123474..5cac219 100644 --- a/src/pages/general/home/Home.js +++ b/src/pages/general/home/Home.js @@ -43,6 +43,25 @@ export default function Home() { +<<<<<<< HEAD +======= + {/*
      이거 홈 맞지?*/} + {/*
      */} + {/* */} + {/* */} + {/* 공부하는 여우원숭이 프로필 이동*/} + {/* */} + {/*
      */} + {/*
      */} + {/* */} + {/*

      BUKSAN 채널 바로가기

      */} + {/*
      */} + {/* */} + {/*

      POST CREATE 바로가기

      */} + {/*
      */} + {/*
      */} + {/*
      */} +>>>>>>> cfc9fcc (feat: Added searching feature) ); } From ac604daab1410b858f1e3bdb8048a0431995a600 Mon Sep 17 00:00:00 2001 From: dancingKim Date: Wed, 19 Jul 2023 10:18:21 +0900 Subject: [PATCH 85/90] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=EC=95=88=20?= =?UTF-8?q?=ED=95=A0=EC=8B=9C=20api=20=EC=A3=BC=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api.js b/src/api.js index f4d5ab6..c6dc34f 100644 --- a/src/api.js +++ b/src/api.js @@ -1,8 +1,8 @@ import axios from 'axios'; export const api = axios.create({ - // baseURL: 'http://localhost:8080', - baseURL: 'http://3.39.6.61:8080', + baseURL: 'http://localhost:8080', + // baseURL: 'http://3.39.6.61:8080', }); From 9b4ba1b074cb2169acd6d970beab7908b53ef3af Mon Sep 17 00:00:00 2001 From: dancingKim Date: Thu, 20 Jul 2023 00:16:27 +0900 Subject: [PATCH 86/90] feat: Added scrap button to webtoon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 웹툰 박스에 스크랩 버튼을 추가했음 --- .../molecules/channel/SubscribeBlackBtn.js | 6 +-- src/components/molecules/post/LikeBtn.js | 4 +- src/pages/general/channel/ChannelHome.js | 40 ++++++++++++------- src/pages/general/channel/ChannelWebtoon.js | 33 ++++++++------- src/pages/general/home/Home.js | 19 --------- src/pages/general/search/SearchWebtoon.js | 34 ++++++++-------- 6 files changed, 66 insertions(+), 70 deletions(-) diff --git a/src/components/molecules/channel/SubscribeBlackBtn.js b/src/components/molecules/channel/SubscribeBlackBtn.js index 27eb771..5e20537 100644 --- a/src/components/molecules/channel/SubscribeBlackBtn.js +++ b/src/components/molecules/channel/SubscribeBlackBtn.js @@ -5,7 +5,7 @@ import { useApiPost, useApiDelete } from '../../../hooks/useApi'; import useModal from '../modal/useModal'; import ConfirmBox from '../modal/ConfirmBox'; import { useEffect } from 'react'; -import useChannelList from '../../../stores/useChannelList'; +import useSubChannelList from '../../../stores/useSubChannelList'; import SubsBlackBtn from '../../atoms/Channel/SubsBlackBtn'; export default function SubscribeBlackBtn({ isSubsed, chnlId }) { @@ -23,7 +23,7 @@ export default function SubscribeBlackBtn({ isSubsed, chnlId }) { setError: setUnsubsErr, deleteData: unsubscribe, } = useApiDelete(`/subscriptions/cancle?chnlId=${chnlId}`); - const { removeChannel } = useChannelList(); + const { removeChannel } = useSubChannelList(); const currentPath = window.location.pathname; useEffect(() => { @@ -82,7 +82,7 @@ export default function SubscribeBlackBtn({ isSubsed, chnlId }) { )} ) : ( - + 구독123 + + 구독 )}
      ); diff --git a/src/components/molecules/post/LikeBtn.js b/src/components/molecules/post/LikeBtn.js index af09cdc..458225e 100644 --- a/src/components/molecules/post/LikeBtn.js +++ b/src/components/molecules/post/LikeBtn.js @@ -3,7 +3,7 @@ import { useApiPost, useApiDelete } from '../../../hooks/useApi'; import useModal from '../modal/useModal'; import ConfirmBox from '../modal/ConfirmBox'; import { useEffect } from 'react'; -import useChannelList from '../../../stores/useChannelList'; +import useSubChannelList from '../../../stores/useSubChannelList'; import { AiOutlineHeart } from 'react-icons/ai'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import UnHeartBtn from './UnHeartBtn'; @@ -24,7 +24,7 @@ export default function LikeBtn({ liked, postId }) { setError: setUnlikeErr, deleteData: unlike, } = useApiDelete(`/library/like?postId=${postId}`); - const { removeChannel } = useChannelList(); + const { removeChannel } = useSubChannelList(); const currentPath = window.location.pathname; useEffect(() => { diff --git a/src/pages/general/channel/ChannelHome.js b/src/pages/general/channel/ChannelHome.js index a78c634..d25b257 100644 --- a/src/pages/general/channel/ChannelHome.js +++ b/src/pages/general/channel/ChannelHome.js @@ -8,6 +8,13 @@ import NoContent from '../../../components/molecules/error/NoContent'; import BtnLinkSC from '../../../components/atoms/Link/BtnLinkSC'; import { useNavigate } from 'react-router'; import { countDate } from '../../../components/molecules/user/dateConversion'; +import SubsBtn from '../../../components/atoms/Channel/SubsBtn'; +import ScrapPostBtn from '../../../components/molecules/user/ScrapPostBtn'; +import UserBanner from '../../../components/molecules/user/UserBanner'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faCalendarAlt, faUser } from '@fortawesome/free-solid-svg-icons'; +import { faClone } from '@fortawesome/free-regular-svg-icons'; +import { AiOutlineHeart } from 'react-icons/ai'; const SectionHeader = styled.div` padding: 0 0 10px; @@ -62,6 +69,7 @@ const WebtoonTitle = styled.h3` const WebtoonSubInfo = styled.div` display: flex; + justify-content: space-between; `; const WebtoonWriter = styled.span` @@ -73,7 +81,7 @@ const WebtoonCount = styled.span` margin-left: 8px; color: rgba(0,0,0,.47); font-size: 12px; - + `; const WebtoonViewCountIcon = styled.img` @@ -107,7 +115,7 @@ export default function ChannelHome() { 웹툰 - {">"} + {">"} {data.data.webtoons.map((post, index) => ( @@ -118,19 +126,21 @@ export default function ChannelHome() { {post.postTtl}
      - {data.data.channelUser.nic} - - - {post.postInqrCnt} - - - {/* */} - ♡ - {post.postLikCnt} - - - {countDate(post.postPblcDtm)} - +
      + {data.data.channelUser.nic} + + + {post.postInqrCnt} + + + + {post.postLikCnt} + + + {countDate(post.postPblcDtm)} + +
      +
      ))} diff --git a/src/pages/general/channel/ChannelWebtoon.js b/src/pages/general/channel/ChannelWebtoon.js index c74c23b..478b6f3 100644 --- a/src/pages/general/channel/ChannelWebtoon.js +++ b/src/pages/general/channel/ChannelWebtoon.js @@ -10,6 +10,8 @@ import { useApiGet } from '../../../hooks/useApi'; import { post } from 'axios'; import { useNavigate } from 'react-router'; import { countDate } from '../../../components/molecules/user/dateConversion'; +import { AiOutlineHeart } from 'react-icons/ai'; +import ScrapPostBtn from '../../../components/molecules/user/ScrapPostBtn'; const SectionHeader = styled.div` padding: 0 0 10px; @@ -71,6 +73,7 @@ const WebtoonTitle = styled.h3` const WebtoonSubInfo = styled.div` display: flex; + justify-content: space-between; `; const WebtoonWriter = styled.span` @@ -202,21 +205,21 @@ export default function ChannelWebtoon() { {post.postTtl}
    - {data.data.channelUser.nic} - - - {post.postInqrCnt} - - - {/* */} - ♡ - {post.postLikCnt} - - - {countDate(post.postPblcDtm)} - - - +
    + {data.data.channelUser.nic} + + + {post.postInqrCnt} + + + + {post.postLikCnt} + + + {countDate(post.postPblcDtm)} + +
    +
    ))} diff --git a/src/pages/general/home/Home.js b/src/pages/general/home/Home.js index 5cac219..5123474 100644 --- a/src/pages/general/home/Home.js +++ b/src/pages/general/home/Home.js @@ -43,25 +43,6 @@ export default function Home() { -<<<<<<< HEAD -======= - {/*
    이거 홈 맞지?*/} - {/*
    */} - {/* */} - {/* */} - {/* 공부하는 여우원숭이 프로필 이동*/} - {/* */} - {/*
    */} - {/*
    */} - {/* */} - {/*

    BUKSAN 채널 바로가기

    */} - {/*
    */} - {/* */} - {/*

    POST CREATE 바로가기

    */} - {/*
    */} - {/*
    */} - {/*
    */} ->>>>>>> cfc9fcc (feat: Added searching feature) ); } diff --git a/src/pages/general/search/SearchWebtoon.js b/src/pages/general/search/SearchWebtoon.js index 92acc47..94a48b6 100644 --- a/src/pages/general/search/SearchWebtoon.js +++ b/src/pages/general/search/SearchWebtoon.js @@ -9,6 +9,8 @@ import styled from 'styled-components'; import { useNavigate } from 'react-router'; import NoContent from '../../../components/molecules/error/NoContent'; import { countDate } from '../../../components/molecules/user/dateConversion'; +import { AiOutlineHeart } from 'react-icons/ai'; +import ScrapPostBtn from '../../../components/molecules/user/ScrapPostBtn'; const SectionHeader = styled.div` padding: 0 0 10px; @@ -70,6 +72,7 @@ const WebtoonTitle = styled.h3` const WebtoonSubInfo = styled.div` display: flex; + justify-content: space-between; `; const WebtoonWriter = styled.span` @@ -190,7 +193,6 @@ export default function SearchWebtoon() { <> - {data.posts.map((post, index) => ( goPost(post.postId)} /> @@ -199,21 +201,21 @@ export default function SearchWebtoon() { {post.postTtl} - {post.userNic} - - - {post.postInqrCnt} - - - {/* */} - ♡ - {post.postLikCnt} - - - {countDate(post.postPblcDtm)} - - - +
    + {post.userNic} + + + {post.postInqrCnt} + + + + {post.postLikCnt} + + + {countDate(post.postPblcDtm)} + +
    +
    ))} From 3d5f5ec4fe5cc246fd807f55df57bff7bc9ed4d4 Mon Sep 17 00:00:00 2001 From: dancingKim <111945532+dancingKim@users.noreply.github.com> Date: Fri, 28 Jul 2023 17:26:03 +0900 Subject: [PATCH 87/90] Update README.md --- README.md | 206 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 163 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 5b1aaaf..3bd4168 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,196 @@ -# 정석코딩 2기 1팀 프로젝트 포스토리 -## 팀원 -- 황인태 (팀장) -- 김정호 -- 강세민 +# POSTORY-검색형 웹툰 웹소설 플랫폼 -# Getting Started with Create React App -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). +작가들 번아웃과 독자들 피로감을 해결하기 위한 구글식 컨텐츠 플랫폼입니다. +내용을 검색하고, 마음에 드는 채널을 구독하고, 보고 싶은 내용을 스크랩합니다. -## Available Scripts +## 프로젝트 설명 +지속가능한 웹툰 웹소설 플랫폼을 만들고자 하였습니다. +그러기 위해 크리에이터에게 비교보단 자기 자신 작품에 집중하는 환경을 만들어주고, +독자에겐 주어진 작품을 보기보단 원하는 작품을 찾아 보관하는 경험을 제공했습니다. +독자와 크리에이터, 크리에이터와 크리에이터, 작품과 작품 간 상호작용이 많아질 수록 +비슷한 작품만 만들어지고, 그런 작품 중 수작이 나오는 건 규모가 큰 기업만 가능하다는 판단을 했기 때문입니다. -In the project directory, you can run: +### 사용한 기술들 -### `yarn start` +1. Back-end -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in your browser. + 처음엔 Spring Legacy를 maven 방식으로 빌드했습니다. 그리고 spring security를 적용했습니다. 그리고 완성한 영역 외에 결제, 멤버십, 상품, 포인트 등 모두 68개 테이블을 설계했었습니다. -The page will reload when you make changes.\ -You may also see any lint errors in the console. + 그러나 spring security를 spring legacy 환경에서 김정호(본인)는 사용할 수 있으나, + 팀이 사용하기 어렵다 판단해 spring boot로 전환했습니다. 프로젝트 완성 기한을 맞추기 위해 앞서 언급한 상당수 기능 구현을 포기했습니다. 더 자세한 이유는 링크로 첨부합니다. -### `yarn test` + 최종적으로 사용한 기술은 다음과 같습니다. + * Spring Boot -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + spring security 적용을 모두가 쉽게 할 수 있어 사용했습니다. + * Spring Security -### `yarn build` + JWT 방식을 사용하기 쉬워 사용했습니다. + * Mybatis -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. + 쿼리를 연습할 시간이라 판단하여 사용했습니다. + * gradle -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! + 추후 서비스를 확장할 때 변경을 쉽게 할 수 있게 maven보다 gradle을 선택했습니다. + * MySql -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + AWS 서비스들과 연동이 쉬워 사용했습니다. + * AWS RDS -### `yarn eject` + 공용 database를 위해 선택했습니다. 공식문서와 유저풀이 풍부해 자료를 찾기 쉬웠습니다. + * AWS S3 -**Note: this is a one-way operation. Once you `eject`, you can't go back!** + 이미지 업로드를 하기 위해 사용했습니다. 처음엔 AWS 공부할 여유가 없다 판단해 local react 서버에 저장하는 방법을 택했습니다. 그러나 client가 server 컴퓨터 경로를 알아낼 수 있다는 점과 리액트 서버가 무거워진다는 단점이 있어 AWS S3를 도입했습니다. + * AWS EC2 -If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + 배포를 하기 위해 이용한 서비스입니다. 다른 서비스들보다 현장에서 많이 사용하는 서비스였고, 공식문서와 그외 자료들에 접근하기 쉬웠습니다. 또한 웹툰 웹소설 플랫폼 특성상 많은 트래픽 처리를 하게 될 거라 판단했습니다. 접근성과 중요성, 확장성을 모두 만족하는 서비스이기에 선택했습니다. -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. -You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. +2. Front-end -## Learn More + 팀원이 3명밖에 되지 않아 재사용성에 초점을 뒀습니다. -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + 팀 프로젝트를 위해 상품 게시판, 상품 뷰, 결제, 주문, 주문 목록, 주문 상세를 포기했습니다. -To learn React, check out the [React documentation](https://reactjs.org/). + 프로젝트를 기한 내 맞추기 위해 팀원들이 사용할 수 있게 자체 제작 form library를 만들었습니다. + 참조하는 POSTYPE 사이트 폼 형식에 패턴을 발견했고, 변수만 바꿔서 사용하면 개발 시간이 단축된다 판단했기 때문입니다. + 백엔드 개발자 취업이 목표기에 렌더링 시간은 고려 대상이 아니었기 때문이기도 합니다. + 그러나 짧은 시간에 팀원들을 설득하지 못해 사용하지 못했습니다. 자세한 내용은 링크를 첨부합니다. + (첨부할 링크 자료) -### Code Splitting + * React, JSX -This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) + 재사용성을 위해 javascript보다 React를 이용한 JSX를 사용했습니다. + * styled-components -### Analyzing the Bundle Size + 위와 같은 이유로 html, css보다 styled-componets를 사용했습니다. + * javascript, html, css -This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) + 위 기술들을 보완하는 차원에서 사용했습니다. + 동적으로 조건을 판단할 때 javascript를 사용했고, 포스트 내용을 통으로 저장할 때 html, css를 사용했습니다. + * react quill -### Making a Progressive Web App + 핸들러 기능을 사용하고 에디터 객체에 접근하기 쉬워 사용했습니다. DB 비용을 줄여야 했습니다. + 그렇기 위해서 base64로 나타나는 이미지 태그 src 값을 짧은 String URL로 변환해야 했습니다. + react quill은 업로드하는 이미지에 이미지 핸들러로 접근할 수 있게 해줬고, + 그 이미지 base64 src 값만 서버에 보내 짧은 String 고유값(URL)으로 변환했습니다. + S3 서버에 업로드한 후, 고유값으로 바뀐 URL을 client에게 전달했습니다. + 그리고 에디터 객체에 접근해 현재 커서가 있는 곳 위치에 src를 이 값으로 바꿨습니다. + 포스트를 수정할 때도 위 편의 기능들을 유용하게 썼습니다. + * AWS EC2 -This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) + 백엔드에서 서술했던 내용과 동일합니다. + * nginix -### Advanced Configuration + 동시에 여러명이 이용하는 웹툰 웹소설 플랫폼 특성을 고려해 비동기 통신에 효율적인 nginix를 이용했습니다. + * yarn -This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) + 팀원 간 컴퓨터 환경이 달랐기에 yarn을 이용해 package를 관리했습니다. + * ZUSTAND -### Deployment + 로그인한 유저 정보와 같이 전역변수를 관리하기 위해 사용했습니다. -This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) +## 프로젝트 설치 및 실행 방법 +develop branch 기준으로 진행해주세요.(main, dev_kjh는 완성본이 아닙니다.) -### `yarn build` fails to minify +1. git clone으로 코드를 받아주세요. + ``` + git clone git clone -b develop https://github.com/dancingKim/postory_back.git + ``` -This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) +2. 아직 도메인을 사진 않았습니다. 현재 AWS 계정 비밀번호를 아는 것 외에 프로그램을 로컬에서 실행시킬 수 있는 방법이 없습니다. 추후 도메인을 산 후에 업데이트 하겠습니다. +3. 불편하시더라도 코드 위주로 봐주세요. +## 프로젝트 사용 방법 + + + +원하는 키워드로 검색을 합니다. +![home_webtoon](https://github.com/dancingKim/postory_back/assets/111945532/4b8a77df-2d56-48c4-877d-ae97fa3fb7d7) + +만화 키워드로 웹툰을 검색했습니다. +관심 포스트를 스크랩합니다. + +![search_webtoon](https://github.com/dancingKim/postory_back/assets/111945532/ae910869-8a02-41e4-bc18-70b61d2134ff) + + +보관함에서 스크랩한 포스트를 모아볼 수 있습니다. + +![save_scrap](https://github.com/dancingKim/postory_back/assets/111945532/a0d2ba9b-f044-46b3-b38d-297bcf340ec4) + + +채널을 검색해 들어갈 수도 있습니다. +채널을 구독할 수도 있습니다. +구독한 채널과 채널이 발행한 포스트들은 구독 탭에서 볼 수 있습니다. + +![other_webnovel_home](https://github.com/dancingKim/postory_back/assets/111945532/31fa729b-e2cd-4132-8554-bb856222994c) + + +내 채널에선 포스트를 발행할 수 있습니다. +구독버튼 자리에 포스트 발행하기 버튼이 뜹니다. + +![channel_home](https://github.com/dancingKim/postory_back/assets/111945532/626a66e7-1891-40f5-84c5-dd7537941f9e) + + +웹툰, 웹소설 타입으로 포스트를 발행할 수 있습니다. + +![post_create](https://github.com/dancingKim/postory_back/assets/111945532/ed92b0f1-d629-48eb-ae22-8f853d5245d9) + +포스트를 보고, 마음에 안 들면 수정할 수도 있습니다. +이미지를 추가해 보았습니다. + +![edited_post_view](https://github.com/dancingKim/postory_back/assets/111945532/d5a0c3cd-47b8-4c3d-9600-100f84e92d4f) + + +## 팀원 및 참고 자료 +### 팀 소개 +1. 팀장 +황인태 + +2. 팀원 +김정호 +강세민 + +### 참고 자료 +### 참조 사이트 +POSTYPE +### 참조 컨텐츠 +NAVER WEBTOON + +https://comic.naver.com/webtoon/list?titleId=769209 + +https://comic.naver.com/webtoon/list?titleId=687915 + +https://comic.naver.com/webtoon/list?titleId=809054 + +https://comic.naver.com/webtoon/list?titleId=807178 + +https://comic.naver.com/webtoon/list?titleId=747269 + +https://comic.naver.com/webtoon/list?titleId=758662 + +NAVER WEBNOVEL + +https://novel.naver.com/webnovel/list?novelId=1062250 + +https://novel.naver.com/webnovel/list?novelId=1073928 + +https://novel.naver.com/webnovel/list?novelId=949763 + +https://novel.naver.com/webnovel/list?novelId=1058954 + +https://novel.naver.com/best/list?novelId=22398 + +OTHERS + +https://bbs.ruliweb.com/family/212/board/300063/read/30643759 + +https://cafe.naver.com/steamindiegame/5396703 + +https://softwaremill.com/programmers-day-programming-memes-2022/ + +https://velog.io/@heelieben/%EA%B0%9C%EB%B0%9C-%EA%B3%B5%EA%B0%90-%EC%A7%A4-%EA%B5%AC%EA%B2%BD%ED%95%98%EA%B3%A0-%EA%B0%80%EC%84%B8%EC%9A%94 + +## 라이센스 + +“This project is licensed under the terms of the MIT license.” From f92301872402a7d6685556c5ea95bf4ee59ad931 Mon Sep 17 00:00:00 2001 From: dancingKim <111945532+dancingKim@users.noreply.github.com> Date: Fri, 28 Jul 2023 17:29:02 +0900 Subject: [PATCH 88/90] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3bd4168..10b7199 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ develop branch 기준으로 진행해주세요.(main, dev_kjh는 완성본이 1. git clone으로 코드를 받아주세요. ``` - git clone git clone -b develop https://github.com/dancingKim/postory_back.git + git clone git clone -b develop https://github.com/dancingKim/Postory_Front.git ``` 2. 아직 도메인을 사진 않았습니다. 현재 AWS 계정 비밀번호를 아는 것 외에 프로그램을 로컬에서 실행시킬 수 있는 방법이 없습니다. 추후 도메인을 산 후에 업데이트 하겠습니다. From 49162d61733e847dc867656f8a072436dd6fd28e Mon Sep 17 00:00:00 2001 From: dancingKim <111945532+dancingKim@users.noreply.github.com> Date: Fri, 4 Aug 2023 11:28:18 +0900 Subject: [PATCH 89/90] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 10b7199..99bd723 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ 처음엔 Spring Legacy를 maven 방식으로 빌드했습니다. 그리고 spring security를 적용했습니다. 그리고 완성한 영역 외에 결제, 멤버십, 상품, 포인트 등 모두 68개 테이블을 설계했었습니다. 그러나 spring security를 spring legacy 환경에서 김정호(본인)는 사용할 수 있으나, - 팀이 사용하기 어렵다 판단해 spring boot로 전환했습니다. 프로젝트 완성 기한을 맞추기 위해 앞서 언급한 상당수 기능 구현을 포기했습니다. 더 자세한 이유는 링크로 첨부합니다. + 팀이 사용하기 어렵다 판단해 spring boot로 전환했습니다. 프로젝트 완성 기한을 맞추기 위해 앞서 언급한 상당수 기능 구현을 포기했습니다. 더 자세한 이유는 링크로 첨부합니다. + https://lovedprogrammer.tistory.com/2 최종적으로 사용한 기술은 다음과 같습니다. * Spring Boot From 2a071a385e326492af84cd0ce9d3560e17e70498 Mon Sep 17 00:00:00 2001 From: KIM JEONGHO <111945532+dancingKim@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:29:24 +0900 Subject: [PATCH 90/90] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 99bd723..5c07751 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ 처음엔 Spring Legacy를 maven 방식으로 빌드했습니다. 그리고 spring security를 적용했습니다. 그리고 완성한 영역 외에 결제, 멤버십, 상품, 포인트 등 모두 68개 테이블을 설계했었습니다. - 그러나 spring security를 spring legacy 환경에서 김정호(본인)는 사용할 수 있으나, - 팀이 사용하기 어렵다 판단해 spring boot로 전환했습니다. 프로젝트 완성 기한을 맞추기 위해 앞서 언급한 상당수 기능 구현을 포기했습니다. 더 자세한 이유는 링크로 첨부합니다. - https://lovedprogrammer.tistory.com/2 + spring security를 spring legacy 환경에서 사용할 수 있게 했으나, + 팀원 모두가 이해하기엔 어렵다 판단해 spring boot로 전환했습니다. 프로젝트 완성 기한을 맞추기 위해 앞서 언급한 상당수 기능 구현을 포기했습니다. 더 자세한 이유는 링크로 첨부합니다. + https://velog.io/@iamloved5959/Spring-Legacy%EC%99%80-React%EC%97%90%EC%84%9C-spring-security-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0 최종적으로 사용한 기술은 다음과 같습니다. * Spring Boot @@ -48,7 +48,7 @@ 배포를 하기 위해 이용한 서비스입니다. 다른 서비스들보다 현장에서 많이 사용하는 서비스였고, 공식문서와 그외 자료들에 접근하기 쉬웠습니다. 또한 웹툰 웹소설 플랫폼 특성상 많은 트래픽 처리를 하게 될 거라 판단했습니다. 접근성과 중요성, 확장성을 모두 만족하는 서비스이기에 선택했습니다. -2. Front-end +3. Front-end 팀원이 3명밖에 되지 않아 재사용성에 초점을 뒀습니다. @@ -57,8 +57,8 @@ 프로젝트를 기한 내 맞추기 위해 팀원들이 사용할 수 있게 자체 제작 form library를 만들었습니다. 참조하는 POSTYPE 사이트 폼 형식에 패턴을 발견했고, 변수만 바꿔서 사용하면 개발 시간이 단축된다 판단했기 때문입니다. 백엔드 개발자 취업이 목표기에 렌더링 시간은 고려 대상이 아니었기 때문이기도 합니다. - 그러나 짧은 시간에 팀원들을 설득하지 못해 사용하지 못했습니다. 자세한 내용은 링크를 첨부합니다. - (첨부할 링크 자료) + 그러나 짧은 시간에 팀원들을 설득하지 못해 사용하지 못했습니다. + 폼 라이브러리 자료는 컴퓨터 백업 문제로 자료가 날라갔습니다. 자료를 복원시키는 즉시 링크를 첨부하겠습니다. * React, JSX @@ -97,7 +97,7 @@ develop branch 기준으로 진행해주세요.(main, dev_kjh는 완성본이 1. git clone으로 코드를 받아주세요. ``` - git clone git clone -b develop https://github.com/dancingKim/Postory_Front.git + git clone git clone -b develop https://github.com/dancingKim/postory_back.git ``` 2. 아직 도메인을 사진 않았습니다. 현재 AWS 계정 비밀번호를 아는 것 외에 프로그램을 로컬에서 실행시킬 수 있는 방법이 없습니다. 추후 도메인을 산 후에 업데이트 하겠습니다.