inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

[리뉴얼] React로 NodeBird SNS 만들기

CORS 문제 해결하기

user router가 실행되지 않는 문제 질문드리겠습니다.

해결된 질문

408

hib4888

작성한 질문수 47

0

 

아래와 같이 saga도 정상적으로 동작하며, 회원가입 기능, DB에 user 정보 저장, 중복ID체크 오류 모두 정상적으로 동작합니다.

Recipe.io - Chrome 2022-09-10 오후 3_03_00 (3).pngRecipe.io - Chrome 2022-09-10 오후 3_03_27.pngRecipe.io - Chrome 2022-09-10 오후 3_03_32.pngMySQL Workbench 2022-09-10 오후 3_04_50 (2).png회원가입 _ Recipe.io - Chrome 2022-09-10 오후 3_16_33.png근데 network탭을 확인해보니 아래와 같이 user router가 실행되지 않습니다.

또한 cors문제도 해결했는데 Resopnse Headers탭에는 Access-Control-Allow-Origin이 적용되지 않아 질문드립니다.

Recipe.io - Chrome 2022-09-10 오후 3_06_14.pngRecipe.io - Chrome 2022-09-10 오후 3_06_22.png

참고할 수 있는 코드 첨부하겠습니다.

바쁘시겠지만 답변해주시면 정말 감사하겠습니다.

 

<참고 코드>

signupform

import React, { useCallback, useEffect } from 'react';
import { Button, Checkbox, Form, Input, message } from 'antd';
import { useSelector, useDispatch } from 'react-redux';
import Router from 'next/router';

import { formItemLayout, tailFormItemLayout } from './styles';
import { signupRequestAction } from '../../reducers/user';

const SignupForm = () => {
  const { signUpLoading, signUpDone, signUpError } = useSelector((state => state.user));
  const dispatch = useDispatch();
  const [form] = Form.useForm();    

  useEffect(() => {
    if (signUpDone) {
      Router.push('/');
      message.success('정상적으로 회원가입이 완료됬습니다.');
    }
  }, [signUpDone]);

  useEffect(() => {
    if (signUpError) {
      message.error(signUpError);
    }
  }, [signUpError]);

  const onSubmitForm = useCallback((value) => {
    console.log(value);
    dispatch(signupRequestAction(value));
  }, []);

  return (
    <section>
      <Form      
        {...formItemLayout}
        form={form}
        name="signup"
        onFinish={onSubmitForm}      
        scrollToFirstError
      >
        <Form.Item
          name="email"
          label="E-MAIL"
          rules={[
            {
              type: 'email',
              message: 'E-mail형식이 올바르지 않습니다.',
            },
            {
              required: true,
              message: 'E-mail을 입력하세요.',
            },
          ]}
        >
          <Input />
        </Form.Item>

        <Form.Item
          name="password"
          label="Password"
          rules={[
            {
              required: true,
              message: '비밀번호를 입력하세요.',
            },
          ]}
          hasFeedback
        >
          <Input.Password />
        </Form.Item>

        <Form.Item
          name="confirm-password"
          label="Confirm Password"
          dependencies={['password']}
          hasFeedback
          rules={[
            {
              required: true,
              message: '비밀번호를 입력하세요.',
            },
            ({ getFieldValue }) => ({
              validator(_, value) {
                if (!value || getFieldValue('password') === value) {
                  return Promise.resolve();
                }

                return Promise.reject(new Error('비밀번호가 일치하지 않습니다.'));
              },
            }),
          ]}
        >
          <Input.Password />
        </Form.Item>

        <Form.Item
          name="nickname"
          label="Nickname"        
          rules={[
            {
              type: 'text',
            },
            {
              required: true,
              message: '닉네임을 입력하세요.',            
            },
          ]}
        >
          <Input />
        </Form.Item>    
        
        <Form.Item
          name="agreement"
          valuePropName="checked"
          rules={[
            {
              validator: (_, value) =>
                value ? Promise.resolve() : Promise.reject(new Error('약관에 동의해 주세요.')),
            },
          ]}
          {...tailFormItemLayout}
        >
          <Checkbox>
            이용약관에 모두 동의합니다.
          </Checkbox>
        </Form.Item>
        <Form.Item {...tailFormItemLayout}>
          <Button type="primary" htmlType="submit" loading={signUpLoading} >회원가입</Button>
        </Form.Item>
      </Form>
    </section>
  );
};

export default SignupForm;

reducers/user.js

import produce from 'immer';
import { dummyPost } from './post';

export const initialState = {
  me: null,
  signUpData: {},
  loginData: {},  
  logInLoading: false,
  logInDone: false,
  logInError: null,
  logOutLoading: false,
  logOutDone: false,
  logOutError: null,  
  signUpLoading: false,
  signUpDone: false,
  signUpError: null,
  nicknameEditLoading: false,
  nicknameEditDone: false,
  nicknameEditError: null,
};

const dummyUser = (data) => ({
  ...data, // email, password
  nickname: 'Mirrer',
  id: 2,
  Posts: [],
  Scrap: [],
  Board: [],
});

export const LOG_IN_REQUEST = 'LOG_IN_REQUEST';
export const LOG_IN_SUCCESS = 'LOG_IN_SUCCESS';
export const LOG_IN_FAILURE = 'LOG_IN_FAILURE';

export const LOG_OUT_REQUEST = 'LOG_OUT_REQUEST';
export const LOG_OUT_SUCCESS = 'LOG_OUT_SUCCESS';
export const LOG_OUT_FAILURE = 'LOG_OUT_FAILURE';

export const SIGN_UP_REQUEST = 'SIGN_UP_REQUEST';
export const SIGN_UP_SUCCESS = 'SIGN_UP_SUCCESS';
export const SIGN_UP_FAILURE = 'SIGN_UP_FAILURE';

export const NICKNAME_EDIT_REQUEST = 'NICKNAME_EDIT_REQUEST';
export const NICKNAME_EDIT_SUCCESS = 'NICKNAME_EDIT_SUCCESS';
export const NICKNAME_EDIT_FAILURE = 'NICKNAME_EDIT_FAILURE';

export const BOARD_ADD_POST_TO_ME = 'BOARD_ADD_POST_TO_ME';
export const BOARD_REMOVE_POST_OF_ME = 'BOARD_REMOVE_POST_OF_ME';

export const SCRAP_ADD_POST_TO_ME = 'SCRAP_ADD_POST_TO_ME';
export const SCRAP_REMOVE_POST_OF_ME = 'SCRAP_REMOVE_POST_OF_ME';

export const loginRequestAction = (data) => {
  return {
    type: LOG_IN_REQUEST,
    data
  }
};

export const signupRequestAction = (data) => {  
  return {
    type: SIGN_UP_REQUEST,    
    data
  }
};

export const nicknameEditRequestAction = (data) => {  
  return {
    type: NICKNAME_EDIT_REQUEST,    
    data
  }
};

const reducer = (state = initialState, action) => {
  return produce(state, (draft) => {
    switch (action.type) {
      case LOG_IN_REQUEST:
        draft.logInLoading = true;
        draft.logInDone = false;
        draft.logInError = null;
        break;
      case LOG_IN_SUCCESS:
        draft.logInLoading = false;
        draft.logInDone = true;
        draft.me = dummyUser(action.data);
        break;
      case LOG_IN_FAILURE:
        draft.logInLoading = false;
        draft.logInError = action.error;
        break;
      case LOG_OUT_REQUEST:
        draft.logOutLoading = true;
        draft.logOutDone = false;
        draft.logOutError = null;
        break;
      case LOG_OUT_SUCCESS:
        draft.logOutLoading = false;
        draft.logOutDone = true;
        draft.me = null;
        break;
      case LOG_OUT_FAILURE:
        draft.logOutLoading = false;
        draft.logOutError = action.error;
        break;
      case SIGN_UP_REQUEST:
        draft.signUpLoading = true;
        draft.signUpDone = false;
        draft.signUpError = null;
        break;        
      case SIGN_UP_SUCCESS:
        draft.signUpLoading = false;
        draft.signUpDone = true;
        break;        
      case SIGN_UP_FAILURE:
        draft.signUpLoading = false;
        draft.signUpError = action.error;
        break;
      case NICKNAME_EDIT_REQUEST:
        draft.nicknameEditLoading = true;
        draft.nicknameEditDone = false;
        draft.nicknameEditError = null;
        break;      
      case NICKNAME_EDIT_SUCCESS:
        draft.me.nickname = action.data.nicknameEdit;
        draft.nicknameEditLoading = false;
        draft.nicknameEditDone = true;
        break;        
      case NICKNAME_EDIT_FAILURE:
        draft.nicknameEditLoading = false;
        draft.nicknameEditError = action.error;
        break;      
      case SCRAP_ADD_POST_TO_ME:
        draft.me.Scrap.unshift(dummyPost(action.data));
        break;
      case SCRAP_REMOVE_POST_OF_ME:
        draft.me.Scrap = draft.me.Scrap.filter((v) => v.id !== action.data);
        break;
      case BOARD_ADD_POST_TO_ME:
        draft.me.Board.unshift(dummyPost(action.data));
        break;
      case BOARD_REMOVE_POST_OF_ME:
        draft.me.Board = draft.me.Board.filter((v) => v.id !== action.data);
        break;
      default:
        break;
    }
  });
};

export default reducer;

 

sagas/user.js

import { all, fork, delay, put, takeLatest, call } from 'redux-saga/effects';
import axios from 'axios';
import { 
  LOG_IN_REQUEST, LOG_IN_SUCCESS, LOG_IN_FAILURE, 
  LOG_OUT_REQUEST, LOG_OUT_SUCCESS, LOG_OUT_FAILURE, 
  SIGN_UP_REQUEST, SIGN_UP_SUCCESS, SIGN_UP_FAILURE,
  NICKNAME_EDIT_REQUEST, NICKNAME_EDIT_SUCCESS, NICKNAME_EDIT_FAILURE,
} from '../reducers/user';

// function logInAPI(data) {
//   return axios.post('/api/login', data);
// }

function* logIn(action) {
  try {    
    // const result = yield call(logInAPI, action.data);        
    yield delay(1000);
    yield put({
      type: LOG_IN_SUCCESS,       
      data: action.data,
    })
  } catch(err) {
    yield put({
      type: LOG_IN_FAILURE,
      error: err.response.data
    })
  }  
}

// function logOutAPI() {
//   return axios.post('/api/logout');
// }

function* logOut() {
  try {  
    // const result = yield call(logOutAPI);
    yield delay(1000);
    yield put({
      type: LOG_OUT_SUCCESS,      
    })
  } catch(err) {
    yield put({ 
      type: LOG_OUT_FAILURE,
      error: err.response.data
    })
  }  
}

function signUpAPI(data) {
  console.log('signUpAPI 실행');
  return axios.post('/user', data);
}

function* signUp(action) {
  console.log('signUp 실행');
  try {  
    const result = yield call(signUpAPI, action.data);
    console.log(result);
    yield put({
      type: SIGN_UP_SUCCESS,      
    })
  } catch(err) {
    console.log(err);
    yield put({ 
      type: SIGN_UP_FAILURE,
      error: err.response.data
    })
  }  
}

// function NicknameEditAPI() {
//   return axios.post('/api/NicknameEdit');
// }

function* nicknameEdit(action) {
  try {  
    // const result = yield call(NicknameEditAPI);
    console.log(action.data);
    yield delay(1000);
    yield put({
      type: NICKNAME_EDIT_SUCCESS,
      data: action.data,
    })
  } catch(err) {
    yield put({ 
      type: NICKNAME_EDIT_FAILURE,
      error: err.response.data
    })
  }  
}

function* watchLogIn() {   
  yield takeLatest(LOG_IN_REQUEST, logIn);
}

function* watchLogOut() {  
  yield takeLatest(LOG_OUT_REQUEST, logOut);
}

function* watchSignUp() {  
  console.log('watchSignUp 실행');
  yield takeLatest(SIGN_UP_REQUEST, signUp);
}

function* watchNicknameEdit() {  
  yield takeLatest(NICKNAME_EDIT_REQUEST, nicknameEdit);
}

export default function* userSaga() {
  yield all([
    fork(watchLogIn),
    fork(watchLogOut),
    fork(watchSignUp),
    fork(watchNicknameEdit),
  ]);
}

 

back/app.js

const express = require('express');
const cors = require('cors');

const postRouter = require('./routes/post');
const userRouter = require('./routes/user');
const db = require('./models')
const app = express();

db.sequelize.sync()
  .then(() => {
    console.log('db 연결 성공');
  })
  .catch(console.error);

app.use(cors({
  origin: true,
}));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get('/', (req, res) => {
  res.send('Hello Express');
});

app.get('/api', (req, res) => {
  res.send('Hello API');
});

app.get('/api/posts', (req, res) => {
  res.json([
    { id: 1, content: 'Hello1'},
    { id: 2, content: 'Hello2'},
    { id: 3, content: 'Hello3'},
  ])
});

app.use('/post', postRouter);
app.use('/user', userRouter);

app.listen(3065, () => {
  console.log('서버 실행 중');
});

 

back/routes/user.js

const express = require('express');
const bcrypt = require('bcrypt');
const { User } = require('../models');

const router = express.Router();

router.post('/', async(req, res, next) => {  
  try {
    const exUser = await User.findOne({
      where: {
        email: req.body.email,
      }
    });
    if (exUser) {
      return res.status(403).send('이미 사용중인 아이디입니다.');
    }

    const hashedPassword = await bcrypt.hash(req.body.password, 10);
    await User.create({
      email: req.body.email,
      password: hashedPassword,
      nickname: req.body.nickname,
    });
    res.status(200).send('OK');
  } catch (error) {
    console.error(error);
    next(error);
  }
});

module.exports = router;

 

back/models/user.js

const DataTypes = require('sequelize');
const { Model } = DataTypes;

module.exports = class User extends Model {
  static init(sequelize) {
    return super.init({
      email: {
        type: DataTypes.STRING(30),
        allowNull: false,
        unique: true,
      },
      nickname: {
        type: DataTypes.STRING(30),
        allowNull: false,
      },
      password: {
        type: DataTypes.STRING(100),
        allowNull: false,
      },
    }, {
      modelName: 'User',
      tableName: 'users',
      charset: 'utf8',
      collate: 'utf8_general_ci',
      sequelize,
    });
  }
  static associate(db) {
    db.User.hasMany(db.Post);
    db.User.hasMany(db.Comment);
    db.User.belongsToMany(db.Post, { through: 'Like', as: 'Liked' })    
  }
};

 

 

Next.js redux react nodejs express

답변 1

0

제로초(조현영)

그러니까 에러가 뭔가요?? 콘솔에 있는 에러를 해결하셔야할것같습니다.

0

hib4888

명절인데도 빠른 답변 정말 감사합니다!!

첫 빌드시 콘솔에는 다음과 같은 에러가뜨는데 해당 에러가

user router실행되지 않는 문제랑 연관이 있는걸까요?

image

0

hib4888

회원가입 이후에는 해당 콘솔에러가 출력되지 않고 정상적으로 기능이 동작합니다

다만 질문글의 사진처럼 network탭에서만 user router가 실행되지 않습니다

0

제로초(조현영)

이해가 안 가는 것이 user router라는 게 뭔가요? 왜 user router가 실행되야한다고 생각하시는 건가요...??

0

hib4888

해당 강의에서 회원가입을 구현하신 뒤 회원가입을 수행하면 네트워크탭에 name: user, method: post가 표시되는데, 제 네트워크탭에서는 아무런 표시가안되고 있습니다.

표시안되는이유가 routes/user에서 작성한 post요청이 정상적으로 안되서 그런건지 그 이유에 대해 궁금해서 질문드렸습니다.

router.post('/', async(req, res, next) => {  
  try {
    const exUser = await User.findOne({
      where: {
        email: req.body.email,
      }
    });
    if (exUser) {
      return res.status(403).send('이미 사용중인 아이디입니다.');
    }

    const hashedPassword = await bcrypt.hash(req.body.password, 10);
    await User.create({
      email: req.body.email,
      password: hashedPassword,
      nickname: req.body.nickname,
    });
    res.status(200).send('OK');
  } catch (error) {
    console.error(error);
    next(error);
  }
});

 

0

제로초(조현영)

지금 개발자도구 네트워크탭에서 필터가 걸려있는것 아닌가요? 잘 보시면 56개 요청 중 10개만 필터로 보고 계십니다.

0

hib4888

정상적으로 실행되고 있었네요.. 진짜 바보같은 질문을 드렸습니다 바쁘신데 죄송합니다 ㅜㅜ

추가로 passport login을 구현해서 실행했는데 로그인, 회원가입시 둘다 아래와 같은 서버 에러가 발생하는데 어떤 이유때문에 발생했는지 알 수 있을까요?

구글링으로 계속 찾고있는데 답을 찾기힘들어서 질문드립니다 ㅜㅜ

image

image

image

image

<관련코드>

back/app.js

const express = require('express');
const cors = require('cors');
const passport = require('passport');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const dotenv = require('dotenv');

const postRouter = require('./routes/post');
const userRouter = require('./routes/user');
const db = require('./models')
const app = express();
const passportConfig = require('./passport');

dotenv.config();
db.sequelize.sync()
  .then(() => {
    console.log('db 연결 성공');
  })
  .catch(console.error);

passportConfig();

app.use(cors({
  origin: true,
}));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(passport.initialize());
app.use(passport.session({
  saveUninitialized: false,
  resave: false,
  secret: process.env.COOKIE_SECRET,  
}));
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session());

app.use('/post', postRouter);
app.use('/user', userRouter);

app.listen(3065, () => {
  console.log('서버 실행 중');
});

 

routes/user.js

const express = require('express');
const bcrypt = require('bcrypt');
const passport = require('passport');

const { User } = require('../models');

const router = express.Router();

router.post('/login', (req, res, next) => {
  passport.authenticate('local', (err, user, info) => {
    if (err) {
      console.error(err);
      return next(err);
    }

    if (info) {
      return res.status(401).send(info.reason);
    }
    return req.logIn(user, async (loginErr) => {
      if (loginErr) {
        console.error(loginErr);
        return next(loginErr);
      }
    return res.status(200).json(user);
    });
  })(req, res, next);
}); 

module.exports = router;

 

passport/index.js

const passport = require('passport');
const local = require('./local');
const { User } = require('../models');

module.exports = () => {
  passport.serializeUser((user, done) => {
    done(null, user.id);
  });

  passport.deserializeUser(async (id, done) => {
    try {
      const user = await User.findOne({
        where: { id }
      })
      done(null, user);
    } catch (error) {
      console.error(error);
      done(error);
    }
  });

  local();
};

 

passport/local.js

const passport = require('passport');
const { Strategy: LocalStrategy } = require('passport-local');
const bcrypt = require('bcrypt');
const { User } = require('../models');

module.exports = () => {
  passport.use(new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password',
  }, async (email, password, done) => {
    try {
      const user = await User.findOne({
        where: { email }
      });

      if (!user) {
        return done(null, false, { reason: '존재하지 않는 이메일입니다!!' });
      }

      const result = await bcrypt.compare(password, user.password);
      if (result) {
        return done(null, user);
      }      
      return done(null, false, { reason: '비밀번호가 틀렸습니다.' })      
    } catch (error) {
      console.error(error);
      return done(error);
    }
  }));
};

0

hib4888

back/app.js에서 오타가 발생해서 session이 미적용됬었네요

해결되었습니다 다른 답변들 정말 너무 감사합니다!!

넥스트 버젼 질문

0

90

2

로그인시 401 Unauthorized 오류가 뜹니다

0

104

1

무한 스크롤 중 스크롤 튐 현상

0

198

1

특정 페이지 접근을 막고 싶을 때

0

117

2

createGlobalStyle의 위치와 영향범위

0

103

2

인라인 스타일 리렌더링 관련

0

98

2

vsc 에서 npm init 설치시 오류

0

159

2

nextjs 15버전 사용 가능할까요?

0

166

1

화면 새로고침 문의

0

129

1

RTK에서 draft, state 차이가 있나요?

0

164

2

Next 14 사용해도 될까요?

0

455

1

next, node 버전 / 폴더 구조 질문 드립니다.

0

359

1

url 오류 질문있습니다

0

218

1

ssh xxxxx로 우분투에 들어가려니까 port 22: Connection timed out

0

391

1

sudo certbot --nginx 에러

0

1295

2

Minified React error 콘솔에러 (hydrate)

0

481

1

카카오 공유했을 때 이전에 작성했던 글이 나오는 버그

0

257

1

프론트서버 배포 후 EADDRINUSE에러 발생

0

341

1

npm run build 에러

0

526

1

front 서버 npm run build 중에 발생한 에러들

0

399

1

서버 실행하고 브라우저로 들어갔을때 404에러

0

351

2

css 서버사이드 랜더링이 적용되지 않아서 문의 드립니다.

0

291

1

팔로워 3명씩 불러오고 데이터 합쳐주는걸로 바꾸고 서버요청을 무한으로하고있습니다.

0

251

2

해시태그 검색에서 throttle에 관해 질문있습니다.

0

207

1