Node+React 페이지에서 nodemailer 활용하여 이메일 보내기 기능 구현

우디·2024년 3월 4일
0
post-thumbnail

안녕하세요:) 개발자 우디입니다! 아래 내용 관련하여 작업 중이신 분들께 도움이되길 바라며 글을 공유하니 참고 부탁드립니다😊
(이번에 벨로그로 이사오면서 예전 글을 옮겨적었습니다. 이 점 양해 부탁드립니다!)

작업 시점: 2021년 11월

상황

  • 회사 홈페이지에서 프로그램을 다운로드 할 수 있음 → 그런데 모바일로 들어온 사용자들은 바로 다운로드 할 수 없어 접근성이 낮은 상황임
  • 프로그램 다운로드까지의 접근성을 높이기 위해 모바일 방문자들에게는 이메일로 다운로드 링크를 보내주는 기능을 개발함.

클라이언트 UI 구현 및 버튼 연동

function MainPage(props: ModalProps) {
  const { openGlobalAlertModal } = props;
  const isMobile = GetIsMobile();
  const { state, dispatch } = useContext(ModalStateContext);

  const [emailInput, setEmailInput] = useState('');
  const [isEmailValid, setIsEmailValid] = useState(false);
  const [isSendEmailButtonDisabled, setIsSendEmailButtonDisabled] = useState(false);

  ...

  const checkEmail = useCallback(() => {
    const emailRegExp =
      /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i;
    const emailChecked = emailRegExp.test(emailInput);
    setIsEmailValid(emailChecked);
  }, [emailInput]);

  useEffect(() => {
    checkEmail();
  }, [checkEmail]);

  ...

  const onGetDownloadLinkBtnClick = () => {
    ...
    if (!isEmailValid) {
      openGlobalAlertModal(
        'assets/globalAlertModalNoticeIcon.svg',
        '알림',
        '잘못된 형식의 이메일입니다.\n올바른 형식의 이메일을 입력해주세요.',
        'Open',
      );
      return;
    }
    // send email
    axiosInstance
      .post('sendDownloadLinkEmail', {
        email: emailInput,
      })
      .then(res => {
        if (res.data === 'success') {
          openGlobalAlertModal(
            'assets/globalAlertModalSuccessIcon.svg',
            '전송 완료',
            '이메일로 다운로드 링크가 전송됩니다.\nPC를 통해 다운로드해주세요!',
            'Open',
          );
          setIsSendEmailButtonDisabled(true);
          ...
        }
      })
      .catch(err => {
        console.log(err);
      });
  };

  return (
    <div className={styles.mainPageContainer}>
      <div className={styles.mainPageInnerContainer}>
        ...
        {isMobile ? (
          <div className={styles.mainPageBottomContainerForMobile}>
            <div className={styles.mainPageBottomContainerForMobileDesc}>
              이메일을 입력하여 다운로드 링크를 받아보세요!
            </div>
            <input
              className={styles.emailInput}
              id="emailToGetDownloadLink"
              value={emailInput}
              onChange={handleChange}
              placeholder="이메일을 입력해주세요"
            />
            <button
              type="button"
              onClick={onGetDownloadLinkBtnClick}
              className={styles.submitEmailButton}
              disabled={isSendEmailButtonDisabled}
            >
              이메일로 다운로드 링크 받기
            </button>
            <div className={styles.mainPageBottomContainerForMobilAlertText}>
              * 이메일이 수신되지 않았을 경우 스팸메일함을 확인해주세요.
            </div>
          </div>
        ) : (
          ...
  • 홈페이지의 메인 페이지를 담당하는 MainPage 컴포넌트에서 이메일 입력창과 버튼 등을 구현해 줌
  • 모바일 여부는 GetIsMobile 모듈을 통해 받아온 isMobile 변수를 통해 판단
  • isMobile 이 true일 경우 이메일 입력창을 보여주고, false일 경우 다른 UI를 보여주도록 삼항 연산자로 구현함.
  • 사용자가 이메일 입력할 수 있도록 input 구현
    • 사용자의 input을 감지할 수 있도록 input 태그 onChange 속성에 handleChange 함수 연결.
    • 사용자가 입력한 이메일의 형식이 제대로 된 형식인지 판단하기 위한 함수 checkEmail
      • checkEmail 함수는 useCallback을 통해 emailInput이 변경될 때를 제외하고 재사용 됨.
      • handleChange 로 emailInput이 변경되면, checkEmail 함수가 새로 선언됨.
      • checkEmail 함수가 변경되면, useEffect로 인해 checkEmail이 실행됨.
      • checkEmail을 통해 사용자가 입력한 이메일의 형식이 제대로 된 것인지 판별하여 isEmailValid 변수에 담아 줌
  • 다운로드 링크를 이메일로 보내기 위해 emailInput 값을 담아 post 요청을 보냄(sendDownloadLinkEmail)

이메일 보내기 기능 구현

  • node서버에서 메일을 보낼 수 있는 메일 전송 모듈인 nodemailer를 이용

  • client 단의 컴포넌트에서 post 요청을 보내면 서버 단의 app.js에서 요청을 받아 수행하는 구조로 진행함.

  • 서버 단의 app.js

    • 이메일 보내기 위해 필요한 함수들 정의

      const nodeMailer = require('nodemailer');
      
      ...
      
      // Send Mail Method
      const mailPoster = nodeMailer.createTransport({
        host: 'smtp.office365.com',
        auth: {
          user: '{your email}',
          pass: '{your password}',
        },
      });
      
      const mailOpt = (userEmail, title, contents) => {
        const mailOptions = {
          from: '{your email}',
          to: userEmail,
          subject: title,
          html: contents,
        };
      
        return mailOptions;
      };
      
      const sendMail = mailOption => {
        mailPoster.sendMail(mailOption, function (error, info) {
          if (error) {
            console.log('에러 ' + error);
          } else {
            console.log('전송 완료 ' + info.response);
          }
        });
      };
    • post 요청을 받으면, 요청에 맞는 작업을 수행

      app.post('/api/sendDownloadLinkEmail/', (req, res) => {
          if (!req.body.email) {
            res.status(400).json({
              error: '잘못된 접근입니다.',
            });
            return;
          }
          // Email input value
          const userEmail = req.body.email;
      
          // Email additional info
          const titleOfMailToSendDownloadLink = '...';
      
          const mailOption = mailOpt(
            userEmail,
            titleOfMailToSendDownloadLink,
            contentOfMailToSendDownloadLink,
          );
      
          sendMail(mailOption);
          res.send('success');
        });
      • /api/sendDownloadLinkEmail/ 엔드포인트를 만들어, 요청에 따른 작업을 수행할 수 있도록 세팅
      • client에서 넘어온 이메일 값과 타이틀, 내용으로 메일 옵션을 구성함
      • sendMail 메서드로 메일을 보내고, 완료 시 success 값을 반환하도록 구현.
    • 메일 내용이 너무 길어서 모듈로 따로 관리

메일 전송 결과 (모자이크 처리)

  • 성공적으로 내용이 전송된 것으로 보임
profile
넓고 깊은 지식을 보유한 개발자를 꿈꾸고 있습니다:) 기억 혹은 공유하고 싶은 내용들을 기록하는 공간입니다

0개의 댓글