이번 항해99 5주차는 좀더 심화된 리액트 개념으로 개인 미니로젝트를 만들어보는것이었다.
프로젝트 기간 : 2021-07-05 ~ 2012-07-08
프로젝트명 : 이미지커뮤니티 ( 가짜스타그램 )
사용 기술 : 리액트, 파이어베이스
사용 IDE : VS Code
프로젝트 설명 : 유저들이 이미지와 설명글을 업로드하여 다른 유저들에게 공유
기능 : 회원가입 및 로그인, 이미지를 포함한 게시글 업로드 및 수정, 게시글 업로드시 세가지 종류의 노출 레이아웃 설정
const signupFB = (id, pwd, userName) => {
return function (dispatch, getState, { history }) {
auth
.createUserWithEmailAndPassword(id, pwd)
.then((user) => {
auth.currentUser
.updateProfile({
displayName: userName,
})
.then(() => {
dispatch(
setUser({ user_name: userName, id: id, user_profile: "" })
);
history.push("/");
})
.catch((error) => {
console.log(error);
});
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorCode, errorMessage);
});
};
};
const loginFB = (id, pwd) => {
return function (dispatch, getState, { history }) {
auth.setPersistence(firebase.auth.Auth.Persistence.SESSION).then((res) => {
auth
.signInWithEmailAndPassword(id, pwd)
.then((user) => {
console.log(user);
dispatch(
setUser({
user_name: user.user.displayName,
id: id,
user_profile: "",
uid: user.user.uid,
})
);
history.push("/");
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
console.log("error : ", error);
if (error.code === "auth/invalid-email") {
alert("아이디를 이메일 형식으로 입력해주세요");
} else if (error.code === "auth/user-not-found") {
alert("존재하는 아이디가 없습니다.");
} else if (error.code === "auth/wrong-password") {
alert("비밀번호가 일치하지 않습니다.");
}
console.log(errorCode, errorMessage);
});
});
};
};
로그인 시 아이디와 패스워드 파라미터를 firebase 로그인 저장소에 넘겨서 만약 일치한다면 리덕스 저장소에 해당 유저 정보 저장한 후 메인페이지로 이동
일치하는 정보가 없다면 에러코드별로 alert 출력
게시글 내용 작성 전
게시글 내용 작성 후
const selectFile = (e) => {
const reader = new FileReader();
const file = e.target.files[0];
reader.readAsDataURL(file);
setUploadFile(true);
reader.onloadend = () => {
dispatch(imageActions.setPreview(reader.result));
setUploadedFile(file);
};
setHideUploadBtn(true);
};
<FormControl component="fieldset" style={{ marginTop: "30px" }}>
<FormLabel component="legend">레이아웃 (택1)</FormLabel>
<RadioGroup
aria-label="layout"
name="layout"
value={radioValue}
onChange={handleChange}
>
<FormControlLabel
value="post1"
control={<Radio selected />}
label="텍스트 상단 이미지 하단형"
/>
<FormControlLabel
value="post2"
control={<Radio />}
label="텍스트 오른편 이미지 왼편형"
/>
<FormControlLabel
value="post3"
control={<Radio />}
label="텍스트 왼편 이미지 오른편형"
/>
</RadioGroup>
</FormControl>
function uploadImageFB(image) {
return function (dispatch, getState, { history }) {
dispatch(uploading(true));
console.log(`images/${new Date().getTime()}_${image.name}`);
const _upload = storage.ref(`images/${image.name}`).put(image);
// 업로드!
_upload
.then((snapshot) => {
console.log(snapshot);
snapshot.ref.getDownloadURL().then((url) => {
console.log(url);
dispatch(uploadImage(url));
});
})
.catch((err) => {
dispatch(uploading(false));
});
};
}
2.이미지 업로드 시 firebase 내 이미지 저장소에 저장 후 이미지 url 추출
<Grid style={{ margin: "30px 0" }}>
<Typography variant="h4" style={{ fontWeight: 500 }}>
미리보기
</Typography>
{(function () {
if (radioValue === "post1") {
return (
<Post1Box
contents={textContent}
image_url={
preview ? preview : "http://via.placeholder.com/400x300"
}
></Post1Box>
);
} else if (radioValue === "post2") {
return (
<Post2Box
contents={textContent}
image_url={
preview ? preview : "http://via.placeholder.com/400x300"
}
></Post2Box>
);
} else if (radioValue === "post3") {
return (
<Post3Box
contents={textContent}
image_url={
preview ? preview : "http://via.placeholder.com/400x300"
}
></Post3Box>
);
}
})()}
</Grid>
const addPostFB = (contents = "", type = "post1") => {
return function (dispatch, getState, { history }) {
const postDB = firestore.collection("post");
const _user = getState().user.user;
const _image = getState().image.preview;
const user_info = {
user_name: _user.user_name,
user_id: _user.uid,
user_profile: _user.user_profile,
};
const _upload = storage
.ref(`images/${user_info.user_id}_${new Date().getTime()}`)
.putString(_image, "data_url");
const _post = {
...initialPost,
contents: contents,
type: type,
insert_dt: moment().format("YYYY-MM-DD hh:mm:ss"),
};
_upload
.then((snapshot) => {
snapshot.ref
.getDownloadURL()
.then((url) => {
console.log(url);
dispatch(imageActions.uploadImage(url));
return url;
})
.then((url) => {
console.log(url);
postDB
.add({ ...user_info, ..._post, image_url: url })
.then((doc) => {
let post = {
user_info,
..._post,
id: doc.id,
image_url: url,
};
dispatch(addPost(post));
dispatch(imageActions.deletePreview());
history.replace("/");
})
.catch((err) => {
window.alert("앗! 포스트 작성에 문제가 있어요!");
console.log("post 작성 실패!", err);
});
});
})
.catch((err) => {
window.alert("앗! 이미지 업로드에 문제가 있어요!");
console.log(err);
});
};
};
const getPostFB = (start = null, size = 3) => {
return function (dispatch, getState, { history }) {
let _paging = getState().post.paging;
if (_paging.start && !_paging.next) {
return;
}
const postDB = firestore.collection("post");
let query = postDB.orderBy("insert_dt", "desc");
if (start) {
query = query.startAt(start);
}
query
.limit(size + 1)
.get()
.then((docs) => {
console.log("docs : ", docs.docs);
let post_list = [];
docs.forEach((doc) => {
let _post = doc.data();
let post = Object.keys(_post).reduce(
(acc, cur) => {
if (cur.indexOf("user_") !== -1) {
return {
...acc,
user_info: { ...acc.user_info, [cur]: _post[cur] },
};
}
return { ...acc, [cur]: _post[cur] };
},
{ id: doc.id, user_info: {} }
);
post_list.push(post);
});
// 마지막 하나는 빼줍니다.
// 그래야 size대로 리스트가 추가되니까요!
// 마지막 데이터는 다음 페이지의 유무를 알려주기 위한 친구일 뿐! 리스트에 들어가지 않아요!
post_list.pop();
dispatch(setPost(post_list));
});
};
};
const editPostFB = (post_id = null, post = {}) => {
return function (dispatch, getState, { history }) {
if (!post_id) {
console.log("게시물 정보가 없어요!");
return;
}
const _image = getState().image.preview;
const _post_idx = getState().post.list.findIndex((p) => p.id === post_id);
const _post = getState().post.list[_post_idx];
const postDB = firestore.collection("post");
if (_image === _post.image_url) {
postDB
.doc(post_id)
.update(post)
.then((doc) => {
dispatch(editPost(post_id, { ...post }));
history.replace("/");
});
return;
} else {
const user_id = getState().user.user.uid;
const _upload = storage
.ref(`images/${user_id}_${new Date().getTime()}`)
.putString(_image, "data_url");
_upload.then((snapshot) => {
snapshot.ref
.getDownloadURL()
.then((url) => {
console.log(url);
return url;
})
.then((url) => {
postDB
.doc(post_id)
.update({ ...post, image_url: url })
.then((doc) => {
dispatch(editPost(post_id, { ...post, image_url: url }));
dispatch(imageActions.deletePreview());
history.replace("/");
});
})
.catch((err) => {
window.alert("앗! 이미지 업로드에 문제가 있어요!");
console.log("앗! 이미지 업로드에 문제가 있어요!", err);
});
});
}
};
};
저번주에 배웠던 리액트 기본 개념에서 더 나아가 좀 더 깊은 리덕스 개념들을 사용하면서 로그인 및 회원가입, 게시글 등록 및 수정등 여러 기능들을 사용하면서 프로젝트를 진행해보았다.
아직 손에 많이 익질 않았지만, 매우 재미있게 개발을 했던것 같다. 개발이라는것은 처음에는 참 이해가 안가고 어렵지만 프로젝트를 진행하면서 많은 고민을 하면서 적용을 하다보면 갑작스럽게 이해가 가고 손에 익는 계기가 오게되는것 같다. 이러한 과정이 참 뿌듯하고 재미있는 것 같다.
항해99를 하면서 여러 프로젝트를 만들어 실무적인 감각을 키울 수 있다는 것에 다시 한번 감사하고
이 기회를 최대한 활용하여 나의 리액트 주특기 감각을 최대치로 끌어올려 봐야겠다고 생각한다.
github url
- https://github.com/zlzlzlmo/image_community