728x90
📍 21일 차 11.23. 화.(실시간 강의) (클린 코드와 타입 스크립트)
이번 주는 클린코드와 typescript의 타입, 클래스, 인터페이스, Generic, Decorator에 대해서 배웠다. 엘리스 엔지니어 SW 트랙을 신청할 당시 제일 기대가 되었던 커리큘럼이 이번 주차에 배우는 내용이었는데, 벌써 그 내용을 듣게 된다니.. 이전보다 더욱 열심히 들어야겠다. 여느 때와 마찬가지로 이론수업과 실습수업으로 나뉘었는데, 실습수업은 김병철 코치님이 가르쳐주셨다. 처음에는 누군지 잘 몰랐는데 알고리즘계에서 꽤 유명한 분이셨다. 개발자 오픈 채팅방에 코딩 테스트 대비 순서에 대해 물어보면 항상 tony9402 - 코딩테스트 대비 문제집부터 풀라는 얘기가 많았는데 그 레포지터리 컨트리뷰터의 2번째(VSfe)분이셨다. 😲😲 저번 이 고잉님이 가르쳐주실 때도 신기했는데 이번에도 엄청 신기했다.. 공부 의지가 한껏 솟아오르는 계기가 되었다.
❏ 클린 코드란
- 비즈니스 전문가 → 분석가(제품 관리자 / 서비스 기획자 / 프로그래머) → 프로그래머 → 컴퓨터
- 좌측 방향은 목적 / 추상적, 우측 방향은 수단 / 구체적
- 시간이 갈수록 코드를 쓰는 것보다 읽는 비중이 늘어난다.(코드 가독성의 중요성이 늘어남)
- 코드 재사용(반복되는 문제의 풀이는 재사용 가능, SW 개발 비용 절감)
💡 테스트 기법
- 수동 테스트
- 품질 담당자가 UI를 사용해 기능 검증
- 사람의 손으로 일일이 테스트한다.(인건비 증가)
- 소프트웨어 회귀(시간에 따라 기능이 많아지면, 추가한 기능과 기존의 기능과의 충돌로 인해 기존의 있던 코드조차 실행이 안 되는 현상이 발생한다.)
- 인수 테스트
- 배치된 시스템을 대상으로 검증
- 전체 시스템 이상 여부 신뢰도가 높음
- 높은 비용(작성 / 관리 / 실행 비용)
- 피드백 품질이 낮음(현상을 드러나지만 원인은 숨겨짐)
- 테스터가 품질 외부를 살펴본다.
- 자동 테스트
- 기능을 검증하는 코드를 작성
- 테스트 코드 작성 비용이 소비되지만 실행 비용이 낮고 결과의 신뢰도가 높음
- 테스트 코드 작성과 관리가 프로그래머 역량에 크게 영향을 받는다.
- 단위 테스트
- 시스템 일부(하위 시스템)를 대상으로 검증
- 낮은 비용
- 높은 피드백 품질
- 전체 시스템 이상 여부 신뢰도가 낮음
- 단위끼리 오류가 나는 경우가 있다.
- 테스터가 프로그램의 기능을 살펴본다.
❏ 타입 스크립트의 역사
- ES2015부터 모듈 스펙을 제공하기 시작: 프로젝트의 규모 증가, 개발환경이 복잡해짐,
npm의 등장, 번들러 등장(Webpack 등등) 주석제거 / 파일압축 / 어글리파이, 트랜스파일러 등장 (Babel, Typescript 등등) TS는JS의 모든 기능을 기본으로 제공한다. (슈퍼셋), 명시적인 데이터에 대한 유형 설명- 자바스크립트보다 타입에 대한 추가적인 정보를 얻을 수 있다.
- 안정성 있는 코드를 작성하기 위하여 별도의 타입 체크가 필요함
- 예상치 못한 오류가 발생할 수 있음
- 타입 체크를 위해 코드가 길어지는 문제 발생
- 실제 코드를 실행을 해보기 전까지 오류 체크 불가
- 실제 서비스 단계가 아닌 컴파일러 과정에서 오류를 확인할 수 있다.(디버깅, 새로고침 과정까지 가지 않아도 확인 가능함) 보다 안전한 프로그래밍 환경을 제공받을 수 있다.
JS
- 인터프리터 언어(한줄 한줄 실행)
- 스크립팅 언어
- 컴파일러 필요 없음
- 객체 지향적이지 않음, 프로토타입 기반
TS
- 컴파일 언어
- 객체 지향 프로그래밍 언어
- 컴파일러 필요
- 클래스 기반, 상속, 인터페이스, 수정자 사용 가능
728x90
❏ typescript의 type 알아보기
- 인터페이스의 경우, 컴파일하는 과정에서 인터페이스끼리 전부 합쳐지고 해당 인터페이스에 대응하는 객체를 생성해주는 과정을 거칩니다. (JS의 호이 스팅을 떠올릴 수 있지만, 실제 동작 과정은 약간 다릅니다. 일반적으로 인터페이스, 클래스 등의 정의는 최상단에 고정하고 하단에 추가하거나 수정하는 것은 지양하는 편입니다.)
// 타입 유틸리티
// 1. Tuple: 튜플, 보다 엄격하게 타입을 정의하고 싶을 때 사용
let x : [number, string];
x = [27, "AYW"];
// enum
enum Color {
Red, Green, Blue
}
console.log(Color)
👉🏽 { 0: 'Red', 1: 'Green', 2: 'Blue', Red: 0, Green: 1, Blue: 2 }
- 값을 할당해주지 않으면 0, 1, 2가 들어간다.
enum Color {
Red = 2, Green, Blue
}
console.log(Color[2])
👉🏽 Red
// any
let num: any = "123";
// void: 함수가 리턴하는 값이 없을 때 사용하는 타입
function warnUser(): void{
console.log("This is my warning message")
}
function temp(age: number): number{
return age;
}
// null
let none: null = null;
// undefined
let done: undefined = undefined;
// never
function error(message: string): never{
throw new Error(message);
}
function infiniteLoop(): never {
while(true){
/* 기능 */
}
}
// 타입 별칭
let x: number = 10;
let xPositina: number = 10;
type YesOrNo = string;
type YesOrNoDetail = "Y" | "N";
let sayMe: YesOrNo = "HI";
let sayYou: YesOrNoDetail = "Y"
// 인자가 없고 리턴 값이 string 타입인 type 지정
type FooFunction = () => string;
let temp: FooFunction = () => {
return "temp"
}
// interface
type Name = "AYW";
interface IUser{
id: number;
name: Name;
email: string;
age: number;
}
const myInfo: IUser = {
id: 1,
name: "AYW",
email: "any",
age: 27
}
// interface는 속성 값끼리 합칠 수 있다.
interface IUser{
id: number;
name: Name;
email: string;
age: number;
}
interface IUser {address: string}
// type 별칭은 불가능하다.
type IUser{
id: number;
name: Name;
email: string;
age: number;
}
type IUser {address: string}
👉🏽 Error. Duplicate identifier 'IUser'.
// keyof: 타입에 있는 속성값들을 하나의 타입으로 묶는다.
interface User {
id: number;
name: string;
age: number;
gender: "male" | "female";
}
type UserKey = keyof User;
// 'id' | 'name' | 'age' | 'gender'
const uk: UserKey = "name"
console.log(uk)
❏ type utility 알아보기
typescript는 일반적인 타입 변환을 쉽게 하기 위해서 몇 가지 유틸리티 타입을 제공합니다. 이러한 유틸리티는 전역으로 사용 가능합니다.
// Partial<T>: type 집합의 모든 프로퍼티를 선택적으로 타입을 생성한다.
// Partial 대신 ?를 써도 된다.
interface User {
id: number;
name: string;
age: number;
gender: 'male' | 'female'
}
let admin: Partial<User> = {
id: 1,
name: "AYW",
}
console.log(admin);
// Readonly<T>: 처음 설정한 값외에 속성을 수정 할 수 없다. 재할당 금지
interface User {
id: number;
name: string;
age: number;
gender: 'male' | 'female'
}
let admin: Readonly<User> = {
id: 1,
name: "AYW",
age: 27,
gender: "male"
}
admin.id = 3;
console.log(admin)
👉🏽 Cannot assign to 'id' because it is a read-only property.
// Readonly
interface User {
id: number;
name: string;
age: number;
readonly gender: 'male' | 'female'
}
let admin: User = {
id: 1,
name: "AYW",
age: 27,
gender: "male"
}
admin.id = 3;
admin.gender = "female"; // error
console.log(admin)
👉🏽 Cannot assign to 'gender' because it is a read-only property.
// Record<K, T>: 개체의 속성과 타입을 정의할 때 사용하는 메소드
type Grade = "1" | "2" | "3" | "4";
type Score = "A" | "B" | "C" | "D";
const score: Record<Grade, Score> = {
1: "A",
2: "B",
3: "C",
4: "D",
}
console.log(score)
// Record<K, T>: 타입의 프로퍼티 키의 집합으로 타입을 생성한다. 개체의 속성과 타입을 정의할 때 사용하는 메소드
interface User {
id: number;
name: string;
age: number;
}
function isValid(user: User){
const result: Record<keyof User, boolean> = {
id: user.id > 0,
name: user.name !== '',
age: user.age > 0,
}
return result;
}
// Pick<T, K>: Type에서의 Key값을 선택하여 새로 type으로 정하는 기능
interface User {
id: number;
name: string;
age: number;
gender: "male" | "female";
}
const admin: Pick<User, "id"|"name"> = {
id : 0,
name: "TED"
}
console.log(admin)
// Omit<T, K>: Type에서의 Key값을 선택하여 사용하지 못하게 한다.
interface User {
id: number;
name: string;
age: number;
gender: "male" | "female";
}
const admin: Omit<User, "id"|"name"> = {
age: 27,
gender: "male"
}
console.log(admin)
// Exclude<T1, U>: 속성 대신 Type의 key 값을 제외한다.
type T1 = string | number | boolean;
type T2 = Exclude<T1, number | string>;
let isNum: T2 = 3;
👉🏽 Type 'number' is not assignable to type 'T2'.
let isNum: T2 = true;
// NonNullable<T>: undefined와 null 타입을 제거한다.
type T1 = string | null | undefined | void;
type T2 = NonNullable<T1>;
let empty: T2 = null;
👉🏽 Type 'null' is not assignable to type 'T2'.
let empty: T2 = "Hi";
// Parameters<T>: 함수 타입을 인자로 받아서 튜플타입으로 리턴해주는 문법
type T0 = Parameters<() => string>; // []
type T1 = Parameters<(s: string) => void>; // [string]
type T2 = Parameters<(s: string, i: number) => number>
let T1Arr: T1 = ["123"];
// ReturnType<T>: return type값을 정의하는 문법
type T0 = ReturnType<() => string>;
function returnString(): T0{
return "123"
}
type T7 = ReturnType<any> // any
type T8 = ReturnType<never> // any
// Required<T>: partial과 반대되는 기능으로 type집합의 모든 프로퍼티를 필수로 설정한다.
interface User {
id: number;
name?: string
}
let admin: Required<User> = {
id: 1,
}
👉🏽 Property 'name' is missing in type '{ id: number; }' but required in type 'Required<User>'.
let admin: Required<User> = {
id: 1,
name: "AYW"
}
❏ 타입 스크립트의 함수
- 일급 객체(first-class object): 함수를 변수로 선언하고, 함수에 인자를 함수로 넘길 수 있고, 함수의 리턴 값을 함수로 설정할 수 있는 것을 일급 객체라 부릅니다.
JS와TS는모두 일급 객체입니다. - 일급 객체의 특징 때문에 고차 함수, 콜백 함수를 만들 수 있다.
// 함수 자체를 값으로 저장한다.
let sum = function(a, b){
return a + b;
}
function ul(child){
return `<ul>${child}</ul>`
}
// 함수를 리턴할 수 있다.
function makeLi(container, contents){
const liList = [];
for (const content of contents){
liList.push(`<li>${content}</li>`)
}
return container(liList.join(""))
}
// 함수를 함수의 인자로 넣어줄 수 있다.
const htmlUl = makeLi(ul, ['월', '화', '수', '목', '금', '토', '일'])
- 함수의 선언문, 선언식
// 함수 선언문 function sum(a, b){ return a+b; } // 함수 표현식 const myFunc = function(a, b){ return a+b; }
4. 함수의 가변인자
function sum(a, b){
return a+b;
}
const abcSum = sum(10, 20, 30);
// 가변 인자식
function sum(){
let s = 0;
for (let i=0; i< arguments.length; i++){
s+=arguments[i];
}
return s;
}
// arguments
function sum(...args){
let s = 0;
for (let i=0; i< args.length; i++){
s+=args[i];
}
return s;
}
- 함수 호출
// call과 apply는 인자를 하나씩 넘겨주냐 배열로 넘겨주냐의 차이 sum(10, 20, 30, 40); // 컨텍스트 null sum.call(null, 10, 20, 30, 40); arr = [10, 20, 30, 40]; sum.apply(null, arr);
// 즉시 실행 함수
(function(){
console.log(123);
})
// 일반함수와 화살표함수의 차이: 컨텍스트의 차이점
const sumV2 = (a, b, ...args) => {
}
const ten = () => 100;
const hundred = x => 100 + x;
// 생성기 함수(generate function): 함수를 한번에 실행시키는 것이 아니라 텀을 두고 실행할 때
function* gen(){
yield 10;
yield 20;
return 30;
}
const g = gen();
console.log(g.next()); // 10
console.log(g.next()); // 20
console.log(g.next()); // 30
- 함수 작성 시 반환 타입 명시 권장(필수는 아님,
TS컴파일러는 방정식의 한쪽에만 타입이 있더라도 타입을 추론할 수 있다.) - 매개변수와 인수의 타입이 호환 가능하게 작성, 인수 타입의 전달이 잘못된 경우 오류 발생
// 함수를 안전하게 만들 수 있다.
interface MathFn {
(a: number, b: number): number;
operator: string;
};
const sum: MathFn = (a, b) => a + b;
sum.operator = "+";
1. void: 반환값이 없는 함수입니다. (그냥 출력하거나, 상태를 바꿀 때 자주 씁니다.)
2. never: 함수가 종료되지 않습니다. (무한루프를 돌거나, Error를 띄울 때 사용합니다.)
- 함수의 매개변수: 함수에 주어진 인자의 개수는 함수가 기대하는 매개변수의 수와 일치해야 함
function sum(a: number, b:number, type?: string): number{
return a+b;
}
let sumAge = sum(2, 3, "ted");
function buildName(firstName: string, lastName="Smith") {
return firstName + ' ' + lastName;
}
let result1 = buildName("Bob");
let result2 = buildName("Bob", undefined);
let result3 = buildName("Bob", 'Ted');
console.log(result1)
👉🏽 "Bob Smith"
[LOG]: "Bob Smith"
[LOG]: "Bob Ted"
console.log(result2)
console.log(result3)
undefined가 넘어가면 lastName이 없다고 판단되지만, null이 들어가면 lastName이 있다고 판단을 합니다.
(undefined는 값 자체가 없다고 판단하나, null은 값은 있는데 의미없는 특별한 값이 들어있다고 보시면 좋을 것 같아요.)
+ undefined는 미리 선언된 global variable이나, null은 키워드입니다.
❏ 객체 지향 프로그래밍(OOP)
OOP는 컴퓨터 프로그램을 객체의 모임으로 파악하려는 프로그래밍 패러다임- 프로그램을 유연하고, 변경이 용이하고, 개발과 보수를 간편하게 만든다, 직관적인 코드 분석이 가능해진다.
- 클래스 요소: 필드(field), 생성자(constructor, 객체가 처음 생성될 때 호출, 멤버 변수 초기화), 메서드(method)
- 인스턴스:
new연산자에 의해서 생성된 객체
class Person {
name: string;
constructor(name: string){
this.name = name
}
say(){
return `Hello, ${this.name}!`
}
}
let person = new Person("AYW");
console.log(person.say())
❏ 클래스 만들기
- 생성자의 매개변수에
public과 같은 접근 제한자를 붙이면 해당 매개변수의 이름을 가진 속성이 클래스에 선언된 것처럼 동작합니다.
// public 접근 제한자
class Information {
constructor(public name: string, public age: number){}
}
const myInfo = new Information("AYW", 27);
console.log(myInfo)
👉🏽 { name: 'AYW', age: 27 }
// this
class Information {
name: string;
age: number
constructor(name: string, age: number){
this.name = name;
this.age = age;
}
}
const myInfo = new Information("AYW", 27);
console.log(myInfo)
👉🏽 { name: 'AYW', age: 27 }
❏ 클래스 상속받기
- 부모 클래스를 상속받는 키워드:
extends
class AYW extends Information{
constructor(public name: string, public age: number, gender: "male" | "female"){
super(name, age)
}
sayHello(): void {
console.log(`제 이름은 ${this.name}입니다.`)
}
}
const myInfo: AYW = new AYW("AYW", 27, "male")
console.log(myInfo);
👉🏽 AYW { name: 'AYW', age: 27 }
myInfo.sayHello();
👉🏽 제 이름은 AYW입니다.
❏ 추상 클래스
- 추상 클래스는 자신의 속성이나 메서드 앞에
abstract를 붙여 나를 상속하는 다른 클래스에서 이 속성이나 메서드를 구현하게 합니다.
abstract class Information {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
abstract sayHello(): void;
}
class AYW extends Information {
constructor(
public name: string,
public age: number,
gender: "male" | "female"
) {
super(name, age);
}
sayHello(): void {
console.log(`제 이름은 ${this.name}입니다.`);
}
}
const myInfo: AYW = new AYW("AYW", 27, "male");
console.log(myInfo);
👉🏽 AYW { name: 'AYW', age: 27 }
myInfo.sayHello();
👉🏽 제 이름은 AYW입니다.반응형
'Frontend > 엘리스 SW 엔지니어 트랙' 카테고리의 다른 글
| [ 엘리스 SW 엔지니어 트랙 ] 23일차 (0) | 2021.11.25 |
|---|---|
| [ 엘리스 SW 엔지니어 트랙 ] 22일차 (0) | 2021.11.24 |
| [ 엘리스 SW 엔지니어 트랙 ] 20일차 (0) | 2021.11.21 |
| [ 엘리스 SW 엔지니어 트랙 ] 19일차 (0) | 2021.11.19 |
| [ 엘리스 SW 엔지니어 트랙 ] 18일차 (0) | 2021.11.18 |
댓글