LHJ

I'm a FE developer.

7. 흐름제어문(Flow Control Statement) - 조건문

08 Aug 2020 » codespitz_re

조건문

// 아래 소괄호 안에는 '식'이 들어온다.
// '식'이 truthy인지 falsy인지 판단하는 것이다.
// if문으로만 끝나는 것은 옵셔널 구문이다. 해도되고 안해도 그만인 문
if () 옵셔널

// if else 문은 필수적인 멘다토리 문이다. 반드시 결정된 로직.
if () else 필수적(멘다토리)

// 많은 사람들이 아래 식을 else if 문이라고 인식한다.
// 하지만 실제로 제어문엔 if ... else 문만 존재한다.  
// 아래식 또한 if ... else 문이다.
// 아래 else 문 다음에 if 문 단문이 하나 들어온 것이다. 
const c = 3;
if (c === 1) {

} else if (c === 2) {

} else if (c === 3) {

} else {

}
// 따라서 위의 식은 아래처럼 쓸 수 있다.
// 사실 아래와 같이 쓰는 게 코드를 이해하기 더 쉽고(오해를 불러일으키지 않고) 더 좋다. 명시적으로 알 수 있으므로.
const c = 3;
if (c === 1) {

} else {
    if (c === 2) {
    
    } else {
        if (c === 3) {
            
        } else {
        
        }
    }
}
// 이젠 아래와 같은식도 이해되지?
const c = 3;
if (c > 5) {

} else switch (c) {
  
}
// 위의 식을 정확히 쓰면 아래와같이 쓸 수 있음
const c = 3;
if (c > 5) {

} else {
    switch (c) {
      
    }
}
// else if가 안 이상하다면 아래와 같은 식도 전부 안 이상해야 정상이다.
const c = 3;
if (c > 5) {

} else for () {

}

else if 문이라는 것은 없다.
if else 문의 문제가 무엇이냐면, if else가 반복되는 경우에 else가 후방 결합을 한다는 것이다.

// javascript if는 후방에 있는 else가 앞에 있는 if를 따라가도록 되어있다.
// 이러한 현상을 방지하려면 {} 로 묶어서 분리해주지 않으면 항상 후방 결합을 하도록 되어있다.
// 즉, 이런 뜻 같다. 아래의 식은 서로가 각각 1, 2, 3에 매칭되어 있지만 만약에 ==이 아니라 범위라면,
// 후방결합으로인해 겹치는 영역이 발생한다는 것이다. (이런 뜻 같음)
// 그럼 필연적으로 에러가 날수밖에 없다.
const c = 3;
if (c === 1) {

} else if (c === 2) {

} else if (c === 3) {

} else {

}
// 그래서 이런식으로 쓰는 것이 좋다.
// else if 구문은 사용하지 말고, 앞으로는 중괄호를 사용해서 작성한다.
const c = 3;
if (c === 1) {

} else {
    if (c === 2) {
    
    } else {
        if (c === 3) {
            
        } else {
        
        }
    }
}

else if 를 병렬적으로 사용하면 위험성이 커진다.
else if는 원래도 어려운 코드이고, 남들이 봤을 때 이해하기 난감한 코드인데다가, else 후방조건지 제대로 붙어있는지를 검사하기가 굉장히 까다롭다.

컴파일 에러도 안나고, 런타임도 그대로 통과하기 때문에 그렇다.
그냥 알고리즘 자체가 잘못된 경우가 너무 많다.

그렇다면 else if는 언제 쓰는 것일까?
병렬로 나열하는 것이 아닌 부분집합일 때만 쓴다.

이전까지는 else if병렬로 인식했을 것이다.
하지만 else if는 사실 병렬이 아닌 안에 nested 되어있는 sub 집합인 것이다.
따라서 else if는 병렬 조건일 때 쓰면 원래 안되는 것인데, 착시효과 때문에 병렬 조건처럼 썼던 것이다.
그래서 switchelse if랑 똑같다고 생각하는 것이다.
아주 잘못된 생각이다.

  • 병렬 조건일 땐 switch 문을 쓴다.
  • sub 집합이면 if else 문을 쓴다.
const c = 3;
if (c === 1) {

} else {
    if (c === 2) {
    
    } else {
        // 그럼 이런 경우는 어떤 경우일까?
        // 이런 경우를 '논리적 부수효과'라고 부른다.
        // 여러분들의 코드들은 대부분 이런 경우에서 망가진다.
        // if ... else는 mandatory 로직이라고 했다. 절대로 빠져나갈 수 없는 필수적인 로직
        if (c === 3) {
            // if ... else 구문안에 if문이 덩그러니
            // 이건 mandatory인지 optional인지 알 수 없다.
            // 이 코드를 완전히 읽고 분석하기 전까지는..
        } 
    }
}

// 추천하는 로직은 mandatory로 갔으면 전체 다 mandatory로 갈 것을 추천한다.
// 위와 같은 경우는 코드를 다 까보고 실행시켜야 이해할 수 있다. 그 전까지는 절대로 이해못한다.
// 기계는 괜찮지만 사람이 이해할 수 없다.
// mandatory로 시작했으면 mandatory로 끝나야지 중간에 optional이 들어오면 그 다음부턴 이 로직을 이해할 수 없게 된다.

switch 문은 굉장히 섬세하고 어렵다.
왜냐하면 병렬조건을 나열하면 반드시 여집합이 생기기 때문이다.
그래서 switch 문에는 반드시 default가 있다.
default가 없는 switch 문은 사용하지 않는다.
병렬 조건에는 반드시 예외가 생길 수 있는 가능성이 있기 때문이다.

요약

  1. if … else 문은 mandatory로 작성할꺼면 끝까지 mandatory로 작성해라.
  2. else 뒤에 if 문을 사용할 거면 그건 subset이라는 뜻이기 때문에 {}로 묶어줘라.
    그래야 의미를 혼동하지 않는다.
    반대로 subset일 때만 if … else를 사용해라.
  3. 병렬조건일 때는 switch 문을 사용해라.
    병렬조건을 사용하면 반드시 여집합이 생기기 때문에 default 처리를 해라.

어려운 경우

if … else 문에 switch 문이 들어가 있을 때, if … else 문은 mandatofy 문인데 병렬 조건인 switch 문이 들어가 있다.
이런 경우는 뭘까?
병렬 조건은 default가 있는 이상 mandatory로 빠질 수 있다.
case에 예외가 없기 때문이다.

if … else 문에 if 문만 왔다.
mandatory에 optional이 온 경우이다.
그런데 로직 돌렸는데 아무 이상이 없다.
이는 수많은 경우 중에 일부만 성공한 것이다.
더 돌리다보면 망가지고 에러난다.
mandatory는 mandatory로 가고 optional은 optional로 가는 것이 좋다.
아니면 로직 분리를 굉장히 잘 하던지, 그게 아니라면 완전히 분리해서 함수로 보내던지 해야된다.

mandatory인데 optional이 오는 경우

if () {

} else {    
    a();
}

이런 경우인데, a함수 내용이 optional이라면?
이런 경우는 괜찮다.
function 안에서 optional로 보면 되기 때문이다.
이런걸 격리라고 부른다.

우리가 익혀야될 기술은 격리이다.