논리 연산자 in javascript

SYhwang·2023년 2월 19일
0

논리 연산자

자바스크립트에는 && (AND) 와 || (OR), ! (NOT) 논리 연산자가 존재한다.
일반적으로 boolean 값과 사용하여 참/거짓을 반환하는 데 쓰이지만, 자바스크립트에서는 논리 연산자의 피연산자가 반드시 boolean 값일 필요는 없으며 반환 값도 항상 boolean 타입인 것은 아니다.

Truthy & Falsy

자바스크립트에는 truthyfalsy 라는 개념이 있다. 참/거짓을 판단하는 컨텍스트에서 truthy 한 값은 true로, falsy 한 값은 false로 판단된다.

falsy 로 판단되는 값은 다음과 같다.

  • 0
  • -0
  • 0n
  • ""
  • null
  • undefined
  • NaN

falsy한 값을 제외한 모든 값은 truthy 이다.

  • {} (빈 객체) 는 truthy 이다.
  • [] (빈 배열) 역시 truthy 이다.
    이는 참조 타입의 데이터(객체)를 생성해 변수에 담을 때 실제로 그 변수에 담기는 값이 객체에 담긴 데이터 자체가 아니라, 그 객체가 위치하고 있는 있는 메모리 공간의 주소(참조값)이기 때문이다.

&& (AND) 연산자와 || (OR)연산자

  • AND 연산자는 연산자 왼쪽과 오른쪽의 값이 모두 true이면 true, 한 쪽이라도 false이면 false 를 반환한다.
  • OR 연산자는 연산자 왼쪽과 오른쪽의 값이 모두 false이면 false, 한 쪽이라도 true이면 true 를 반환한다.
console.log(true && true) // true
console.log(true && false) // false
console.log(false && true) // false
console.log(false && false) // false

console.log(true || true) // true
console.log(true || false) // true
console.log(false || true) // true
console.log(false || false) // false

그러나 실제로 이는 피연산자를 boolean 타입의 true 혹은 false로 변환하는 것이 아니라 두 피연산자 중 하나를 선택하여 반환하는 식으로 작동한다. truthy 와 falsy 한 값에 논리 연산자를 적용해 보면 이를 확인할 수 있다.

console.log(3 && 6) // 6
console.log(3 && 0) // 0
console.log("" && "truthy") // ""
console.log(NaN && undefined) // NaN

console.log([] || 3) // []
console.log([] || null) // []
console.log(undefined || {}) // {}
console.log(null || undefined) // undefined

truthy 와 falsy 한 값에 논리 연산자를 적용하면, true 혹은 false로 변환 되어 반환되는 것이 아니다. 위와 같이 입력한 그대로의 두 값 중 하나를 선택하여 반환하는 것을 볼 수 있다!

  • AND 연산자는 연산자 왼쪽이 true일 때 오른쪽 값을 반환하고, 연산자 왼쪽이 false일 때는 왼쪽 값을 반환한다.
  • OR 연산자는 연산자 왼쪽이 true일 때는 왼쪽 값을 반환하고, 연산자 왼쪽이 false일 때는 오른쪽 값을 반환한다.

이 원리를 응용해 보자.

const globalErrorHandler = (err, req, res, next) => {
	if (err.statusCode) {
		res.status(err.statusCode).json({ message: err.message});
	} else {
		res.status(500).json({ message: err.message});
	}
};
  • 위는 전역적으로 에러 핸들링을 하는 globalErrorHandler 함수이다. err 인자를 받으면 response로 에러의 상태 코드와 에러 메시지를 반환해 준다.
  • 에러 상태코드가 존재하는 경우 (truthy 한 값이 들어올 경우) 존재하는 에러 상태코드를 사용하고, 에러 상태코드가 존재하지 않는 경우 (undefined가 들어올 경우) 500을 사용하도록 하고 싶다.
  • 이 경우 다음과 같이 OR 연산자 활용이 가능하다.
const globalErrorHandler = (err, req, res, next) => {
	res.status(err.statusCode || 500).json({ message: err.message});
};
  • OR 연산자를 활용하여 아까의 복잡한 if-else 문을 한 줄로 리팩토링하였다.

! (NOT) 연산자

truthy 한 값이 들어오면 false를 반환하고, falsy 한 값이 들어오면 true 를 반환한다.

  • 다음과 같은 상황에 응용할 수 있다.
const getUserPoint = async (userId) => {
    const [result] = await appDataSource.query(
      `SELECT
    amount
    FROM points
    WHERE points.user_id = ?;`,
      [userId]
    );
    
    if (!result) {
      throw new Error("존재하지 않는 유저입니다")
    }
      
    return result.amount;
}
  • 위는 userId를 인자로 받아 데이터베이스에서 해당 유저의 포인트를 조회하는 함수이다.
  • 존재하지 않는 유저의 userId가 인자로 들어왔을 경우, select 쿼리문의 결과물인 result의 값은 undefined 가 된다. ! undefined === true 이므로 아래의 if문 내로 들어가 에러를 발생시게 된다.
    if (!result) {
      throw new Error("존재하지 않는 유저입니다")
    }

!!

또한 ! 는 true 혹은 false를 반환한다는 점을 이용하여,
!! 과 같이 사용하여 truthy 한 값이 들어오면 true를 반환하고, falsy 한 값이 들어오면 false 를 반환하도록 truthy/falsy 한 값을 boolean 타입으로 형변환을 시키는 데 사용할 수도 있다.

const doesUserIdExist = async (userId) => {
    const [result] = await appDataSource.query(
      `SELECT EXISTS (
        SELECT id
        FROM users
        WHERE users.id = ?
      ) as registerd
      `,
      [userId]
    );
  
    return !!parseInt(result.registerd);
  • 위는 유저의 id를 인자로 받아 데이터베이스 내에 해당 id가 존재하는지 조회하는 함수이다.
  • SELECT EXISTS 쿼리문은 SELECT 문의 결과물이 존재하면 1, 존재하지 않으면 0을 반환한다.
    id가 존재하지 않을 경우 result 는 { registered : "0" },
    id가 존재할 경우 result 는 { registered : "1" } 이 된다.
  • 쿼리의 결과물이 string 타입으로 나오므로 우선 parseInt 를 이용해 숫자로 변환해 주었다.
  • 숫자로 변환한 값을 다시 !! 를 이용해 true 혹은 false로 변환해 주므로 이 함수의 결과물은 true 혹은 false로 깔끔하게 리턴된다!

0개의 댓글