Dart 문법 - 제어문

악어·2023년 5월 4일
0

Dart 문법

목록 보기
2/7
post-thumbnail

다트 문법 기본 2편

새 언어를 배우면 반드시 거쳐야할 관문으로

첫번째는 표기방식, 관습, 타입
두번째는 if, loop문, switch문 등의 기본 제어문
세번째는 함수 / class 및 고급 문법 활용들
이 있다고 생각한다.

이 포스팅은 두번째에 해당한다.



각종 제어문들


1. if문

별거 없다. 코틀린과 똑같다.
조건 자리에 ()를 꼭 해줘야한다.
(swift와의 차이)

int age = 23;
  
if(age < 20) {
  print("미성년자시군요");
} else if(age < 30) {
  print("20대시군요");
} else {
  print("30대 이상이군요");
}

추가 조건을 걸고 싶으면 else if로 표기하면 된다.

또, 개인적으로 유용하게 쓰는 삼항 연산자도 활용 가능하다!

int age = 23;
  
String msg = age < 20 ? "미성년자" : "미성년자 아님";
print(msg);   // 미성년자 아님

특히 UI 제작시 굉장히 유용한 기능이니 만큼
만족하는 부분이다.



2. switch문

코틀린에서의 when, swift에서의 switch와
대응되는 부분으로, 표현은 다음과 같다.

int age = 23;
  
switch(age) {
  case 20:
    print("이제 막 성인이 됐군요");
    break;
  case 21:
    print("성인 2년차군요");
    break;
  case 22:
    print("남자라면 높은 확률로 군대에 있겠군요");
    break;
  default:
    print("당신의 나이는 ${age}살이군요");
    
  // 당신의 나이는 23살이군요
}

비교하자면 코틀린의 when보다는 swift의
switch와 훨씬 닮아있다.

상태에 대한 표현이 가능해 완전히 else if를
대체할 수 있었던 when과는 달리
같은지 여부만 파악할 수 있는 switch문은
그 활용이 제한된다.

다만 성능이 조금 더 좋고, 코드의 가독성이
좋아진다는점, enum과의 조합이 좋다는 점등으로
활용성은 충분하다.


한가지 더 재밌는 특징은 각 부분에 break문을
걸어주어야 한다는 점이다.

혹시 break를 안걸면 자연스럽게 다음 부분으로
넘어갈 수 있게 설계되었나 싶었지만
그냥 빨간줄이 뜬다.

그럼에도 break 걸지않고 내려가는 방법이 있나
싶어서 찾아봤는데, 역시 있었다.
case 내용을 비우거나, continue를 쓰는 방법이다.

int age = 20;
  
switch(age) {
  case 20:
  	print("성인 1년차군요");
    continue default;
  case 21:
    print("성인 2년차군요");
    break;
  case 22:
    print("남자라면 높은 확률로 군대에 있겠군요");
    break;
  default:
    print("당신의 나이는 ${age}살이군요");
}

// 성인 1년차 혹은 2년차군요

위와 같이 해당 케이스를 비워두면 바로 아래
케이스가 실행된다. 이렇게 쓰게 둘바에는
그냥 case 20, 21, 22: 이런식으로 쓰게 뒀으면
어땠을까 싶긴하다.

continue 활용법은 이렇다.

int age = 20;
  
switch(age) {
  case 20:
    print("성인 1년차군요");
    continue defaultCase;
  case 21:
    print("성인 2년차군요");
    break;
  case 22:
    print("남자라면 높은 확률로 군대에 있겠군요");
    break;
  defaultCase:      
  default:
    print("당신의 나이는 ${age}살이군요");
}
// 성인 1년차군요
// 당신의 나이는 20살이군요
  

이렇게 break 대신 continue를 써주고,
이어질 부분에 대한 명시를 해주면 된다.

이러면 명시된 부분에서 코드가 진행된다.

뭔가 낯설지만 잘 활용하면 유용해보인다.



3. for loop

기본적인 사용법은 c계열과 같다.

for(int i = 1; i < 6; i++) {
  print("현재 ${i}번째 실행입니다.");
}

// 현재 1번째 실행입니다.
// 현재 2번째 실행입니다.
// 현재 3번째 실행입니다.
// 현재 4번째 실행입니다.
// 현재 5번째 실행입니다.

활용이야 워낙 무궁무진하니 케이스에 맞게
사용하면 되는거고, 중요한건 forEach를
활용할 수 있냐는건데, 결론은 활용 가능하다 :)

List<String> msgs = ["첫번째 메세지", "두번째 메세지", "세번째 메세지"];
  
msgs.forEach((msg) {
  print(msg);  
});

// 첫번째 메세지
// 두번째 메세지
// 세번째 메세지

// 아래처럼 간략하게도 표현 가능
msgs.forEach((msg) => print(msg));

// 첫번째 메세지
// 두번째 메세지
// 세번째 메세지

더하여, 파이썬의 enumerate와 같이
인덱스와 value를 동시에 받을 수 있는 방안도
찾아봤는데, 기본 제공 메소드로는 없다.

그래서 collection을 import 해서 쓰나보다.

import 'package:collection/collection.dart';

void main() {
  
  List<String> msgs = ["첫번째 메세지", "두번째 메세지", "세번째 메세지"];
  
  msgs.forEachIndexed((index, msg) => print("인덱스: ${index}, 메세지: ${msg}"));
  
  // 인덱스: 0, 메세지: 첫번째 메세지
  // 인덱스: 1, 메세지: 두번째 메세지
  // 인덱스: 2, 메세지: 세번째 메세지
  
}

swift에서는 index와 value를 동시에 받는게
생각보다 까다로웠던 기억이 있었는데,

다트에서는 코틀린처럼 직관적으로
forEachIndexed를 활용할 수 있어 좋아보인다.


forEach를 활용할 수도 있지만,
for in문을 통해 파이썬과 같은 형태로
loop를 돌릴수도 있다.

List<String> msgs = ["첫번째 메세지", "두번째 메세지", "세번째 메세지"];
  
for(String msg in msgs) {
  print(msg);
}

// 첫번째 메세지
// 두번째 메세지
// 세번째 메세지

이렇게 써놓고보니 c스러운 기존 for loop을
실제 애플리케이션에 활용할 일이 있을까 싶긴하다.

참고로 List뿐만 아니라 Map이나 Set에서도
위와같은 operator를 자유롭게 활용할 수 있다.



4. while loop

표기만 보고 넘어가도록하자.

int i = 1;

while(i < 6) {
  print(i);
  i++;
}
// 1 2 3 4 5

아무래도 앱을 만들다보니
메모리 누수 걱정을 자주 하게된다.

때문에 while처럼 설계가 중요한 제어문은
선뜻 활용하지 못하는 경우가 많았다.

위 예는 양반이고,
중간에 break를 걸어야만 끝나는 loop라면
버그가 났을때 치명적일수 있다.

고로 웬만하면 for loop 쓰자..



5. 예외처리

요즘 앱에서 서버와의 통신은 거의 필수다.
카페자리에서는 통신간 발생하는 에러를 잡을때
try catch문이 가장 많이 활용된다.

try {통신}
catch(400번 에러) { validation 에러 처리 }
catch(409번 에러) { 서비스 로직 에러 처리 }
이런식이다.

먼저 에러를 throw해보자.

void main() {
  
  print(addIntNumbers(1, 2));    // 3
  print(addIntNumbers(1, 2.5));  // Uncaught Error: Exception
  
}

int addIntNumbers(dynamic num1, dynamic num2) {
  if(num1.runtimeType != int || num2.runtimeType != int) {
    throw Exception;
  }
  return num1 + num2;
}

위와 같이 throw를 통해 exception을 일으킬 수 있다.
아직 자세하게 활용해보지는 않았지만,
Exception을 상속하여 custom error를
다룰 수 있을 것으로 예상된다.

이제 try catch문을 써보자

void main() {

  try {
    print(addIntNumbers(1, 2.5));
  } catch(Exception) {
    print("잡았다 요놈");   // 잡았다 요놈
  }
  
}

int addIntNumbers(dynamic num1, dynamic num2) {
  if(num1.runtimeType != int || num2.runtimeType != int) {
    throw Exception;
  }
  return num1 + num2;
}

다른 언어의 exception과 크게 다르지 않아보인다.
덧붙여 finally를 사용하면 예외 여부와 관계없이
특정 활동을 강제할 수도 있다.



6. assert문

다트에서는 assert라는 재밌는 놈이 있어 써본다.
위 사례와 같이, 보통 원하는 예외처리를 위해서는
if 등을 통해 원하는 조건에서 throw를 써야하는데,
assert를 쓰면 조금더 쉬워보인다.

위에서 활용한 예제를 그대로 assert문으로 바꿔보자.

void main() {
  
  try {
    print(addIntNumbers(1, 2.5));
  } catch(Exception) {
    print("잡았다 요놈");  // 잡았다 요놈
  }
  
}

int addIntNumbers(dynamic num1, dynamic num2) {
  assert(num1.runtimeType != int, Exception);
  assert(num1.runtimeType != int, Exception);
  return num1 + num2;
}

이건 진짜 유용해보인다!!

안그래도 dart언어가 잦은 indent때문에
가독성 떨어지기로 유명하기 때문에,
if를 남발하는건 헬코딩에 지름길이다.

이런 유용한 operator로 가독성 높이고
행코하는게 목표다ㅋㅋㅋ



정리

더 많은 제어문이 있겠지만, 자주 활용하는
제어문은 이정도면 정리가 된것 같다.

아직 훑어보기만 한 수준이지만,
다트는 참 잘 설계된 언어라는 느낌을 받았다.

다음에 다룰 함수와 class부분에서도
이 생각이 유지되길 바라본다!

profile
냅다 회사부터 세워버린 개발자

0개의 댓글