프로젝트 리뷰 - From.C
1. 완성화면 보러가기
2. 개발 기간 및 기여
개발 기간:
- 2022년 9월 28일(수) ~ 10월 26일(수) ※ 현재 운영 중에 있음.
기여:
- 프론트 메인 개발
3. 프로젝트 제목 및 설명
제목:
- FromC - 위탁 교육, 도입 문의 웹
설명:
- 초, 중, 고 학생 방과 후 코딩 교육 지원
- root > src: 프론트를 구성하는 소스코드
4. 기술 스택 및 사용된 도구
언어 :
- JavaScript(ES6+): 최신 ECMAScript 표준을 사용한 프로그래밍.
프레임워크 및 도구:
- react: 프레임워크
- react-router: SPA(Single Page Application) 라우팅 관리
- react-redux: 전역 상태 관리
- react-calendar: 도입 문의 시, date input 노출
- react-daum-post: 도입 문의 시, 학교명/기관명 주소 검색
- react-scroll: 스크롤 관리
백엔드 통신:
- axios: HTTP 클라이언트 라이브러리
5. 다음 프로젝트에서 알아야 할 것들
1. 마우스 스크롤의 위치에 따라 반응하고 싶다면?
- useEffect 에 window.addEventListener 에 params 로 콜백함수를 추가하고, 콜백함수에 마우스 스크롤에 따른 액션을 정의하면 된다.
// useEffect로 state 변화될 때마다, changeHeader함수를 콜백
useEffect(() => {
window.addEventListener('scroll', changeHeader, { capture: true });
}, []);
// 스크롤 작동 중 내리거나 올리는 중인지 체크 변수
let prevScrollTop = 0;
let nowScrollTop = 0;
let wheelDelta = () => {
return prevScrollTop - nowScrollTop > 0 ? 'up' : 'down';
}
const changeHeader = () => {
nowScrollTop = window.scrollY;
// 스크롤 작동 중 내리거나 올리는 중인지 체크
if (wheelDelta() === 'down') {
if (window.scrollY >= 1000) {
setChangeHeaderTransfrom('translateY(-100px)');
}
}
if (wheelDelta() === 'up') {
setChangeHeaderTransfrom('translateY(0px)');
}
prevScrollTop = nowScrollTop;
// 스크롤의 위치에 따라 헤더의 로고의 feature 변경
if (window.scrollY <= 0) {
setChangeLogo(`url(${logo_light})`);
setChangeColor('');
setChangeFontColor('#FFFFFF');
}
else {
setChangeLogo(`url(${logo_dark})`);
setChangeColor('#FFFFFF')
setChangeFontColor('#6F3AC1');
}
}
2. 리액트 라우트를 어떻게 사용했지?
- 가장 먼저 index.js 에서 BrowserRouter 로 사이트맵이 담긴 상위 컴포넌트(App)을 그룹화한다. 여기서, 사이트 맵의 모든 사이트로 이동할 때마다 스크롤이 최상단에서 보여지길 원하는 경우, 아래와 같이 ScrollToTop 컴포넌트를 상위에 그룹핑해야한다.
import { BrowserRouter } from 'react-router-dom';
import ScrollToTop from './Components/ScrollToTop';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
// ScrollToTop 컴포넌트는 렌더될 때마다, 화면의 가장 상단으로 이동하게 하는 컴포넌트
<ScrollToTop />
// App 컴포넌트는 사이트맵이 담긴 상위 컴포넌트
<App />
</BrowserRouter>
);
- ScrollToTop 코드
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
- App.js 에서 사이트맵에 따라 Routes > Route 를 묶었다. 여기서 가장 위에 있는 Route 가 첫 랜딩 시 보여지는 페이지가 된다.
import { Routes, Route } from 'react-router-dom';
import MainPage from './MainPage';
import ProgramPage1 from './ProgramPage1';
import ProgramPage2 from './ProgramPage2';
// <Route path="주소규칙" element={보여줄 컴포넌트}
const App = () => {
return (
<Routes>
<Route path="/" element={<MainPage />} />
<Route path="/scratch_ai" element={<ProgramPage1 />} />
<Route path="/appinventor_ai" element={<ProgramPage2 />} />
...
<Routes>
- App.js 에서 MainPage.js 로 이동할 수 있도록, 앞에서 설정한 주소규칙과 동일한 값으로 id를 입력해준다.
const MainPage = () => {
return (
// Route의 path 주소값과 보여줄 Component의 id가 서로 일치해야 연결됨
<div className='MainPage' id='/'>
<Header />
<Contact />
...
);
}
3. 어떠한 스코프의 state는 다른 스코프에서 변경할 수 없다.
useEffect 를 사용하면, 웹 페이지에 변화가 감지될 때마다 해당 실행문을 실행하게 되는데 여기에 입력과 관련된 기능을 정의하면, 비동기를 동기처리하는데에도 사용할 수 있다.
[가능한 경우]
const ContactToRegister = () => {
// calendarHeight 의 스코프는 ContactToRegister 에 속해있음
const [calendarHeight, setCalendarHeight] = useState('750px');
// 조건식에 따라 calendarHeight 의 상태값을 변경
useEffect(() => {
if(pointer === 0) {
setCalendarHeight('750px');
} else if(pointer === 1) {
setCalendarHeight('822px');
} else {
setCalendarHeight('890px');
}
}, [pointer])
...
return(
<div
className="selectLessonDays"
style=
>
);
}
4. 카카오 주소 팝업창 사용하는 방법
import { useDaumPostcodePopup } from 'react-daum-postcode';
// 주소창에서 선택한 주소 상태값
const [address, setAddress] = useState("");
// 카카오 주소 팝업창
const Postcode = () => {
const open = useDaumPostcodePopup();
const handleComplete = (data) => {
let fullAddress = data.address;
let extraAddress = '';
if (data.addressType === 'R') {
if (data.bname !== '') {
extraAddress += data.bname;
}
if (data.buildingName !== '') {
extraAddress += extraAddress !== '' ? `, ${data.buildingName}` : data.buildingName;
}
fullAddress += extraAddress !== '' ? ` (${extraAddress})` : '';
}
// 주소창에 입력한 값 상태 저장
setAddress(fullAddress);
// UserInfo에 주소 정보 전달
setUserInfo((prevState) => ({
...prevState,
inquirer: {
...prevState.inquirer,
organizationAddress: fullAddress,
}
}));
};
const handleClick = () => {
open({ onComplete: handleComplete });
};
return (
<div className="wrap">
<input
className="orgInfoInput"
type="text"
placeholder="학교/기관 주소"
// 주소 입력창 클릭 시, Daum 주소창 팝업
onClick={handleClick}
defaultValue={address}
onFocus={e => (e.target.placeholder = "")}
>
</input>
</div>
);
};
5. 유저가 인풋을 통해 제출한 값을 API로 전달하기 위한 객체 정보 관리
[객체의 프로퍼티 value가 하나인 경우]
// 유저가 인풋을 통해 제출한 값이 묶이게 될 userInfo 객체
const [userInfo, setUserInfo] = useState({
classCount: 0,
...
});
const onChangeClassesCount = (e) => {
let tmp = parseInt(e.target.value);
setUserInfo({
// userInfo를 복사하고, 업데이트되는 프로퍼티의 항목만 수정
...userInfo,
classCount: tmp
});
};
[객체의 프로퍼티 value가 객체인 경우]
const [userInfo, setUserInfo] = useState({
inquirer : {
name : "",
}
});
const onChangeName = (e) => {
if(e.target.value.length > 10) {
return;
}
// 콜백함수(prevState)를 만들어 이전의 userInfo를 복사
setUserInfo((prevState) => ({
...prevState,
// 복사된 userinfo의 inquirer 항목을 덮어 씌움
inquirer: {
...prevState.inquirer,
name: e.target.value,
}
}));
}
6. state를 객체로 관리하고 업데이트할 수 있다.
const Navigation = ({higlightLine}) => {
const [fontWeight, setFontWeight] = useState(
{
_1: '400',
_2: '400',
});
const bottomLineShow = (e) => {
if(e.target.className === 'scratchAI' || e.target.className === 'bottomLine1') {
setFontWeight({...fontWeight, _1: 'bold' });
7. 스크롤을 이동시키는 방법
import { Link as LinkToRegsiter } from 'react-scroll';
const [ref, setRef] = useState('cityTown');
if (userInfo.inquirer.name === '') {
setRef('applicant');
}
// 이동하고 싶은 컴포넌트에 id 지정
<div id="applicant"></div>
// LinkToRegister의 to에 이동하고 싶은 컴포넌트의 id값을 입력
<LinkToRegsiter
className="submitBtn"
onClick={submitHandler}
to={ref}
smooth={true}
>
<span>도입 문의 하기</span>
</LinkToRegsiter>
댓글남기기