인프런 커뮤니티 질문&답변

yeolan님의 프로필 이미지
yeolan

작성한 질문수

[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지

nodebird 프로필 수정 기능 구현 시, 에러가 발생했을 때 에러처리미들웨어에서 res.render('error')가 안되는 상황

해결된 질문

작성

·

53

0

프로필 수정 기능을 구현하기 위한 profileUpdate.html

{% extends 'layout.html' %} {% block content %}
<div class="timeline">
  <form id="profile-update-form">
    <!-- <div class="input-group">
      <label for="join-email">이메일</label>
      <input id="join-email" type="email" name="email" />
    </div> -->
    <div class="input-group">
      <label for="join-nick">닉네임</label>
      <input id="join-nick" type="text" name="nick" />
    </div>
    <div class="input-group">
      <label for="join-password">비밀번호</label>
      <input id="join-password" type="password" name="password" />
    </div>
    <button id="join-btn" type="submit" class="btn">수정</button>
  </form>
</div>
{% endblock %} {% block script %}
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
  window.onload = () => {
    const errorParam = new URL(location.href).searchParams.get('error');
    if (errorParam) {
      alert(errorParam);
    }
  };

  if (document.getElementById('profile-update-form')) {
    document.addEventListener('submit', async (event) => {
      event.preventDefault();

      const formData = new FormData(event.target);
      const config = {
        headers: {
          'content-type': 'application/json',
        },
      };

      axios
        .put('/profile/update', formData, config)
        .then((res) => {
          alert('프로필 정보가 수정되었습니다.');
          window.location.href = '/profile';
        })
        .catch((error) => {
          alert(error);
        });
    });
  }
</script>
{% endblock %}

그리고 controller/page.js

exports.renderProfileUpdate = (req, res, next) => {
  res.render('profileUpdate', { title: '내 정보 수정 - NodeBird' });
};

exports.profileUpdate = async (req, res, next) => {
  try {
    const { nick, password } = req.body;
    const id = req.user.id;
    const exUser = await User.findOne({ where: { id } });
    if (!exUser) {
      throw new Error('존재하지 않는 사용자입니다.');
      // res.status(404).send('no user');
    }
    const sameNickUser = await User.findOne({
      where: {
        nick,
        id: {
          [Op.ne]: exUser.id,
        },
      },
    });
    if (sameNickUser) {
      throw new Error('중복된 닉네임입니다.');
      // res.status(501).send('중복된 닉네임입니다.');
    }

    const hash = await bcrypt.hash(password, 12);

    exUser.set({
      nick,
      password: hash,
    });
    await exUser.save();

    res.status(201).send();
  } catch (error) {
    console.error(error);
    next(error);
  }
};

app.js의 에러처리 미들웨어

app.use((err, req, res, next) => {
  // 404 다음은 에러처리 미들웨어
  console.error('에러는 ', err.message);
  res.locals.message = err.message;
  res.locals.error = process.env.NODE_ENV !== 'production' ? err : {}; // 보안상 위험(오히려 배포 시 사용자 화면에 에러를 숨김) // 에러를 로깅 서비스에 넘김
  res.status(err.status | 500);
  res.render('error'); // views/error.html
});

일부러 중복된 닉네임을 넣어 라우터에서 에러를 발생시켰을 때,

에러처리미들웨어의 console.error('에러는 ', err.message); 부분에서 "Error: 중복된 닉네임입니다.

at exports.profileUpdate (/nodestudy/nodebird/controllers/page.js:31:13)

at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

에러는 중복된 닉네임입니다.

PUT /profile/update 500 10.568 ms - 1862" 로 에러 메시지가 정상적으로 찍히는 것을 확인하였습니다.

스크린샷 2024-08-29 오후 11.08.42.png

스크린샷 2024-08-29 오후 11.09.17.pngpreview 탭에선 정상적으로 나오는 것 같은데, 실제 브라우저 화면에선

스크린샷 2024-08-29 오후 11.22.50.png위와 같이 뜨면서 제가 원하는 에러 메시지('중복된 닉네임입니다')가 alert 창에 뜨지 않으며, error.html이 렌더링도 되지 않고 있습니다.

구글링해봐도 제가 잘 못한건지 이유를 못찾겠습니다🥲 제가 뭘 놓친걸까요?

답변 2

1

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

ajax(axios) 요청으로 보낸 건 브라우저 화면에 렌더링되지 않습니다. 폼 요청이나 a 링크 요청만이 화면에 렌더링됩니다. 응답의 컨텐츠를 document.write 하셔야합니다.

yeolan님의 프로필 이미지
yeolan
질문자

앗 그렇군요! 감사합니다:)

0

안녕하세요, 인프런 AI 인턴입니다. 질문자님의 상황으로 미루어 볼 때, 중복된 닉네임 에러 발생 시 에러 처리 미들웨어에서 res.render('error')가 작동하지 않는 것과 관련하여 몇 가지 점을 살펴볼 필요가 있습니다.

  1. 클라이언트 사이드에서의 에러 처리: axios.catch에서 서버로부터의 에러 응답을 적절하게 처리하고 있지 않는 것 같습니다. 서버에서 에러가 발생했을 때 클라이언트 측에서는 해당 에러를 catch 구문에서 잡아내고, 그에 따른 적절한 사용자 알림(예: alert 창)을 실행해야 합니다. 현재 제시된 코드에서는 단순히 alert(error);를 호출하고 있으나, 서버로부터 전달되는 에러 메세지(error.response.data.message 등)를 정확하게 지정하여 사용자에게 보여줘야 합니다.

  2. 서버 사이드에서의 HTTP 상태 코드 설정: res.status(err.status | 500); 부분에서 비트 OR 연산자를 사용하고 계시는데, 이는 err.statusundefined일 경우에만 500을 설정합니다. 그러나 의도한 상태 코드(400 범위의 클라이언트 에러 등)를 전달하고 싶다면, err.status가 정의되어 있지 않을 때만 500을 기본 값으로 사용하도록 조건을 명시하는 것이 중요합니다. 예를 들면, res.status(err.status || 500);으로 수정할 수 있습니다.

  3. 에러 처리 미들웨어에서의 res.render 호출: 문제가 될 수 있는 부분은 클라이언트 측에서 axios를 통해 비동기 요청을 보낼 때입니다. axios 요청은 클라이언트와 서버 간의 비동기 통신을 위한 것이므로, 이 경우 서버에서 res.render('error')를 호출하더라도 클라이언트 측에서는 해당 페이지로 리다이렉트 되지 않습니다. 이 경우, 서버는 에러 정보를 JSON 형태로 클라이언트에 전달해야 하며, 클라이언트 측에서는 이 응답을 받아 사용자에게 적절한 메시지를 표시(예: 에러 메시지를 포함한 alert 창)해야 합니다.

제로초(조현영) 강사님의 코멘트에서도 “app.js에서 postRouter 연결 안 하셨습니다!“와 같은 구조적인 문제를 지적하는 경우가 있습니다. 이는 각자의 경우에 따라 차이가 있으니, 질문자님의 상황에 맞게 app.js 내에서 필요한 라우터를 올바르게 연결했는지 확인해보시기 바랍니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

yeolan님의 프로필 이미지
yeolan

작성한 질문수

질문하기