npm install firebase
npm install @reduxjs/toolkit react-redux
firebase
- 프로젝트 생성
- authentication
- email/password
선택firebase.js
파일 생성import "firebase/compat/auth";
import firebase from "firebase/compat/app";
const firebaseConfig = {
apiKey: "~",
authDomain: "~",
projectId: "~",
storageBucket: "~",
messagingSenderId: "~",
appId: "~",
};
firebase.initializeApp(firebaseConfig);
export default firebase;
🔗 [firebase 공식문서] Get Started with Firebase Authentication on Websites
[Client]
// firebase 회원가입 기능
const RegisterFunc = async (e) => {
e.preventDefault();
let createdUser = await firebase
.auth()
.createUserWithEmailAndPassword(email, pw);
await createdUser.user.updateProfile({
displayName: name,
});
let body = {
email: createdUser.user.multiFactor.user.email,
displayName: createdUser.user.multiFactor.user.displayName,
uid: createdUser.user.multiFactor.user.uid,
};
axios.post("/api/user/register", body).then((response) => {
setFlag(false); // 회원가입이 완료되면 회원가입 버튼 다시 활성화해주기
if (response.data.success) {
navigate("/login");
} else {
return alert("회원가입에 실패하였습니다.");
}
});
};
[Server]
먼저 UserSchema 생성해주기
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema(
{
userNum: Number,
email: String,
displayName: String,
uid: String,
},
{ collection: "users" }
);
const User = mongoose.model("User", userSchema);
module.exports = { User };
// 회원가입
router.post("/register", (req, res) => {
let temp = req.body;
Counter.findOne({ name: "counter" })
.then((doc) => {
temp.userNum = doc.userNum;
const userData = new User(req.body);
userData.save().then(() => {
Counter.updateOne({ name: "counter" }, { $inc: { userNum: 1 } }).then(
() => {
res.status(200).json({ success: true });
}
);
});
})
.catch((err) => {
res.status(400).json({ success: false });
});
});
[Client]
const NameCheckFunc = (e) => {
e.preventDefault();
if (!name) {
return alert("닉네임을 입력해주세요.");
}
let body = { displayName: name };
axios.post("/api/user/namecheck", body).then((response) => {
if (response.data.success) {
if (response.data.check) {
setNameCheck(true);
setNameInfo("사용가능한 닉네임입니다.");
} else {
setNameInfo("사용불가능한 닉네임입니다.");
}
}
});
};
[Server]
router.post("/namecheck", (req, res) => {
User.findOne({ displayName: req.body.displayName })
.exec()
.then((doc) => {
let check = true;
if (doc) check = false;
res.status(200).json({ success: true, check });
})
.catch((err) => {
res.status(400).json({ success: false });
});
});
// firebase 로그인 인증
const SignInFunc = async (e) => {
e.preventDefault();
try {
await firebase.auth().signInWithEmailAndPassword(email, pw);
navigate("/");
} catch (err) {
console.log(err.code);
if (err.code === "auth/user-not-found") {
setErrorMsg("존재하지 않는 이메일입니다.");
} else if (err.code === "auth/wrong-password") {
setErrorMsg("비밀번호가 일치하지 않습니다.");
} else {
setErrorMsg("로그인에 실패하였습니다.");
}
}
🔗 [redux-toolkit 공식문서] Quick Start | Redux Toolkit
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
displayName: "",
uid: "",
accessToken: "",
};
export const userSlice = createSlice({
name: "user",
initialState,
reducers: {
loginUser: (state, action) => {
state.displayName = action.payload.displayName;
state.uid = action.payload.uid;
state.accessToken = action.payload.accessToken;
},
clearUser: (state) => {
state.displayName = "";
state.uid = "";
state.accessToken = "";
},
},
});
export const { loginUser, clearUser } = userSlice.actions;
export default userSlice.reducer;
modal 외부 클릭시 닫히도록 hooks 사용
🔗 [usehooks 공식문서] useOnClickOutside
const [modalFlag, setModalFlag] = useState(false);
useOnClickOutside(ref, () => setModalFlag(false));
function useOnClickOutside(ref, handler) {
useEffect(() => {
const listener = (event) => {
if (!ref.current || ref.current.contains(event.target)) {
return;
}
handler(event);
};
document.addEventListener("mousedown", listener);
document.addEventListener("touchstart", listener);
return () => {
document.removeEventListener("mousedown", listener);
document.removeEventListener("touchstart", listener);
};
}, [ref, handler]);
}
const ref = useRef();
// 모달 사용시 모달 태그쪽에 ref 추가해주기
{modalFlag && (<div ref={ref}>...</div>)}
Create new app
컴터에 Heroku CLI
설치
🔗 [사용가이드] The Heroku CLI
# macOS는 아래 명령어를 통해 heroku를 설치해준다.
brew tap heroku/brew && brew install heroku
config
폴더 → key
관리
// production.js 파일을 생성해서 key들을 아래와 같이 설정해준다
module.exports = {
mongoURI: process.env.MONGO_URI,
access_key: process.env.S3_KEY,
secret_key: process.env.S3_SECRET,
};
heroku app dashboard
로 가서 Settings
탭 - Config Vars
에서 key
들 입력해주기
process.env.~
뒤에 작성했던 KEY
와 해당하는 VALUE
입력server/index.js
파일의 port
재설정
// HEROKU로 배포시 포트가 무조건적으로 설정한 4000이 아닐 수 있기 때문에 아래와 같이 설정
const port = process.env.PORT || 4000;
폴더 구조 변경
# 변경 전
ㄴclient
ㄴserver
본래 client 폴더를 server 폴더로 이동시키고, 본래 server 폴더 이름을 App으로 변경한다.
이후, App 폴더 내에 새로운 server 폴더를 생성해서 원래 server 폴더에 만들었던
config, Router, Model, Util 폴더를 새로 생성한 server 폴더에 넣어준다.
# 변경 후
App
ㄴclient
ㄴserver
server/index.js
파일 내에 import
경로 변경 & gitignore
내 파일 경로 변경
App
폴더 내에 Profile
파일 생성
web: node index.js
server/package.json
- script
- start
명령어 변경해주고, 커밋까지 해주기!
// 변경 전
"start": "nodemon index.js"
// 변경 후
"start": "node index.js"
// 편의를 위해 nodemon으로 명령어를 변경했던 것을 다시 node로 변경하기
heroku app dashboard
로 가서 Deploy
탭으로 이동해서 명령어 확인 후, 실행
$ heroku login
# 터미널에서 아무키나 누른 후, heroku 사이트에서 로그인 버튼 한번 클릭해주면 로그인 완료
$ heroku git:remote -a go-street
$ git subtree push --prefix App heroku main
이렇게하면 배포 성공 🎃
이제 부가기능 추가하고, 디자인 틈틈히 수정하자~!