[TIL] JavaScript & TypeScript Essential - Chapter 6
2023-2-4
- Fastcampus
- 김민태의 프론트엔드 아카데미
5장은 로그인, 회원가입 등 실전예제에 대한 내용이다. 순서대로라면 5장을 들어야하지만, 선생님께서 하신 말씀을 요약하자면 아래와 같다.
5장은 다른 장에 비해 강의 시간이 짧은 편이다. 덜 중요해서가 아니고, 가장 중요한 장이지만 개발 공부에 대한 가이드를 잡아주고 나머지는 여러분이 자가 학습 역량을 발휘해 스스로 하기를 바라는 마음에서이다.
예제들은 시작점이라고 생각하고, 각각의 앱들에 살을 붙여가면서 여러 아키텍쳐/방법론을 적용해보면 좋겠다. 다른 장들을 수강한 다음에 5장 수강을 권한다.
알겠습니다! 6장으로 감미다.
6장의 타이틀은 '참조 사전'으로, 문법/개념/기법 등에 대한 전반적인 내용이다.
기본적인 내용들도 많지만, 개념들을 다잡기 위해 건너뛰진 않고 빠르게 훑으면서 수강했다.
6-1. 문법-식별자
- 변수, 함수, 속성명을 작성할때 유니코드 글자, $ _ 숫자로 구성할 수 있지만, 숫자로 시작할 수 없다.
- 공백 사용 불가.
- 길이제한은 딱히 없으나 가독성을 위해 적당한 길이로 작성.
- 코드는 데이터로 변환할 수 없지만 데이터는 식별자로 변환할 수 있다. ex.
object['key']
컨밴션
- 상수는 대문자로 이름 짓는다.
- 변수 이름은 카멜 케이스, 스네이크 케이스를 주로 쓰지만 카멜 케이스가 가장 흔히 쓰인다.
6-2. 문법-값(value)
- 자바스크립트에서 값이라는 주제는 굉장히 광범위한 주제이다. 100시간을 해도 모자랄 정도로 내용이 방대하다.
- 다른언어와 굉장히 다른부분들도 있다. 자바스크립트는 값으로 취급하는 범위가 굉장히 넓음.
- 기본 자료형. 자바스크립트 내에서 코드가 아닌 데이터로서의 규격들이 어떤 것이 있는가 하는것.
튜플 (Tuple)
튜플 타입을 사용하면, 요소의 타입과 개수가 고정된 배열을 표현할 수 있습니다. 단 요소들의 타입이 모두 같을 필요는 없습니다. 예를 들어, number, string 이 쌍으로 있는 값을 나타내고 싶을 수 있습니다.
📌 출처 - 튜플 | 기본타입 | typescript GitBook
// 튜플 타입으로 선언
let x: [string, number];
// 초기화
x = ["hello", 10]; // 성공
// 잘못된 초기화
x = [10, "hello"]; // 오류
첫번째는 문자열만, 두번째는 숫자만 들어가는 배열을 만들고싶어! 할때 사용.
열거형 (Enum)
JavaScript의 표준 자료형 집합과 사용하면 도움이 될만한 데이터 형은 enum입니다. C# 같은 언어처럼, enum은 값의 집합에 더 나은 이름을 붙여줄 수 있습니다. 📌 출처 - 튜플 | 기본타입 | typescript GitBook
// javascript
const Color = {
Red: 1,
Blue: 2,
Green: 3
}
// typescript
enum Color {
Red, Blue, Green
}
선생님은 개인적으로 enum을 선호하지 않으신다고한다. enum이라고하는 데이터 타입은 코드이고 객체는 데이터일 수 있다.
enum은 코드이기 때문에, 만약 서버나 외부로부터 어떤 데이터를 가져오고 그 데이터를 객체로 표현해서 실제 UI에서 다루게 될 때. Color라고 하는 데이터를 가지고 왔다고 생각해보면, 그 데이터를 enum타입으로 변환시키기가 까다롭다. object는 그냥 변환해서 사용하면 되는데 enum은 데이터가 아니기 때문에 어려운 부분이 있다.
enum 타입의 데이터가 실제로 주고받는 용도의 데이터일 수 있는지 아닌지. 코드 내에서만 존재하면 되는지 판단해서 enum/object를 선택하면 된다.
(enum은 앞글자를 대문자로 쓰는 컨밴션이 있나보다.)
Any
javascript 개발자가 typescript로 전환할때 점진적으로 할 수 있게 하기위단 수단 같은 것. 미끼 상품 같은 것 ㅎ
Void
- 자바스크립트에 존재는 하지만 잘 사용하지 않는 타입.
- 타입스크립트에서는 많이 사용함.
- 함수가 리턴값이 없을때 대표적으로 많이 사용함.
Never
never
타입은 절대 발생할 수 없는 타입을 나타냅니다. 예를 들어,never
는 함수 표현식이나 화살표 함수 표현식에서 항상 오류를 발생시키거나 절대 반환하지 않는 반환 타입으로 쓰입니다. 변수 또한 타입 가드에 의해 아무 타입도 얻지 못하게 좁혀지면never
타입을 얻게 될 수 있습니다.
never
타입은 모든 타입에 할당 가능한 하위 타입입니다. 하지만 어떤 타입도never
에 할당할 수 있거나, 하위 타입이 아닙니다.(never
자신은 제외) 심지어any
도never
에 할당할 수 없습니다.
never
를 반환하는 몇 가지 예제입니다:
📌 출처 - Never | 기본타입 | typescript GitBook
// never를 반환하는 함수는 함수의 마지막에 도달할 수 없다.
function error(message: string): never {
throw new Error(message);
}
// 반환 타입이 never로 추론된다.
function fail() {
return error("Something failed");
}
// never를 반환하는 함수는 함수의 마지막에 도달할 수 없다.
function infiniteLoop(): never {
while (true) {
}
}
객체
- 자바스크립트 코드 거의 대부분이 객체로 되어있다.
- 언어레벨에서 값으로 취급당하는것은 무엇이든 변수에 넣을 수 있다는 원칙이 있다.
- 자바스크립트/타입스크립트가 값이라고 하는 것을 데이터보다 훨씬 더 확장해놨다.
- object도 값이라고 얘기를 하고 있는데, 객체 안에는 값도 있지만 코드도 들어있다.
- 이 특징을 가지고 할 수 있는 프로그래밍 방식이 엄청 많다.
6-3. 문법-타입
자바스크립트와 타입스크립트에서 타입을 다루는 방식의 차이에 대해서 구체적으로 설명.
javascript
- 값을 넣는 순간 타입이 결정됨.
- 데이터를 변수에 들어가는 시점에 자유롭게 바꾼다는 매커니즘을 가지고 있는 언어.
- 타입을 느슨하게 관리함. 타입이 없는건 아님.
- 편리하지만 큰 불안정성이라고 알 수 있다.
숫자를 인자로 받아야하는데 string을 넣었다면? 의도치 않는 에러가 나게됨.
function addAge(age) {
return age + 1
}
console.log(addAge('30'))
이걸 예방하기 위해서는 age의 타입을 체크해야하고, 이 에러는 런타임에 나는 것이기 때문에 처리에 대해서도 고민해야함.
typescript
- 타입을 지정하고 체크
- 런타임이 아니고 컴파일 타임에 에러를 잡아줌.
- 따라서 원천적으로 에러를 예방.
- 타입스크립트가 제공해주는 가장 큰 가치!
6-4. 문법-변수(&상수)
변수 or 상수? 상수를 많이 쓰는 것을 추천.
변하는 값을 써야할때만 변수를 사용하라.
값 자체가 변해야된다는 것은 불안정성을 내포하고 있는 것.
6-5. 문법-식(연산 또는 계산)
- 식 - 하나의 값으로 환원됨. (세미콜론 필요) 식=값
- 문 - 세미콜론 노필요.
if
,for
문 세미콜론 찍어봤자 아무 의미 없음
괄호로 감싸면 값이된다. → 여러 부분에서 세세하게 쓰이는 방법이니 기억하길바람.
6-6. 문법-참조와 복사
참조와 복사는 간단하지만 중요한 메커니즘 중 하나.
원시값은 완전히 복사된다. a=10
, b=10
, a=b
로 해도 a
와 b
에 관계성이 생긴것은 아님.
그에 반해 객체는 항상 참조된다. 때문에 특정 함수 내부에서 값을 변경해버리면 그 이후에 작성된 로직이 값이 바뀌지 않았다는걸 전제로 작성되었을때 에러가 난다.
객체가 참조된다는것을 꼭 인지하고 코드를 짜야 안전한 코드를 작성할 수 있다.
6-7. 문법-조건(분기)문
if문의 코드가 한줄일때는 {}가 생략가능하지만. 추천하지 않는다. 코드라는것은 변화하고 2-3줄이 될수있다.
6-8. 문법-반복문
패스
6-9. 문법-예외
이렇게만 작성하면 에러가 던져지면서 어플리케이션이 종료된다.
function throwError() {
throw new Error('앗 오류!')
}
function init() {
throwError()
}
init()
에러를 던지기만 하는것이 아니라 받아주면 종료가 되지 않는데, 이때 try catch
문을쓴다.
function throwError() {
throw new Error('앗 오류!')
}
function init() {
try {
throwError()
} catch(e) {
console.log(e)
} finally {
// 모든 상황에서 실행됨.
console.log('done')
}
}
init()
이렇게 작성하면 단순히 오류가 나는 것이 아니라, 오류를 처리한것이된다. 사용자에게 어떠한 피드백을 줄 수 있는 코드가 된 것.
위 예제는 try
문에 에러를 던지는 함수가 바로 들어가 있지만, 해당 함수에서 에러를 직접적으로 던지지 않아도 에러는 캐치할 수 있다.
catch
를 만날때까지 계속 위로 타고타고 에러가 올라간다. 그리고 마침내 잡히지 않으면 사이트가 종료됨.
function throwError() {
throw new Error('앗 오류!')
}
function yesYesYes() {
return true
}
function doSome(isTrue) {
if (isTrue) {
throwError()
} else {
yesYesYes()
}
}
function init() {
try {
doSome(true)
} catch(e) {
console.log(e)
} finally {
// 모든 상황에서 실행됨.
console.log('done')
}
}
init()
return
값을 false
로 해서 매번 if
문 판정으로 오류 여부를 확인하는 것 보다 예외로 에러를 던져서 처리하면 효과적인 코드 구조를 만들 수 있다.
6-10. 문법-인터페이스와 타입 별칭
타입알리아스
number는 숫자라는 의미만 있다. 좌표인지 갯수인지?
const x: number = 10
이렇게 변수명에 의미를 부여하는 방법이 있겠지만
const xPosition: number = 10
number라는 타입 자체에 의미를 부여하고싶다? 이럴때 쓰는 것이 타입별칭. 타입 알리아스라고 하는 것이다.
type을 앞에 붙이고 네이밍 컨밴션으로 앞글자에 대문자를 주로 사용한다.
type Position = number;
const x: Position = 10
타입으로 기술된 것은 컴파일 타임에 이 값이 들어갔는지 아닌지 확인하는것이고, enum
타입은 실제 데이터로 컴파일 타임이 아니라 런타임에 값이 실제로 들어가는것. 특정 값으로 제한하는 기능은 유사하지만 이넘은 실제 데이터이고 타입은 컴파일 타임에 검사하는 용도로 사용됨.
인터페이스
- 객체 규격 정의
- 타입알리아스와 인터페이스 동일하게 정의해놨다면, 모든 결과가 똑같다.
- 각각 언제 사용할지는 개발자가 정의하면된다.
- 인터페이스는 이름이 중복되면 내부 정의들을 합치는 특성이있고. 타입알리아스는 이름이 중복되면 에러를 뱉는다. 하지만 인터페이스를 중복되게 작성하는것은 지양하는것이 좋다.
- 인터페이스는 extends키워드로 인터페이스 확장 가능
- 타입알리아스는 인터섹션 기능으로 인터페이스 확장 가능
이건 어떤 key이름이 어떤 데이터든 모두 숫자인 값만 가지고 있는 인터페이스이다.
interface OnlyNumberValueObject {
[key: string]: number;
}
함수 규격도 만들 수 있다.
interface IGetApi {
(url: string, search?: string): Promise<string>
}
타입알리아스로 함수 규격을 작성할때엔 화살표함수, brace 2가지 방법이 있다.
함수 규격을 함수에 사용할땐 항상 함수 정의문이 아닌 함수 표현식을 작성해야 한다.
인터페이스는 항상 퍼블릭만 다룬다. 따라서 인터페이스를 class에서 구현할때 인터페이스의 특정 값을 private으로 지정할 수 없다.
클래스의 규격과 생성자가 만들어내는 인스턴스의 규격이 미묘하게 다를때가 있을 수 있다. 그래서 인터페이스를 이용해서 생성자의 규격을 기술할 수 있다.
interface IRectConstructor {
new (x: number, y: number, width: number, height: number): IRect
}
new 키워드를 사용해서 생성자 인스턴스를 만드는 것은 이런 인터페이스가 필요없다. 해당 클래스 자체가 설계도이기 때문. 클래스 자체는 설계도이기 때문에 타입으로 사용 가능.
하지만 생성자 인스턴스를 함수 안에서 만들게되면 스펙이 필요하다.
const rect1 = new Rect(0, 0, 100, 20)
function createDefaultRect(cstor: IRectConstructor) {
return new cstor(0, 0, 100, 100)
}
기본적으로 타입알리아스, 인터페이스 어떤것을 쓰든 크게 상관은 없을듯 하다. 하지만 일관된 원칙을 세우고 코드를 작성하는게 좋다.
선생님 개인적인 스타일은 데이터를 표현할때는 타입알리아스를 쓰고.
메소드와 같은 구체적인 행위까지 포함된 객체를 표현할때는 인터페이스를 주로 쓴다. 클래스는 당연히 데이터와 행위를 포괄하고 있기 때문에 인터페이스가 자연스럽다고 생각한다.
수정이 필요한 부분 혹은 더 나은 방법을 알고계신가요?
댓글로 알려주시면 저에게 큰 도움이 됩니다! 😊💜