작성
·
122
0
제가 백엔드 강의는 수강한 적이 없어서요, 대신 노드js 교과서 책을 구매해서 가지고 있는데..
우선 프론트는 네트리파이로 배포 완료했습니다
https://admirable-donut-f22cc6.netlify.app/
백엔드 배포는 선생님 책 노드js 교과서 722쪽 AWS 배포하기 부터 보면서 하면 별 문제없지 진행할 수 있을까요? 추가적으로 백엔드쪽 코드 수정이 필요할지..
배포할 레포 구조는 아래 처럼 루트 폴더 하위에 백엔드, 프론트 폴더 각각 있습니다
답변 2
0
저는 제 프로젝트를 배포하는 작업을 진행 중인데, Node.js 교재에서 제공하는 AWS를 이용한 백엔드 배포 방법을 따르고 있습니다. 설명은 직관적이지만 실제로 설정할 때 문제가 생길까봐 걱정이 됩니다. 프론트엔드는 이미 Netlify에 배포되어 있기 때문에 백엔드 코드가 큰 변경 없이 잘 작동할지 확인하고 싶습니다.
그런데 다른 호스팅 옵션을 찾아보다가 Vultr을 발견했어요. 백엔드 호스팅을 위해 정말 좋은 선택인 것 같습니다. 최적화된 VPS 호스팅, 자동 백업, 보안 데이터베이스 관리 등 유용한 기능이 많이 있어서 훨씬 더 쉬운 작업을 할 수 있게 되더군요.
Node.js 앱을 작업 중이거나 MySQL 데이터베이스에 신뢰할 수 있는 호스팅이 필요하다면 Vultr을 추천드립니다. 특히 Managed Databases 서비스는 데이터베이스 관리를 정말 쉽게 해주기 때문에 유용하게 사용할 수 있습니다.
Vultr에서 MySQL을 설치하는 데 유용한 가이드를 공유할게요:
저는 이제 백엔드 배포를 위해 Vultr로 전환할 생각을 하고 있는데, 이 서비스를 사용해보면 여러분에게도 정말 좋은 선택이 될 거라고 확신합니다!
0
백엔드쪽은 책 따라서 하시면 되는데요. cors는 전체 허용을 해두셔야 합니다. 프론트 백엔드 도메인이 서로 다를 것 같아서요. 그래서 로그인 시 쿠키가 전달 안 되는 문제가 있을 수도 있습니다. 쿠키 대신 토큰 로그인을 하는 게 좋으나 책에는 그 부분은 없습니다.
라이트세일 우분투 고르셨나요? 우분투라면 아래 링크 방법으로 설치 가능합니다.
https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-20-04
데비안같긴 한데 데비안이라면
https://www.digitalocean.com/community/tutorials/how-to-install-the-latest-mysql-on-debian-10
입니다.
https://www.digitalocean.com/community/tutorials/how-to-install-the-latest-mysql-on-debian-10
데비안 기준으로 알려주신 위 링크에서 진행 중 아래 단계에서 에러가 나서요
에러 화면
한 5시간째 gpt, 구글링 다 해보고 있는데 mysql 설치가 안되네요 ㅜ
데비안 버전 문제로 보이는데요. 중간에 wget 쪽에 문자열을 아래처럼 최신 deb 파일로 수정한 뒤 진행하시면 될겁니다. (라이트세일에서 확인완료)
wget https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb
최신 deb 파일로 하니 설치는 잘 되었는는데
이후 mysql 비밀번호 설정하고 git clone 받아서
아파치 서버 종료하고
클론 받은 폴더에서 패키지 설치 후 npm sequelize db:create --ene production 하
access denied for user 'root'@'localhost' 에러가 나서요
<백엔드 코드>
app.js
const express = require("express");
const dotenv = require("dotenv");
const morgan = require("morgan");
const session = require("express-session");
const cookieParser = require("cookie-parser");
const cors = require("cors");
const path = require("path");
const hpp = require("hpp");
const helmet = require("helmet");
const passport = require("passport");
dotenv.config();
const { sequelize } = require("./models");
const passportConfig = require("./passport");
const apiRouter = require("./routes/api");
const webSocket = require("./socket");
const app = express();
app.set("PORT", process.env.PORT || 3095);
sequelize
.sync()
.then(() => {
console.log("DB 연결 성공");
})
.catch(console.error);
passportConfig();
const prod = process.env.NODE_ENV === "production";
if (prod) {
app.enable("trust proxy");
app.use(morgan("combined"));
app.use(helmet({ contentSecurityPolicy: false }));
app.use(hpp());
} else {
app.use(morgan("dev"));
app.use(
cors({
origin: true,
credentials: true,
webSocket: true,
})
);
}
app.use(express.static(path.join(__dirname, "public")));
app.use("/uploads", express.static(path.join(__dirname, "uploads")));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser(process.env.COOKIE_SECRET));
const sessionOption = {
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
},
};
if (prod) {
sessionOption.cookie.secure = true;
sessionOption.cookie.proxy = true;
}
app.use(session(sessionOption));
app.use(passport.initialize());
app.use(passport.session());
app.use("/api", apiRouter);
app.get("*", (req, res, next) => {
res.sendFile(path.join(__dirname, "public", "index.html"));
});
const server = app.listen(app.get("PORT"), () => {
console.log(`listening on port ${app.get("PORT")}`);
});
webSocket(server, app);
config.js
require('dotenv').config();
module.exports = {
"development": {
"username": "root",
"password": process.env.SEQUELIZE_PASSWORD,
"database": "sleact",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "root",
"password": process.env.SEQUELIZE_PASSWORD,
"database": "sleact",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": process.env.SEQUELIZE_PASSWORD,
"database": "sleact",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
.env
COOKIE_SECRET=sleactcookie
MYSQL_PASSWORD=jsmaster
SEQUELIZE_PASSWORD=jsmaster
package.json
{
"name": "sleact-ts-back",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"dev": "nodemon app",
"start": "cross-env NODE_ENV=production PORT=80 pm2 start app.js"
},
"author": "ZeroCho",
"license": "MIT",
"dependencies": {
"bcrypt": "^5.1.0",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"express-session": "^1.17.1",
"helmet": "^4.4.1",
"hpp": "^0.2.3",
"morgan": "^1.10.0",
"multer": "^1.4.2",
"mysql2": "^2.2.5",
"nodemon": "^2.0.7",
"passport": "^0.6.0",
"passport-local": "^1.0.0",
"pm2": "^4.5.4",
"sequelize": "^6.5.0",
"sequelize-cli": "^6.2.0",
"socket.io": "^4.7.5"
}
}
앗 .env도 ignore에 포함됐었군요 ㅜ .env도 추가하니 잘 실행 완료되었는데
회원가입에서 500 에러가 나고 있습니다 ㅜ
app.js:2 POST http://43.201.1.136/api/users 500 (Internal Server Error)
<회원가입 클릭 시 코드>
const onSubmit = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
if (!missmatchError && nickname) {
console.log('회원가입 시도');
setSignUpError('');
setSignUpSuccess(false);
// localhost 3090(프론트)가 3095(백엔드)로 보내는 요청
axios
.post('/api/users', {
email,
nickname,
password,
})
.then(() => {
setSignUpSuccess(true);
})
.catch((error) => {
console.log(error.response);
setSignUpError(error.response.data);
})
.finally(() => {});
}
},
[email, nickname, password, passwordCheck, missmatchError],
);
http://43.201.1.136/api/users 주소 접속해보면 정상적으로 보이는데.. 어디서 잘못되었는지 모르겠습니다
500 에러(Internal Server Error)의 경우에는 사용자가 아닌 서버 자체의 오류로 인해서 사이트의 모든 페이지에 접근이 불가능한 상태가 발생이라고 하는
서버쪽 코드를 수정해야할까요?
브라우저에 /api/users 치는 건 GET이고요. 지금 에러가 나는건 POST입니다. 서버에서 에러 로그 확인해서(pm2 monit) 에러 메시지 따라서 수정해야 합니다.
혹시 sequelize db:create 같은 명령어 수행하셨나요? 데이터베이스, 테이블이 생성되어 있어야 합니다.
넵 데이터는 npx sequelize db:create --env production 명령어로 생성했습니다
pm2 monit 실행 했을 때는 아무 내용이 안 나오더라구요
이상한게 회원가입 1번째 클릭엔 500에러인데 2번쨰 클릭에는 이미 사용 중인 아이디라고 403 에러가 뜨고 있습니다
회원가입 정보 db는 추가되고 있는 것 같아요
회원가입 실패한 정보로 로그인 시도를 해보면 200 코드로 나오고 있습니다
db를 초기화해야 할까요?
cannot read properties of null(reading 'addMembers') 라고 나오고 있습니다
해당 코드 부분
강의 초반에 아래 단계를 진행했었는데
npm i npx sequelize db:create // mysql db에 sleact 데이터(로우) 생성
npx sequelize db:seed:all // 워크스페이스 > 채널에 가짜 데이터 생성
npm run dev // 테이블 생성
여기서도 똑같이 npx sequelize db:seed:all 이것도 해줘야 하는건가요?
아 이제 회원가입은 되는데 로그인이 안 되네요... ㅋㅋㅋ
login response 값으로 {"id":11,"nickname":"qwer","email":"qwer"} 잘 나오는데
user response 값은 false로 나오고 있습니다
<Login.tsx 코드>
const Login = () => {
// useSWR은 get으로 요청한 데이터를 받아와서 저장한다.
// mutate : 내가 원할 때 SWR 호출하기
const { data, error, mutate } = useSWR('/api/users', fetcher, {
dedupingInterval: 5000, // 주기적으로 호출하지만, dedupingInterval 기간 내에는 캐시에서 불러온다
});
const [logInError, setLogInError] = useState(false);
const [email, setEmail, onChangeEmail] = useInput('');
const [password, setPassword] = useInput<string>('');
const onChangePassword = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setPassword(e.target.value);
},
[email, password, data],
);
const onSubmit = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
setLogInError(false);
axios
.post(
'/api/users/login',
{ email, password },
{
withCredentials: true,
},
)
.then(() => {
mutate();
})
.catch((error) => {
setLogInError(error.response?.status === 401);
});
},
[email, password],
);
if (data) return <Navigate to="/workspace/sleact/channel/일반" />;
return (
<div className="max-w-[400px] mx-auto px-[20px]">
<h1 className="flex flex-col items-center justify-center pt-[60px] pb-[20px]">
<LogoChat color="#444791" />
<span className="mt-[10px] text-primary text-[20px] font-bold">ReChat</span>
<span className="blind">Slack</span>
</h1>
<TextField label="이메일 주소" type="email" value={email} onChange={onChangeEmail} />
<TextField label="비밀번호" type="password" value={password} onChange={onChangePassword} />
{logInError && <p className="mb-[20px] mt-[-10px] text-red-500 font-normal">로그인 실패</p>}
<Button text="로그인" onClick={onSubmit} />
<p className="mt-[10px] text-center">
Slack을 처음 사용하시나요?
<Link to="/sign" className="ml-[4px] text-[#004174]">
회원가입
</Link>
</p>
</div>
);
};
로컬에서 돌릴 땐 아무 문제 없는데..
이건 에러 메세지도 보이지 않네요 어디를 봐야 하는 걸까요?
네 로컬 서버 환경에서 테스트 해보면 로그인 후의 /api/users 데이터를 console.log로 조회해보니 아래와 같이 담겨있습니다
근데 배포 환경에서는 login response 값에 유저 정보가 있어도 로그인이 되지 않아 /api/users 에는 false로 나오고 있습니다
로그인 후에 mutate()를 호출하면 자연스레 /api/users 요청이 다시 가게 됩니다. 네트워크 탭에서 응답을 확인하세요. 그리고 로그인 응답에 쿠키도 정상적으로 심어졌나 확인하시고요.
네트워크 탭에서 응답 확인한 내용입니다
login 요청에는 정보가 있으나 user는 여전히 false 이고
애플리케이션 탭에서도 쿠키가 생성되지 않았습니다
해당 부분 강의도 다시 들어봤는데 swr 사용하기(쿠키 공유하기) 강좌 10분 40초에서 지금 제 상황과 완전 동일한 에러에 대해서 설명해주신 내용대로 프론트 서버와 백엔드 서버의 도메인이 다를 경우 쿠키 생성도 안되고 데이터 전달도 안될 때 해야하는 설정 withCredentials: true 옵션도 되어 있습니다.
근데 지금 배포 환경에서는 프론트와 백엔드 도메인도 동일한 상황이라 위 이슈와는 연관은 없어보여서요. Login.tsx 코드를 수정해야할 게 있을까요?
<Login.tsx 코드>
const Login = () => {
// useSWR은 get으로 요청한 데이터를 받아와서 저장한다.
// mutate : 내가 원할 때 SWR 호출하기
const { data, error, mutate } = useSWR('/api/users', fetcher, {
dedupingInterval: 5000, // 주기적으로 호출하지만, dedupingInterval 기간 내에는 캐시에서 불러온다
});
const [logInError, setLogInError] = useState(false);
const [email, setEmail, onChangeEmail] = useInput('');
const [password, setPassword] = useInput<string>('');
const onChangePassword = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setPassword(e.target.value);
},
[email, password, data],
);
const onSubmit = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
setLogInError(false);
axios
.post(
'/api/users/login',
{ email, password },
{
withCredentials: true,
},
)
.then(() => {
mutate();
})
.catch((error) => {
setLogInError(error.response?.status === 401);
});
},
[email, password],
);
if (data) return <Navigate to="/workspace/sleact/channel/일반" />;
return (
<div className="max-w-[400px] mx-auto px-[20px]">
<h1 className="flex flex-col items-center justify-center pt-[60px] pb-[20px]">
<LogoChat color="#444791" />
<span className="mt-[10px] text-primary text-[20px] font-bold">ReChat</span>
<span className="blind">Slack</span>
</h1>
<TextField label="이메일 주소" type="email" value={email} onChange={onChangeEmail} />
<TextField label="비밀번호" type="password" value={password} onChange={onChangePassword} />
{logInError && <p className="mb-[20px] mt-[-10px] text-red-500 font-normal">로그인 실패</p>}
<Button text="로그인" onClick={onSubmit} />
<p className="mt-[10px] text-center">
Slack을 처음 사용하시나요?
<Link to="/sign" className="ml-[4px] text-[#004174]">
회원가입
</Link>
</p>
</div>
);
};
근데 로그인 후에 401 (Unauthorized) 에러가 뜨면서 갑자기 로그아웃 됐다가 알아서 다시 로그인 됐다를 반복하는데.. 이것도 쿠키 문제인가요?
채널리스트나 유저리스트도 간헐적으로 못 불러오는 것 같아요
AWS lightsail에서 mysql-server 설치하는데 에러가 나서요
책 내용 중
$ sudo apt-get update
$ sudo apt-get install -y gnupg
$ sudo wget https://dev.mysql.com/get/mysql-apt-config_0.8.23-1_all.deb
$ sudo dpkg -i mysql-apt-config_0.8.23-1_all.deb
$ sudo apt update
$ sudo apt-get install -y mysql-server -> 여기서 에러
에러 메세지 -> lightsail mysql server has no installation candidate
비번 설정하시고, Use Legacy Authentication Method 선택하세요.
$ sudo mysql -uroot -p
인스턴스를 지우고 다시 해봐도 똑같습니다
강좌에 백엔드 배포 영상도 있으면 좋을텐데 아쉽네요 ㅜ