컴퓨터 공부/🐦‍⬛ Swift

[Swift 공부일지] 기본 문법에 대한 소개 - 변수

letzgorats 2023. 11. 8. 13:38

 

Swift를 공부하면서, 필요한 공부도 진행해보도록 하겠습니다 😎

👉 변수 👈

 

예시 코드를 보면서 개념을 살펴보겠습니다.

import SwiftUI

struct Variable2: View{

    let name: String = "Allu"
    let age: Int = 10
    let height: Double = 8.2
    let isCute: Bool = true
    
    var body: some View{
        Text("Hello, \(name) \(age) \(weight) \(isCute.description)" )
    }
}

struct Variable2_Previews: PreviewProvider{
    static var previews: some View{
        Variable2()
    }
}

 

- Vstack 은 Vertical Stack으로 수직적으로 객체를 쌓는 것입니다. 1개의 Vstack 안에는 객체가 10개이상이 들어갈 수 없습니다.

 

- Text("/(name)" 처럼 역슬래시를 하고 변수를 넣으면 해당 변수가 할당됩니다.

 

- 변수의 타입을 명시하고 싶을 때는

let age: Int = 27

 처럼 콜론을 사용합니다.

 

- boolean은 상태를 나타내는 것이므로, 상태를 나타내기 위해서는 설명을 붙여줘야 합니다. boolean 변수 뒤에 '.description' 을 붙여줘야 합니다.

\(isCute.description)

 


※  변수를 선언하는 방법, 키워드는 어떤 것들이 있나요? 그 차이가 무엇인가요?

var

1. 함수 레벨 scope

  • 함수의 코드 블록만을 scope로 인정해서 for문이나 if문 등에서 var 키워드로 선언한 변수를 코드 블록 외부에서 참조할 수 있습니다.
  • 전역 함수 외부의 변수는 모두 전역 변수가 됩니다.
if(true) {
    var test = "test"; // 전역 변수
}

console.log(test); // "test", 블록 외부에서 참조 가능
test = "changed";
console.log(test); // "changed", 블록 외부에서 재할당도 가능

 

2. var 키워드 생략 허용

  • 암묵적 전역 변수를 양산할 가능성이 큽니다.
test = "test";
console.log(test); // "test"

 

3. 변수 중복 선언 허용

  • 의도하지 않은 변수값의 변경이 일어날 가능성이 큽니다.
var test = "test";
console.log(test); // "test"
var test = "changed";
console.log(test); // "changed"

 

4. 변수 호이스팅

  • 변수를 선언하기 이전에 참조할 수 있습니다.
  • undefined를 반환하며, 에러는 발생하지 않습니다.
console.log(test); // "undefined"
var test = "test";

 

위와 같은 특징들로, 유의해서 var 키워드를 써야 합니다. 특히, 함수 레벨 스코프를 따르기 때문에 전역 변수가 의도치 않게 변경돼 문제를 일으킬 위험이 있습니다. 이러한 단점을 보완하기 위해 let과 const가 도입됐습니다.


let

1. 블록 레벨 scope

  • 모든 코드 블록을 scope로 인정하기 때문에, if문, for문 등에서 선언한 변수를 코드 블록 외부에서 참조할 수 없습니다.
if(true) {
    let test = "test"; // 지역 변수
}
console.log(test);
// Uncaught ReferenceError: test is not defined(에러 발생)

 

 

2. 변수 중복 선언 금지

let test = "test";
let test = "changed";
// Uncaught SyntaxError: Identifier 'test' has already been declared(에러 발생)

 

3. 호이스팅

  • 모든 선언(var, let, const, function, function*, class)을 호이스팅 합니다. 호이스팅은 선언문을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성입니다.
  • 하지만, var 키워드로 선언된 변수와는 달리 let 키워드로 선언된 변수를 선언문 이전에 참조하면, 참조 에러(ReferenceError)가 발생합니다. 이런 차이가 발생하는 이유는 변수 생성 과정에서 차이가 있기 때문입니다.
console.log(test);
let test = "test";
// Uncaught ReferenceError: test is not defined (에러 발생)

 

var 키워드로 선언된 변수는 '선언'과 '초기화' 단계가 한번에 진행됩니다. 따라서 변수 선언문 이전에 변수에 접근해도 scope에 변수가 존재하기 때문에 에러가 발생하지 않고 undefined를 반환하는 것이죠. 이후 변수 할당문에 도달하면 비로소 값이 할당됩니다. 이러한 현상을 변수 호이스팅이라 합니다.

 

반면, let 키워드로 선언된 변수는'선언 단계'와 '초기화 단계'가 분리돼 진행됩니다. scope에 변수를 등록(선언 단계)은 하지만 초기화 단계(메모리 확보)는 변수 선언문에 도달했을 때 이뤄집니다. 따라서 변수 선언문(초기화) 이전에 변수에 접근하려고 하면 참조 에러가 발생하는 것이죠. 변수를 위한 메모리 공간이 아직 확보되지 않았기 때문인 것 입니다. 

 

scope의 시작 지점부터 초기화 시작(변수 선언문) 지점까지의 구간을 일시적 사각지대(Temporal Dead Zone; TDZ)라고 합니다.

이렇게 보면 let, const 키워드로 선언된 변수는 호이스팅이 발생하지 않는 것과 차이가 없어보입니다. 하지만 분명히 호이스팅이 이뤄집니다.

아래의 예제를 보면 호이스팅이 발생하기 때문에 전역 변수가 콘솔에 출력이 되는 것이 아니라 참조 에러가 발생하는 것입니다.

let test = "global test"; // 전역 변수
{
  console.log(test); // Uncaught ReferenceError: Cannot access 'test' before initialization
  let test = "local test"; // 지역 변수
}

 

4. 클로저

  • 블록 레벨 scope를 지원하는 let은 var보다 직관적입니다. 함수 레벨 scope만 인정하는 var는 전역 변수로 인해 의도치 않은 실행 결과를 야기할 수 있기 때문에 클로저를 활용해야 할 때가 있기 때문입니다.

- var 키워드

var func = [];

// for 루프의 i는 전역 변수
for(var i = 0; i < 5; i++){
    funcs.push(function () { console.log(i); });
}

for (var j = 0; j < 5; j++) {
  funcs[j]();
}

 

위 코드의 결과로 차례로 0, 1, 2, 3, 4 를 호출하는 것이 아니라 4가 5번 호출됩니다. for 문의 var i 는 함수 레벨 scope를 가지고 있기 때문에 여기서는 전역 변수로 취급됩니다. 따라서, 0, 1, 2, 3, 4 를 출력하게 하려면 클로저를 활용해야 합니다.

 

- var 키워드 with 클로저

var funcs = [];

for (var i = 0; i < 5; i++) {
  function outer(index) { // index는 자유 변수
    funcs.push(function () {
      console.log(index);
    });
  }
  outer(i);
}

for (var j = 0; j < 5; j++) {
  funcs[j]();
}

 

반면 for문의 초기화 식에 let 키워드를 사용하면 클로저를 사용하지 않아도 위 코드와 동일한 동작을 합니다.

 

- let 키워드

var funcs = [];

// for 루프의 i는 지역 변수이면서 자유 변수
for (let i = 0; i < 5; i++) {
  funcs.push(function () { console.log(i); });
}

for (var j = 0; j < 5; j++) {
  console.dir(funcs[j]);
  funcs[j]();
}

for 문의 let i는 for루프에서만 유효한 지역 변수입니다. 또한, i는 자유 변수로서 for 루프의 생명주기가 종료되어도 변수 i를 참조하는 함수가 존재하는 한 계속 유지됩니다.

 

- 클로저 개념 짚고 넘어가기

  • 클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합입니다.
  • 렉시컬 환경(Lexical environment) : 현재 컨텍스트 내의 식별자들에 대한 정보와 외부 환경 정보이며, 변경 사항이 실시간으로 반영 된다.
function outerFunc() {
  var x = 10; // 자유 변수
  var innerFunc = function () { console.log(x); };
  return innerFunc;
}

var inner = outerFunc();
inner(); // 10

 

위 정의에서 말하는 '함수'란 반환된  내부 함수를 의미하고, '그 함수가 선언될 때의 렉시컬 환경(Lexical environment)'이란 내부 함수가 선언됐을 때의 scope를 의미합니다. 즉, 클로저는 반환된 내부함수가 자신이 선언되 선언됐을 떄의 환경(Lexical environment)인 scope를 기억하여 자신이 선언됐을 때의 환경(scope) 밖에서 호출되어도 그 환경(scope)에 접근할 수 있는 함수를 말합니다. 

이를 조금 더 간단히 말하면 "클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수다!" 라고 말할 수 있습니다.

 

클로저에 의해 참조되는 외부함수의 변수 즉 outerFunc 함수의 변수 x는 자유변수(Free variable)라고 부릅니다. 클로저라는 이름은 자유변수에 함수가 닫혀있다(closed)라는 의미로 의역하면 자유변수에 엮여있는 함수라는 뜻입니다.

 

5. 전역 객체와 let

  • 전역 객체는 모든 객체의 유일한 최상위 객체를 의미하며, 일반적으로 브라우저에서는 window객체, node.js에서는 global 객체를 뜻합니다.
  • var 키워드로 선언된 변수를 전역 변수로 사용하면 전역 객체의 프로퍼티가 됩니다. 반면 let 키워드로 선언된 변수를 전역 변수로 사용하면 보이지 않는 개념적인 블록 내에 존재하게 되어 전역 객체의 프로퍼티로 인식하지 않습니다.
var varTest = "var";
let letTest = "let";

console.log(window.varTest); // "var"
console.log(window.letTest); // undefined

const

  • const는 상수(변하지 않는 값)를 위해 사용합니다. 하지만 반드시 상수만을 위해 사용하지는 않는데, const의 특징은 let과 대부분 동일하나 일부 차이가 있습니다.

 

1. 재할당 금지

  • let은 재할당이 자유로우나 const는 재할당이 금지됩니다.
const test = "test";
test = "changed";
// Uncaught TypeError: Assignment to constant variable.
  • 또한 const는 반드시 선언과 동시에 할당이 이루어져야 합니다.
const test;
// caught SyntaxError: Missing initializer in const declaration

 

2. 상수

  • const는 재할당이 불가하고 let은 가능하기 때문에 사용 의도에 따라 구별하여 사용함으로써 가독성과 유지보수성을 높일 수 있습니다.
const independenceMovementDay = "19190301";
let today = "20231108";
  • const는 재할당이 불가하고 let은 가능하기 때문에 사용 의도에 따라 구별하여 사용함으로써 가독성과 유지보수성을 높일 수 있습니다.

tip ➡️ var 키워드는 예기치 못한 오류를 야기하기 쉽기 때문에 사용하지 않는 것이 좋습니다.

tip ➡️ 변수를 선언할 때에는 일단 const 키워드를 사용하고 추후 반드시 재할당이 필요하다면, let 키워드로 변경하는 것이 좋습니다.

 

3. const와 객체

  • const 변수의 타입이 객체인 경우에는 참조를 변경할 수 없으나 객체의 프로퍼티는 변경할 수 있습니다.
const user = { name: 'Allu' };

// 재할당 금지 (에러 발생)
user = {}; // Uncaught TypeError: Assignment to constant variable.
 
// 객체의 프로퍼티는 변경 가능
user.name = 'Letzgorats';
console.log(user); // { name: 'Letzgorats' }

참고


※  변수명으로 짓지 못하는 이름이 있나요?

- 유니코드를 포함한 어떤 문자든지 사용가능
( 단, 특수문자, 수학 기호, 화살표, 개인용 유니코드, 선 등 사용 할 수 없습니다. )
( 단, 숫자로 시작할 수 없습니다.)
( 단, 공백이 포함되면 안됩니다.)
( 단, 해당 코드 범위 내에서 미리 사용되는 기존 이름과 동일한 이름은 사용하지 말 것)

- 이름 읽었을 때 무슨 역할을 하는 건지 파악 가능해야 합니다.
- 명사, 동사 또는 전치사로 이루어진 단어를 연결하여 만듭니다.
- 시작 단어를 제외한 모든 단어의 시작은 대문자로 하고 그 이외의 모든 문자는 소문자로 합니다.
- 반복문을 사용할 때 index or employee 대신에 i,e를 사용하는 것이 좋습니다.
- 범위에 한 줄만 있어도 중괄호를 추가합니다.
- 대소문자를 구별해야 합니다. (Var != var)
- 스위프트에서 미리 정한 키워드 및 데이터 타입 이름은 사용할 수 없습니다
- 접두사 규칙 : 팀이니셜 혹은 다른 은유어들이 2자를 넘지 않도록 만듭니다.
- 변수명, 함수명을 지을 때 따로 정해진 규칙은 없지만 보통 "헝가리안 표기법" 지키면 좋습니다.

※  문자열이 더해지는 것은 당연한 것 같은데, 왜 빼지지는 않을까요?

chat gpt said

"

문자열을 빼는 것은 프로그래밍에서 일반적이지 않거나 직관적인 의미를 가지고 있지 않습니다. 따라서 Swift의 설계 선택은 문자열 연결(더하기)을 지원하는 반면, 문자열을 빼는 것은 지원하지 않는 것입니다. 이는 대부분의 프로그래밍 시나리오에서 명확한 사용 사례가 없기 때문입니다.

"


반응형