세계는 균형이 잘 잡혀있어야 한다. 양과 음, 빛과 어둠 그리고 왼쪽 괄호와 오른쪽 괄호처럼 말이다. 정민이의 임무는 어떤 문자열이 주어졌을 때, 괄호들의 균형이 잘 맞춰져 있는지 판단하는 프로그램을 짜는 것이다.
문자열에 포함되는 괄호는 소괄호("()") 와 대괄호("[]")로 2종류이고, 문자열이 균형을 이루는 조건은 아래와 같다.
정민이를 도와 문자열이 주어졌을 때 균형잡힌 문자열인지 아닌지를 판단해보자.
💻 입력
각 문자열은 마지막 글자를 제외하고 영문 알파벳, 공백, 소괄호("( )"), 대괄호("[ ]")로 이루어져 있으며, 온점(".")으로 끝나고, 길이는 100글자보다 작거나 같다.
입력의 종료조건으로 맨 마지막에 온점 하나(".")가 들어온다.
📃 출력
각 줄마다 해당 문자열이 균형을 이루고 있으면 "yes"를, 아니면 "no"를 출력한다.
예제 입력
So when I die (the [first] I will see in (heaven) is a score list).
[ first in ] ( first out ).
Half Moon tonight (At least it is better than no Moon at all].
A rope may form )( a trail in a maze.
Help( I[m being held prisoner in a fortune cookie factory)].
([ (([( [ ] ) ( ) (( ))] )) ]).
.
.
예제 출력
yes
yes
no
no
no
yes
yes
힌트
7번째의 " ."와 같이 괄호가 하나도 없는 경우도 균형잡힌 문자열로 간주할 수 있다.
풀이
저번에 풀었던 유효한 괄호 문자열인지 찾는 문제와
프로그래머스 올바른 괄호 https://school.programmers.co.kr/learn/courses/30/lessons/12909
문제를 풀었던 queue 방식을 그대로 활용해서 풀어보았다.
function bracketCheck(str) {
// 여는 괄호를 추가할 배열
const arr = [];
// 괄호가 올바르게 열고 닫혔는지 체크하는 객체 check
const check = {
"[": "]",
"(": ")",
};
for (let i = 0; i < str.length; i++) {
const open = str[i];
// str[i]가 check에 값이 있을 경우 = 열린 괄호인 경우
if (check[open]) {
// 열린 괄호(키)의 값(닫힌 괄호)를 판별하고
// 열린 괄호의 짝이 맞는지 확인하기 위해 arr에 추가
arr.push(open);
// 닫힌 괄호인 경우
} else if (open == "]" || open == ")") {
// 마지막 열린 괄호의 짝을 확인하기 위해 pop으로 제거하고
// close에 삽입
const close = arr.pop();
// check의 키(close)(여는 괄호)에 대응하는 값(닫는 괄호)인지 판별
if (check[close] !== open) return "no";
}
}
return arr.length == 0 ? "yes" : "no";
}
JS는 웹 페이지의 동적인 효과를 주기 위해서 만들어진 스크립트 언어
Node.js의 등장으로 백엔드에서도 JS를 사용할 수 있게 되었다.
프론트단에서의 에러는 사용자가 보는 뷰의 일관성을 해칠 수는 있어도 동작에 피해가 생기지 않는다.
백엔드단에서의 에러는 서버가 다운되어 웹 페이지에 접속할 수 없는 심각한 문제를 초래할 수 있다.
런타임에서 결정되는 변수 타입
개발자의 실수로 인한 오류를 찾기 쉽지 않다.
런타임 단계에서의 변수 값과 타입을 확인해야하는 번거로움이 있다.
약한 타입 체크
JS는 let, const 같이 변수/상수 구분 정도만 지원한다.
숫자 타입으로 초기화한 후 문자열을 할당해서 문제가 될 수 있다. -> 디버깅 어려움
너무나도 물렁한 객체
const obj = { latitude: 11.5, longitude: 47.1 };
const result = obj.latitude * obj.longitute; // 보통은 이런 실수는 컴파일러가 잡아줘야 되는데
console.log(result) // NaN이라는 엉뚱한 값 출현!
오타 등을 컴파일러가 찾아줘야하지만 그렇지 않다.
JS에서는 개발자가 마음대로 객체의 성질을 변화시킬 수 있다.
-> 없는 속성을 추가하거나 속성의 타입을 변경할 수있다. -> 안정성 저하
JS에서
function add(a,b) {
return a + b;
}
const result = add(1, "2");
console.log(result); // "12"
console.log("result type: ", typeof result ) // string
TS에서
입력하는 순간 에러 발생
컴파일 단계에서도 에러 발생
타입 체크
정의되지 않은 프로퍼티 연산
JS에서는 접근 제한자를 사용할 수 없으나
TS에서는 접근 제한자를 사용해서 클래스의 속성을 마음대로 접근하지 못하게 할 수 있다.
class Person {
private name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
public growOlder(): void {
this.age += 1;
}
}
const people = new Person('honggildong', 30);
people.age = 25;
// Error: Property 'age' is private and only accessible within class 'Person'.
people.growOlder();
console.log(people.age);
// Error: Property 'age' is private and only accessible within class 'Person'.
@types 라는 패키지를 사용해서 d.ts라는 확장자의 선언 파일을 통해 외부 모듈 타입 정보를 제공할 수 있다.
JS라이브러리를 TS에서 안전하게 사용하기 위해서 사용한다.
TS 개발자의 수요가 꾸준히 증가 추세에 있다.
TS는 프론트엔드, 백엔드를 가리지 않고 알아야하는 기술 스택이 되었다.
대부분의 JS 프로젝트가 TS로 전환하는 추세
새로운 프로젝트는 TS를 사용해 개발
정적 타입 시스템, 객체 지향 프로그래밍, 디자인 패턴 등 고급 프로그래밍 개념을 학습할 수 있다.
전문성이 높은 개발자로 성장 가능, 다양한 프로젝트에서 활용 가능한 지식 획득
컴파일러
는 프로그래밍 언어로 작성된 소스 코드 → 다른 프로그래밍 언어로 변환하는 도구
변환 과정에서 컴파일러는 소스 코드의 구문과 구조를 검사하여 문제가 없는지 확인
개발자가 작성한 코드에 오류가 있는 경우 미리 알려주어 문제를 해결
소스 코드의 정적 타입 검사
개발자는 코드에서 타입 관련 오류를 미리 발견하고 수정 가능
Typescript -> Javascipt 코드변환
변환된 코드가 어떻게 실행되는지 예측 가능 -> 디버깅에 도움
코드 최적화 -> 전반적인 애플리케이션 실행 시간이 빨라짐
컴파일러가 자동으로 수행
Typescript 프로젝트 내 코드들이 어떤 Javascript 버전으로 변환할 지 정하는 옵션
es5
, es2016
등의 컴파일 버전 설정
프로젝트 동작 환경을 고려하여 설정
Typescript 파일을 컴파일한 후 생성되는 Javascript 모듈의 형식을 지정
모듈을 가져오고 보내는 방식을 결정하는 옵션
target 옵션과는 서로 독립적인 관계 // es5로 설정해도 모듈 방식 사용가능
컴파일된 Javascript 파일이 저장될 출력 디렉토리 지정
"outDir": "dist" -> dist 폴더에 저장
엄격한 타입 검사 옵션을 모두 활성화하는 옵션 // true
실수를 미리 찾아낼 수 있다.
nullCheck, functionType, noimlicitAny 등이 자동으로 true
설정됨
컴파일된 Javascript 파일에 대한 소스 맵을 생성하는 옵션
소스 맵을 사용하면 에러가 발생했을 때 원래 Typescript 소스 코드의 위치를 확인 가능
디버깅에 큰 도움이 되기 때문에 개발 환경에서는 true
설정 권장
tsc가 컴파일할 때 포함하거나 제외할 파일이나 디렉토리를 지정하는 옵션
“include": ["src/*/"]
"exclude": ["node_modules", "dist"]
Javascript 라이브러리도 Typescript 코드에서 사용할 수 있게 해준다.
외부 라이브러리의 함수 타입 정보
외부 라이브러리 클래스 타입 정보
외부 라이브러리 객체 타입 정보
외부 라이브러리의 타입 추론 -> 명시적 타입이 아닐 경우 추론하는 것
@types 라이브러리를 통해 외부 라이브러리에 대한 타입 정도를 제공한다
Typescript 프로젝트에서도 Javascript 라이브러리를 한 줄도 수정하지 않고 그대로 사용할 수 있다
변수
는 데이터를 저장하는 공간
저장되는 데이터는 여러가지 타입이 존재
타입을 올바르게 사용하면 가독성, 안정성이 향상
잘못된 타입을 사용하거나 예상치 못한 타입으로 인해 오류가 발생
JS에서는 런타임에서 오류가 발생하기 때문에 찾기 어렵다
타입을 제대로 사용하면 오류를 미리 방지할 수 있다
타입 안정성은 코드가 예상한 타입대로 동작함을 보장한다
타입 안정성이 높을 수록 런타임에 발생할 수 있는 오류를 줄일 수 있다
코드의 안정성이 향상되어 테스트와 디버깅 시간을 줄일 수 있다
타입을 명확히 명시하면 가독성이 향상된다
다른 개발자가 이해하기 쉽기 때문에 유지 보수가 용이해진다
2가지의 상태를 표현하고 싶은 경우 사용
true || false
조건문, 비교 연산 등에 사용
모든 숫자 타입
Typescript에서는 모든 수치 연산에 사용되는 값은 number로 명시
텍스트 데이터 타입
작은 따옴표(’), 큰 따옴표(”), 백틱(` 템플릿 리터럴) 를 사용하여 문자열을 표현
기본 타입에 []가 붙은 형태
참조 타입
const testScores: number[] = [90, 85, 78, 92, 88];
서로 다른 타입의 원소를 순서에 맞게 가질 수 있는 특수한 배열
정의된 데이터 타입의 개수와 순서에 맞추어 저장을 하는 것이 필수
const person: [string, number, boolean] = ['Spartan', 25, false];
const person2: [string, number, boolean] = [25, 'Spartan', false]; // 오류!
열거형 데이터 타입
명확하게 관련된 상수 값들을 그룹화할 때 사용
각 요소는 값이 설정되어 있지 않으면 숫자 0으로 시작
let 키워드를 사용해서 변수를 선언할 수 있다
let으로 선언된 변수는 값을 재할당(변경)할 수 있다
const 키워드를 사용해서 상수를 선언할 수 있다
const로 선언된 상수는 값을 재할당(변경)할 수 없다
Typescript에서 객체의 속성 불변을 만드는데 사용하는 키워드이다
클래스의 속성이나 인터페이스의 속성을 변경할 수 없게 만들 때 사용한다
클래스의 속성에는 const 키워드를 사용할 수 없다
어쩔 수 없이 가변 타입의 데이터를 저장하고 싶다면
unkown
을 사용
일일이 정의할 수 있다면union
을 사용
Typescript에서 any 타입은 모든 타입의 슈퍼 타입
이다
Javascript에서 object 타입처럼 최상위 타입
Typescript에서는 가급적 사용하지 않아야 한다
any 타입과 유사하지만 더 안전한 방식으로 동작한다
unknown 타입의 변수를 다른 타입의 변수에 할당하려면 명시적으로 타입을 확인해야한다
let unknownValue: unknown = '문자열타입';
let stringValue: string;
stringValue = unknownValue; // 에러 발생!
stringValue = unknownValue as string; // Type Assertion(타입 단언)
unknown 타입도 재할당이 일어나지않으면 타입 안전성이 보장되지 않는다
union은 여러 타입 중 하나를 가질 수 있는 변수를 선언할 때 사용한다
| 연산자를 사용하여 여러 타입을 결합하여 표현한다
type StringOrNumber = string | number;