일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- Babel과 Webpack
- 자바스크립트
- 모던 자바스크립트 Deep Dive
- 학습내용정리
- 브라우저의 렌더링 과정
- js pattern
- mixin pattern
- 프론트엔드 성능 최적화 가이드
- 제너레이터와 async/await
- 스코프
- 자바스크립트 패턴
- 자바스크립트 딥다이브
- js pattern
- package management
- const
- pr review
- 올림픽 통계 서비스 최적화
- 이벤트
- unique identifiers
- 블로그 서비스 최적화
- 진행기록
- 새 코드 받아오기
- 이미지 갤러리 최적화
- peerdependencies
- 딥다이브
- 커리어
- DOM
- version management
- 프로그래머스
- middleware pattern
- Today
- Total
Dev Blog
20210307 19일차 그림판/일기장 로직 구현(85% 완성) 본문
수도 로직
Paint Page: 그림그리기 완료 여부를 상태끌어올리기로 app 으로 올리고 diary 페이지에 props로 전달.
- Diaryinfo component: 날씨 정보 저장 및 상태끌어올리기로 page 로 전달
- Title: isLogin 상태에 따라 (손님) 의 행복한 하루 또는 (유저이름) 의 행복한 하루 로 구현
- Cpaint component: 그림의 좌표 데이터/url 을 저장하고 이를 diary 에 전달.
실제 구현
1. 로그인, 로그아웃, 회원가입 코드 리팩토링. 로그인 상태를 최상위의 app.tsx 에서 관리
로그인, 로그아웃 로직과 회원가입 로직은 이미 끝낸 상태였지만,
회원가입 후 자동 로그인된 상황에서 로그아웃을 했을때 메인페이지의 작동이 잘 안되는 현상이 발생했다. (로그아웃 했는데 로그아웃 버튼이 존재하고 모든 네이게이션 버튼이 존재하는 상황)
결론적으로는, 부모를 app.tsx 로 두고 로그인, 로그아웃, 회원가입 시 변화하는 로그인 상태를 관리하도록 구현했다.
회원가입을 하면 로그인 상태가 변화하는데 changeSignin 이라는 함수를 이용하여 상태끌어올리기를 한다.
로그인, 로그아웃도 마찬가지로 이 함수를 사용하는데, isSignin 상태를 자식에게 전달하는 점이 다르다. 이는 자식 컴포넌트에서 로그인 유무에 따라 보여줘야 하는 엘리먼트가 다르기 때문에 이 isSignin 이 true 냐 false 냐가 기준이 된다.
function App(): ReactElement {
const [isSignin, setSignin] = useState(false);
const [weatherData, setWeatherData] = useState("");
const [imgUrl, setImgUrl] = useState("");
const [imgData, setImgData] = useState("");
const changeSignin = (e: boolean) => {
console.log(e);
setSignin(e);
};
const changeWeather = (e: string) => {
// console.log(e);
setWeatherData(e);
};
const changeImgUrl = (e: string) => {
// console.log(e);
setImgUrl(e);
};
const changeImgData = (e: string) => {
// console.log(e);
setImgData(e);
};
return (
<Router>
<Main>
<Switch>
<Route exact path="/">
<Manual />
<SignIn changeSignin={changeSignin} isSignin={isSignin} />
</Route>
<Route exact path="/signup">
<Manual />
<Application changeSignin={changeSignin} />
</Route>
<Route exact path="/creatediary">
<Paint changeWeather={changeWeather} changeImgUrl={changeImgUrl} changeImgData={changeImgData} />
<Diary weatherData={weatherData} imgUrl={imgUrl} imgData={imgData} />
</Route>
2. 그림판, 일기장 로직 구현
그림판 컴포넌트에서 날씨 데이터, 이미지 url, 이미지 좌표 데이터를 diary 페이지에 전달하는데, 이는 모든 데이터가 제출되었을 때 diary 의 완료버튼이 작동할 수 있도록 하기 위해서이다. 즉, 이 데이터들을 상태끌어올리기 및 diary 에 전달하고,
diary 에서의 제목, 내용 작성, 기분 까지 선택되었을 때 완료버튼이 작동하여 서버에 API 를 통해 제출하게 된다.
예) 다이어리에 그림판 데이터를 전달하는 코드
interface paintDataProps {
weatherData: string;
imgUrl: string;
imgData: string;
}
export default function Diary(props: paintDataProps): ReactElement {
const { weatherData, imgUrl, imgData } = props;
const history = useHistory();
const [isPublic, setIsPublic] = useState(false);
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [emotion, setEmotion] = useState("");
예) 완료버튼을 눌렀을때 작동하는 제출코드. 그림 완료 버튼을 눌러야 이미지 url 과 좌표데이터가 전달된다.
const handleSubmit = async () => {
if (weatherData === "") {
console.log("날씨를 선택해주세요");
} else if (imgUrl === "" || imgData === "") {
console.log("그림 완료 버튼을 누르세요");
} else if (title === "") {
console.log("제목을 써주세요");
} else if (content === "") {
console.log("내용을 써주세요");
} else if (emotion === "") {
console.log("기분을 선택해주세요");
}
return;
await axios.post("https://royal-diary.ml/contents/ccontent", {
3. 그림 PNG 이미지 multer 에 송부 및 이미지 URL 리턴 작동
관련코드는 아래와 같다. 비동기로 받아오기 때문에 하나하나 변수에 담아 리턴하는 방식으로 구현했다. 이해가 편하다.
diary 페이지에 전달하는 그림 데이터는 2가지이다.
1. multer 로 S3 버켓에 그림을 업로드하고 리턴받는 Image Url => 일기보기 page 에서 사용
예) royaldiarymulter.s3.ap-northeast-2.amazonaws.com/1616116451513
2. 그림의 좌표데이터 => 그림 수정시 사용(되돌리기 기능이 가능)
예)
async function dataURLtoFile(dataurl: string) {
const blobBin = atob(dataurl.split(",")[1]); // base64 데이터 디코딩
const array = [];
for (let i = 0; i < blobBin.length; i += 1) {
array.push(blobBin.charCodeAt(i));
}
const u8arr = new Uint8Array(array);
const file = new Blob([u8arr], { type: "image/png" }); // Blob 객체 생성
const formdata = new FormData(); // formData 생성
formdata.append("img", file); // formdata에 file data 추가
// axios 로 서버에 img 파일 보내기
// 유알엘을 리턴하여 saveAsPNG 에서 사용할 수 있도록!!
const imgUrl = await axios
.post("https://royal-diary.ml/image", formdata, {
headers: { "content-Type": "multipart/form-data" },
})
.then((res) => {
const returnedUrl = res.data.imgUrl;
return returnedUrl;
})
.catch((err) => {
console.log("server error occured");
});
return imgUrl;
}
const saveAsPNG = async () => {
const canvas = document.querySelector(".CanvasDraw canvas:nth-child(2)") as HTMLCanvasElement;
const imgUrl = canvas.toDataURL("image/png");//canvas Element 에 그려진 내용을 Data URL로 변환
const returnedUrl = await dataURLtoFile(imgUrl);//S3버켓에 파일업로드 및 이미지URL을 받는 메소드로 Data URL 전달
// 서버로부터 리턴받은 url 을 handleSaveClick 에 전달
return returnedUrl;
/* 이미지 데이터로 원하는 이미지 엘리먼트에 이미지를 만들 수 있다.
const newImage = document.createElement("img");
newImage.src = image;
document.querySelector(Main)?.append(newImage);
*/
/* 이미지 파일을 다운받을수 있다.
downloadImage(image, "my-canvas.png");
*/
};
/* 이미지 파일 다운받는 함수
function downloadImage(data: string, filename: string) {
const a = document.createElement("a");
a.href = data;
a.download = filename;
document.body.appendChild(a);
a.click();
}
*/
const handleSaveClick = async () => {
const data = firstCanvas.current.getSaveData();//그림의 좌표데이터
setImgData(data);//상태(좌표데이터)끌어올리기
const imgUrl = await saveAsPNG();
setImgUrl(imgUrl);//상태()끌어올리기
// multer 에 전송후 받은 url 과 좌표 데이터를 sessionStorage 에 저장해서 글쓰기 완료 버튼을 눌렀을때 사용한다..!
};
시행착오
처음엔 기존에 있던 코드가 아깝다(?)는 마음이 들어서인지, signin page 에 있는 로그인 상태를 사용하여 구현을 시도했다.
하지만, 생각처럼 작동하지가 않았다. 특히, props 가 닿지 않은 곳은 sessionStorage 데이터를 사용하여(props 로 구현하기전, 그림판 데이터들을 이 세션스토리지에 담아 다이어리에 전달하고자 했다.) 해결하려고 했지만, 새로고침이 되어야 다이어리에서 이 데이터들을 읽을 수 있는 문제가 있었다. 그렇다고 새로고침을 하면 다이어리의 내용들이 모두 사라진다.
그래서 결국 최상위에 위치하는 app.tsx 에 로그인 상태를 두었고, 몇가지 함수들을 만들어 그림판 데이터를 다이어리에 전달해 줄 수 있었다.
세션 데이터는 참 좋은 것이지만, 실시간으로 반영하는 것이 불가하다. 관련하여 이 세션 데이터를 사용하는 useEffect 함수에 대해 더 공부가 필요하다. 코드 상 리뷰할 내용이 더욱 많지만, 너무 많아서 추후 정리하도록 한다.
도움 받은 사이트
multer 로 이미지 업로드하기: www.zerocho.com/category/NodeJS/post/5950a6c4f7934c001894ea83
useEffect 사용하기
1) deps 파라미터: react.vlpt.us/basic/16-useEffect.html
2) 공식 문서 번역: medium.com/@dayong/%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85-react-hooks-3-useeffect-6b781a6c6769
3) handle API calls using async await with the useEffect hook: javascript.plainenglish.io/handling-api-calls-using-async-await-in-useeffect-hook-990fb4ae423
4) 여러개의 input 상태관리 하기 (사용하진 않았지만.): velog.io/@zwonlala/Hook-setState-React%EC%97%90%EC%84%9C-%EC%97%AC%EB%9F%AC%EA%B0%9C%EC%9D%98-input-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0
Specifying onClick event type with Typescript and React.Konva:
'BootCamp_Codestates > Final Project' 카테고리의 다른 글
20210309 21일차 그림일기장 수정 로직 구현 (0) | 2021.03.10 |
---|---|
20210308 20일차 그림판/일기장 로직 구현(99% 완성) (0) | 2021.03.09 |
20210306 18일차 로그인/로그아웃 로직 구현 완료 (0) | 2021.03.07 |
20210305 17일차 회원가입 로직 구현 완료 (0) | 2021.03.06 |
20210304 16일차 회원가입 로직 구현(미완) (0) | 2021.03.04 |