ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • #TIL_옵셔널 체이닝(Optional Chaining)이 있어서 듬직허다
    TIL (Today I Learned) 2023. 11. 23. 00:22

    # 옵셔널 체이닝(Optional Chaining)이란?

     

    옵셔널 체이닝은 존재하지 않을 수 있는 프로퍼티 또는 메서드를 안전하게 호출할 수 있도록 도와줍니다.


     

    프로그래밍에서 null 또는 undefind 등의 값으로 인해 발생할 수 있는 오류를 방지하고 처리하기 위한 기술입니다. 객체 또는 배열의 속성에 연속적으로 접근할 때, 해당 속성이 존재하지 않아 발생하는 오류를 방지합니다.

     

    일련의 속성 접근 중, 하나라도 null 또는 undefined인 경우에도 오류를 발생시키지 않고 그대로 실행을 진행하며, 마지막으로 접근한 속성의 값이 존재하지 않으면 undefined를 반환합니다. 


    # 실제 적용한 코드 사례

    // 프로젝트 일부_auth.router.js 파일
    
    import { Router } from 'express';
    import bcrypt from 'bcrypt';
    import jwt from 'jsonwebtoken';
    import db from '../models/index.cjs';
    import {
      PASSWORD_HASH_SALT_ROUNDS,
      JWT_ACCESS_TOKEN_SECRET,
      JWT_ACCESS_TOKEN_EXPIRES_IN,
    } from '../constants/security.constant.js';
    const { Users } = db;
    
    const authRouter = Router();
    
    // 회원가입
    authRouter.post('/signup', async (req, res) => {
      try {
        const { email, password, passwordconfirm, name } = req.body;
    
        if (!email) {
          return res.status(400).json({
            success: false,
            message: '이메일 입력이 필요합니다.',
          });
        }
    
        if (!password) {
          return res.status(400).json({
            success: false,
            message: '비밀번호 입력이 필요합니다.',
          });
        }
    
        if (!passwordconfirm) {
          return res.status(400).json({
            success: false,
            message: '비밀번호 확인 입력이 필요합니다.',
          });
        }
    
        if (!name) {
          return res.status(400).json({
            success: false,
            message: '이름 입력이 필요합니다.',
          });
        }
    
        if (password !== passwordconfirm) {
          return res.status(400).json({
            success: false,
            message: '입력한 비밀번호가 서로 일치하지 않습니다.',
          });
        }
    
        if (password.length < 6) {
          return res.status(400).json({
            success: false,
            message: '비밀번호는 최소 6자리 이상 입력해야합니다.',
          });
        }
    
        let emailValidationRegex = new RegExp('[a-z0-9._]+@[a-z]+.[a-z]{2,3}');
        const isValidEmail = emailValidationRegex.test(email);
        if (!isValidEmail) {
          return res.status(400).json({
            success: false,
            message: '올바른 이메일 형식이 아닙니다.',
          });
        }
    
        const existUser = await Users.findOne({ where: { email } });
        if (existUser) {
          return res.status(400).json({
            success: false,
            message: '이미 가입 된 이메일입니다.',
          });
        }
    
        const hashedPassword = bcrypt.hashSync(password, PASSWORD_HASH_SALT_ROUNDS);
    
        const newUser = (
          await Users.create({ email, password: hashedPassword, name })
        ).toJSON();
        delete newUser.password;
    
        // console.log({ newUser: newUser.toJSON() });
    
        return res.status(201).json({
          success: true,
          massage: '회원가입에 성공했습니다.',
          data: newUser,
        });
      } catch (error) {
        console.error(error);
        return res.status(500).json({
          success: false,
          massage: '예상치 못한 에러가 발생했습니다. 관리자에게 문의하세요.',
        });
      }
    });
    
    // 로그인
    authRouter.post('/signin', async (req, res) => {
      try {
        const { email, password } = req.body;
    
        if (!email) {
          return res.status(400).json({
            success: false,
            message: '이메일 입력이 필요합니다.',
          });
        }
    
        if (!password) {
          return res.status(400).json({
            success: false,
            message: '비밀번호 입력이 필요합니다.',
          });
        }
    
        const user = (await Users.findOne({ where: { email } }))?.toJSON();
        const hashedPassword = user?.password;
        const isPasswordMatched = bcrypt.compareSync(password, hashedPassword);
    
        const isCorrectUser = user && isPasswordMatched;
        if (!isCorrectUser) {
          return res.status(401).json({
            success: false,
            message: '일치하는 인증 정보가 없습니다.',
          });
        }
    
        const accessToken = jwt.sign({ userId: user.id }, JWT_ACCESS_TOKEN_SECRET, {
          expiresIn: JWT_ACCESS_TOKEN_EXPIRES_IN,
        });
    
        return res.status(200).json({
          success: true,
          massage: '로그인에 성공했습니다.',
          data: { accessToken },
        });
      } catch (error) {
        console.error(error);
        return res.status(500).json({
          success: false,
          massage: '예상치 못한 에러가 발생했습니다. 관리자에게 문의하세요.',
        });
      }
    });
    
    export { authRouter };

     


    # 예시 1

    주어진  코드는 Node.js의 Express 프레임워크를 사용하여 회원가입 및 로그인 기능을 구현하는 코드입니다. 옵셔널 체이닝은 코드에서 `authRouter.post('/signin'), async (req, res) => { ... }` 부분에서 사용되었습니다. 

     

    여기서 옵셔널 체이닝은 다음 라인에 발생합니다. 

    const user = (await Users.findOne({ where: { email } }))?.toJSON();
    const hashedPassword = user?.password;

     

    위 코드에서 `Users.findOne({ where: { email } })`은 데이터베이스에서 주어진 이메일에 해당하는 사용자를 찾는 메서드입니다. 그리고 `?.` 옵셔널 체이닝 연산자를 찾은 사용자 객체가 존재할 때만 `,toJSON()` 메서드를 호출하고, 존재하지 않을 경우 `null`을 반환합니다. 이렇게 함으로써 사용자가 없는 경우(예 주어진 이메일에 해당하는 사용자가 없는 경우) `null`을 반환하게 되고, 이후의 코드에서 `null`일 경우 오류가 발생하지 않도록 처리합니다. 


    # 예시 2

    다음 라인에서 옵셔널 체이닝을 다시 사용하여 해시된 비밀번호를 가져옵니다. 

    const hashedPassword = user?.password;

     

    위 코드에서도 마찬가지로 `user`가 존재하고 그 안에 `password` 속성이 존재할 경우에만 `hashedPassword` 변수에 값을 할당합니다. 이를 통해, 사용자가 없거나 사용자 객체 내에 `password` 속성이 없는 경우에도 오류가 발생하지 않도록 합니다. 

     

    옵셔널 체이닝은 코드 실행 중 발생할 수 있는 속성의 존재 여부에 따른 예기치 못한 오류를 방지하고, 코드를 더 안정적으로 만들어 줍니다.


    # 옵셔널 체이닝 장점

    1. 안전한 속성 접근

    객체의 중첩된 속성에 안전하게 접근할 수 있어서, 중첩된 객체에서 일부 속성이 존재하지 않아도 오류를 방지하고 코드 실행을 계속할 수 있습니다.

     

    2. 가독성 향상

    코드를 간결하게 작성할 수 있으며, 일일이 null 또는 undefined인지 확인하는 코드를 줄여줍니다.

     

    3. 유연한 코드 작성

     옵셔널 체이닝을 사용하면 예외 처리를 보다 쉽게 할 수 있어서, 코드 작성이 보다 유연해집니다.


    # 옵셔널 체이닝 단점

    1. 너무 남용 시 코드 이해 어려움

    지나치게 많은 옵셔널 체이닝 사용은 코드를 읽거나 이해하기 어렵게 만들 수 있습니다. 일부 경우에는 오히려 코드의 의도를 명확하게 전달하지 못할 수 있습니다.

     

    2. 코드 성능 저하

     옵셔널 체이닝을 사용하면 각각의 체이닝마다 추가적인 null 또는 undefined 체크가 발생하므로, 성능에 약간의 영향을 미칠 수 있습니다.

     

    3. 코드 오용

    가끔 필요 이상으로 옵셔널 체이닝을 사용하거나 오용하는 경우가 있을 수 있습니다. 이는 코드를 복잡하게 만들 수 있고, 오히려 오류의 가능성을 높이고 디버깅을 하기 어렵게 만듭니다.

     

    # 옵셔널 체이닝 사용시 주의사항

    1. 적절한 사용

     옵셔널 체이닝은 필요한 곳에서만 사용하는 것이 중요합니다. 모든 속성 접근에 옵셔널 체이닝을 사용하는 것보다는 신중하게 선택해야 합니다.

     

    2. 가독성 고려

    코드의 가독성을 유지하기 위해 사용하는 곳에 대해 신중하게 판단해야 합니다.

     

    3. 널 값에 대한 예외 처리

    옵셔널 체이닝으로 인해 값이 null이거나 undefined일 때 어떻게 처리할지 고려해야 합니다. 때로는 예외 처리가 필요한 상황에서 널 값이 전파되지 않도록 주의해야 합니다.

     

    옵셔널 체이닝은 코드 안정성과 가독성을 향상시키는데 도움을 주지만, 적절한 사용과 과용을 구분하여 사용해야 합니다.

    코드의 의도를 명확히 전달하고, null 값에 대한 적절한 예외 처리를 함께 고려하는 것이 중요합니다.

     

     

Designed by Tistory.