위에서 언급했듯이 NodeJs는 서버가 아니다. JavaScript를 실행 시켜주는 런타임 환경 중 하나다. 원래 JavaScript는 브라우저에서만 실행할 수 있는 언어였다. 시간이 지남에 따라 JavaScript 엔진의 기능이 많아지면서 브라우저뿐만 아니라 JavaScript를 실행할 수 있는 환경을 필요로 하게 됐다. 그렇게 생겨난 것이 CommonJs 이고 NodeJs다.
"나중에 기회가 되면 NodeJs의 역사에 대해서 글을 써보려고 한다"
[NodeJs 요약]
- Javascript *런타임 환경이다.
런타임 환경 : 컴퓨터가 실행되는 동안 프로세스나 프로그램을 위한 소프트웨어 서비스를 제공하는 가상 머신의 상태이다.
(쉽게 말해 프로그램이 실행할 수 있는 환경을 의미한다.)
- 자바스크립트를 사용해서 서버측 로직 코드를 작성하고 서버를 구축할 수 있게 도와준다.
- 크롬 *v8 Javascript 엔진으로 빌드되었다.
2. NodeJs의 특징
NodeJs의 특징으로는 크게 4가지가 있다.
1) Event-driven architecture
이벤트를 실행하면 이벤트로 등록한 작업을 수행
자바스크립트로 클릭같은 이벤트에 콜백함수를 작성하고
이벤트 기반의 특정 이벤트 발생하면 전달한 콜백함수를 호출해서 실행시킨다.
(이와 같은 내용을 '이벤트 리스너에 콜백함수를 등록한다.'라고 한다.)
2) Asynchronous programming
nodeJs에서 '비동기 I/O' 작업을 진행하는 동안 또 다른 작업을 실행할 수 있다.
3) Non-blocking I/O
Input/Output : 파일 시스템(브라우저에서 파일을 조작할 수 없고) 네트워크 디스크 등 데이터를 읽거나 쓰거나 하는 작업
(NodeJs는 무거운 작업의 속도가 빠르다.)
NodeJs는 I/O 작업을 수행하는 동안 다른 코드들을 실행할 있게 하는논블로킹I/O 에 해당한다.
반대로블로킹은 한 작업이 끝날 때까지 다른 작업을 수행하지 않는 것을 의미하며
수행하는 동안 다른 코드를 중단시킬 수 있다.
4) Single-threaded execution
위의 네가지의 공통점은 멀티태스킹을 편리하게 만드는 개념이라는 것이다.
3. NodeJs의 장점
1) 하나의 언어로 프론트와 백엔드를 개발할 수 있다
JavaScript를 이용해 백엔드, 프론트엔드 구축 가능
2) npm과 같은 서드파티 모듈이 많다.
서드파티 모듈이란 NodeJs 제작 업체와 개발자 외에 다른 사람이 만들어 놓은 기능
(이미 다른 사람들이 만들어 놓은 모듈이 많으므로 가져다 사용하면 된다.)
3) 개발자 커뮤니티가 크고 다양하다.
커뮤니티가 많다는 것은 여러 환경에서 사용자들끼리 피드백이 활발하고 지역별 또는 주제별로 다양한 논의가 이루어진다는 것을 의미한다.
마무리
NodeJs가 무엇인지 어떤 특징을 가졌는지에 대해 알아봤다. 프론트엔드도 NodeJs를 통해 백엔드를 공부해본 다면 진짜 폭넓은 지식을 가질 수 있을 것이다. JavaScript를 통해 백엔드를 구축할 수 있는 시대에(?) 태어난 것에 감사한 마음이 들기도한다.
- 요청을 보낸 후 응답을 받야지만 다음 동작을 실행한다. 작업을 처리하는 동안 나머지 작업은 대기한다.
비동기
- 병렬적으로 작업을 수행하는 방식이다.
- 요청을 보낸 후 응답의 수락 여부와는 상관 없이 다음 업무가 동작하는 방식이다.
- 비동기 처리를 위해 콜백 패턴을 사용하면 콜백함수가 중첩되어 복잡도가 높아질 단점이 있다. 이를 콜백지옥이라고 부른다. (가독성이 나빠질 수 있다.)
2. 자바스크립트의 비동기 처리
Web API
Web API는 브라우저에서 제공되는 API. 자바스크립트 엔진에서 정의되지 않았던 setTimeout이나
HTTP 요청(ajax) 메소드, DOM 이벤트 등의 메소드를 지원한다.
Task Queue
이벤트 발생 후 호출되어야 할 콜백 함수들이 대기하는 공간. 이벤트 정한 순서대로 줄을 서있고 콜백 함수들이 기다리는 공간이라고 해서 콜백큐(Callback Queue) 라고 부른다.
Event Loop
함수의 실행 순서를 정해준다.
setTimeout(()=>{
console.log("안녕");
},3000) // 3초 뒤에 실행할 함수
function a(){
console.log("안녕1");
console.log("안녕2");
console.log("안녕3");
}
// 함수 실행은 안녕1, 안녕2, 안녕3, 안녕 순서대로 한다.
1. Call Stack 안녕(setTimeout)이 먼저 쌓이고 Web API에 3초 동안 콜백함수가 가지게 된다.
브랜치를 이용하면 한줄의 커밋으로만 작업을 하는 것이 아닌 여러 줄의 커밋을 만들어 줄 수 있다.
잘 돌아가는 코드의 내용을 가지고 있는 master를 한 곳에서만 작업하지 않고 새로운 브랜치를 만들어서 작업한다.
# branch 만들기
git branch 브랜치 이름
# branch 생성과 이동
git switch -c 브랜치 이름
# branch 삭제
git branch -d 브랜치 이름
# brnach 삭제가 안되는 경우가 발생할 경우 강제로 삭제
git branch -D 브랜치 이름
# branch 이름을 바꿀 경우
git branch -m 기존이름 바꿀이름
2. 브랜치 병합
# master 위치로 헤더를 이동
git switch master
# branch 병합
git merge 브랜치이름
# pull 했을 때도 같은 파일의 수정내용이 겹치면 충돌이 발생할 수 있다.
# 코드의 내용을 가져오고 병합까지 진행
git branch -D hotfix
# 작업을 브랜치에서 하다가 다른 브랜치로 이동해서 hotfix를 처리해야하는 경우
# 잠시 작업하던 결과물을 스택에 보관한다
git stash
# 다시 돌아와서 내 작업물 스택에서 꺼내온다
git stash pop
머지 컴플릿
작업을 할 때 같은 파일을 수정하면 안된다.
무슨 내용이 맞는지 확인해서 수정하고 커밋까지 진행 오류 처리다.
3. 원격 저장소
# 원격 저장소 값을 경로로 쓰는것보다 별칭을 정하고 사용
# origin이라는 별칭을 써서 원격저장소 내용을 구분해라
git remote add origin 원격저장소주소
# 첫 커밋 내용을 푸시
git push -u origin master
# 다른 사람이 만든 저장소를 fork해서 내 저장소로 가져오고
git init
git remote add origin ""
git pull origin master
git push origin master
# pr(Pull Request) 날려서 작업
# 실저장소 소유자가 버전 관리를 하는 원격 저장소를 가지고 있고
# 내가 포크 뜬 저장소는 커밋 내용을 완성 작업이 잘 동작하면 push 로 pr날려서 검증 받을 수 있다
# git 의 명령어가 커맨드 창에 출력된다
git help --all
# 새폴더를 만들어준다.
mkdir 폴더명
# 현재 경로에서 해당 폴더로 이동
cd 폴더명
# 상위 폴더로 이동
cd ..
# git 저장소 초기화
git init # 폴더 안에 .git이라는 폴더가 생김(숨김 폴더)
# 설정 파일 속성 추가 사용자 정보
git config user.name "닉네임"
git config user.email "이메일"
# 기본 브랜치명 변경
# 윈도우에서는 master가 기본이다
git config init.defaultBranch main
# vim 명령어
vim 파일명 # 파일을 터미널에 출력 해준다
# i -> 수정, :q! -> 저장 안하고 나감 , :wq! -> 저장 후 종료
4. git 저장소 활용
git 초기화는 완전 비어있는 폴더 아니면 기존에 작업하던 곳에서 가능하다.
작업할 공간에서 git 초기화를 진행해야한다.
working directory : 작업을 하는 공간
tracked가 된다는 것은 git의 소스코드가 관리 대상으로 등록한다.
stage : 임시로 저장하는 공간
working directory에서 추가한 파일을 관리하는 역할을 한다.
추적할 파일의 내용이나 수정할 내용을 변경할 파일들의 차이점을 빠르게 처리하기 위해 있다.
이후에 기록을 한다.
repository : 기록하는 공간
커밋을 진행하면 기록된다.
Head : 현재 작업하고 있는 커밋의 위치
# 해당 파일을 대기소로 보냄
git add 파일명
# 모든 변경된 파일 내용을 대기소로 보냄
git add .
# 현재 대기소의 상태 확인
git status
#git의 저장소에 기록된 내용을 확인하고 싶다.
git log
# 수정한 내용을 삭제하고 이전 상태로 돌아간다
git restore 파일명
# rm 대기소에서 제거 파일을 대기소에서 제거한다
git rm --cached test.json
# repository 저장소에 기록
git commit -m "커밋 메시지"
# 이전 해시로 Head를 이동 시킨다
git checkout 캐시 해시값
# reset 이전 해시로 돌아가는 옵션중에서 hard
git checkout 커밋 해시값
git reset --hard
ex) const input = document.querySelector("input"); // 자바스크립트 경우에서는 에러가 발생하지 않는다.
const input = documnet.querySelector("input") as HTMLinputElemnet // typescript에서는 input에 as 타입을 지정해 주어야 에러가 발생하지 않는다
2. 타입 앨리어스
- 타입 지정에 별명을 붙여줄 때 사용합니다.
ininterface는 기능이나 사물의 객체의 구조를 정의할 때 사용하고
type은 데이터의 형태를 정의할 때 사용합니다.
type userLogin = { uid : string, upw : string }
3. 타입 클래수정자
class User { private name : string; // private 은 객체를 생성하고 점표기법, 대괄호표기법으로 접근이 불가능하다. 직접 참조할 수 없게 만든다. // 다른 작업자나 아니면 혹여나 의도치 않게 변경될 경우를 고려하여 직접 참조 혹은 수정이 불가능하다. // 객체 안에서는 수정이 가능하다.
public age : number; // public 은 객체도 참조 수정이 가능하고 점표기법, 대괄호표기법 접근이 가능하다. // 유저를 관리하거나 하다 보면 값을 풀어야 하는 상황이 생기기도 한다. (객체지향적으로 프로그래밍을 하다가 어쩔 수 없을 때)
// get set getName (){ // private 속성은 객체 안에서 메서드로 this를 참조하여 호출할 수 있다. return this.name; }
setName (_name){ // privatge 속성에 접근해서 name의 값을 수정 this.name = _name; } }
4. 제네릭 타입
호출시에 동적인 타입을 주고 싶은 경우에 사용
<T> : 제네릭 타입 문법
타입을 전달한다.
class User<A> { private name : A[] =[]; } const user = User<string>();
// 제네릭을 사용하는 목적은 코드의 재사용성을 높히기 위해서 const product = Product<number>(); const product = Product<string>();
// T : 타입의 축약어 , E : Error의 타입 , R : 변환타입 function name<T, R>(name : T):R{ return parseInt(name + 1); } name<string, number>(); const user = userLogin ={ uid, upw } name<userLogin, boolean>(user)
타입스크립트에서 인터페이스는 두시스템 사이에 상호간에 정의한 통신규약이며 선언만 존재합니다. 타입을 미리 선언해주는 장치로서 사용할 수 있다. 미리 개발할 때 서로 공통적으로 사용할 부분을 미리 선언을 할 때 사용한다. 사용목적은 클래스와 함수간의 일관성을 유지할 수 있도록 하기 위함이다.
2.interface 특징
- ES6(자바스크립트)가 지원하지 않는 TypeScript 에서만 사용
- 컴파일 후에 사라진다 (자바스크립트에서 지원하지 않기 문이다)
- interface 간에 다중상속이 가능
interface 예시
// 인터페이스명은 대문자로 표기
interface IUser {
name: string; // name 문자열
age: number; // age 숫자
fn1() : void; // fn1은 void 타입
}
// 인터페이스 자체를 타입으로 줘서 객체 생성
const user : IUser = {
name : "Jaka",
age : 33,
fn1: ()=> {console.log("Hello World")};
};
// 매개변수를 인터페이스 타입으로 받기
function fn1(a: IUser): void{
console.log(`${a.name} 은 ${a.age}살이야.`)
};
fn1(user); // jaka는 33살이야
person.fn1(); // Hello World
3. 선택적 프로퍼티 (Optional Properties)
인터페이스에 있는 속성을 다 사용해야 한다면 코드의 유연성이 사라질 수 있다. 이를 해결하기 위한 방법으로 우리는 선택적 프로퍼티라는 것을 사용할 수 있다. 문법은 (:) 전에 '?'를 넣어주면 된다. 아래의 코드로 예제를 보자
interface ICoffee {
name : string;
flavor?: string;
}
function orderCoffee(coffee: ICoffee){
consolel.log(coffee.name);
}
let myCoffee = { name : "latte" }; // flavor는 명시 안함
orderCoffee(myCoffee); // latte
interface ICustomer {
name : string;
age : number;
tel? : string;
}
let customer : ICustomer = {
name : "jaka",
age : 30
}
customer.name = "jaka2";
customer.tel = "123-4567"; // 선택적 프로퍼티가 있으면 값을 나중에 추가할 수 있다.
customer.address = "서울"; // 정의되지 않은 속성을 마음대로 넣을수는 없음
4. 읽기 전용 프로퍼티 (readonly)
readonly는 인터페이스로 객체를 처음 생성할 때만 값을 할당하고 그 이후에는 변경할 수 없는 속성을 의미한다. 변하지 않는 값 const와 비슷하다고 생각하면 된다. 다음 예제를 보자.
interface Book {
title : string;
year : number;
readonly writer : string; // 읽기 속성 전용
}
let book : Book = {
title : "콩쥐팥쥐",
year : 2002,
writer : "김철수"
};
book.writer = "철수"; // Error 이후에는 수정이 불가능
5. 인터페이스 클래스 타입
인터페이스로 클래스를 정의하는 경우, implemets를 사용하면 된다.
interface IUser {
name : string;
getName() : string;
}
// IUser 인터페이스를 Implements 하면 User 클래스의 프로퍼티 구조는 반드시 IUser에 정의 된대로 따라야한다.
// 즉, 반드시 name, getNmae() 클래스에 기본값으로 구현해야 한다
class User implements IUser {
name: string;
constructor(name: string){
this.name = name;
}
getName() {
return this.name;
}
}
const jaka = new User("jaka");
jaka.getName(); // jaka
GUI (Grahpoic User Interface) : 볼 수 있는 아이콘등의 UI가 있는 것
CLI (Command Line Interface) : 콘솔창을 통해서 프로그램을 실행하는 환경
- CLI 는 터미널을 통해 컴퓨터가 상호작용을 하는 방식
- 유저는 문자열의 형태로 작성해서 컴퓨터에게 출력을 받는다
쉘
터미널을 사용하기 위해 키보드 입력같은 명령어를 실행하고 폴더와 파일 등을 관리합니다.
명령어를 입력하면 커널이 읽을 수 있는 이진코드로 변환합니다.
커널
컴퓨터를 켜면 메모리에 항상 올라가 있는 운영체제.
하드웨어와 프로그램 사이에서 인터페이스를 제공하는 역할을 담당합니다.
컴퓨터의 자원들을 관리하는 역할을 합니다.
cd : 하위 폴더 경로로 이동
cd.. : 상위 폴더 경로로 이동
npm
node의 패키지 매니저
다른 개발자들이 만든 코드를 폴더로 만들어서 배포를 한 파일을 다른 개발자들이 받아서
이어서 작업할 수 있게 나이가 많은 프로그램들을 다시 만들 필요 없이 가져와서 사용한다.
npm이란
- 명령줄 클라이언트 공개방식들과 지불 방식들의 패키지를 설치할 수 있습니다.
- 개발자들이 자신이 작성한 모듈 등등을 공유할 수 있는 저장소를 npm을 활용하여 쉽게 설치
받을 수 있습니다.
- 오픈소스 생태계를 구축하기 위함과 개발 생산성을 향상하기 위함입니다.
npm init
# package.json 초기화 --> 설치 받은 라이브러리가 여기에 나와요!
npm init -y #바로 초기화
# 패키지를 설치하는 명령어
npm install [패키지명]
npm i [패키지]
# 개발 환경에서 설치할건지
npm install --save-dev [패키지명]
# 개발환경에서만 사용할 패키지
npm i -D [패키지명]
# 전역으로 패키지 설치
npm i -g [패키지명]
# npm 패키지 설치 package.json에 있는 내용을 가지고
npm i
package.json
- 우리가 작업하는 패키지의 정보와 설치한 라이브러리의 이름과 정보를 가지고 있습니다.
- npm으로 명령어를 실행하는 경우 읽어서 실행하는 json 파일
typescript 패키지를 설치한 내용을 기록이 패키지에서 무엇 무엇을 사용했는지`의존성 모듈` "dependencies": { "typescript": "^5.4.5" }-- ^: 버젼이 없으면 이후 버젼을 설치해야한다
tsconfig.json
컴파일을 진행할 때 검사의 속성이나 파일의 경로 속성을 정의하는 json 파일
# typescript의 json도 따로 설치를 해서 컴파일 옵션을 설정한다.
# typescript 설치
# 설치
npm install typescript
npm i typescript
# 제거
npm uninstall typescript
# 설치가 잘 되어 있는지 버전을 확인
# npx 실행할 때 사용
# tsc 타입스크립트 명령어
# tsc 컴파일 진행
npx tsc --init
# 컴파일 진행
npx tsc
include : 컴파일을 진행할 폴더 지정 exclude : 제외할 폴더 지정
compilerOptions 속성에 포함 target : 변환파일의 es버전 ES5로 만들건지 ES6으로 만들건지 결정 outDir : 변환된 파일을 내보낼 경로를 지정
{
// 와일드 카드
// css *
// /* : 모든 파일을 포함한다.
// /** : 모든 폴더를 포함시키겠다.
// src 폴더에 모든 폴더들에서 모든 파일을 포함 시키겠다
// **/.test.ts : 현재 폴더에서 모든 폴더의 파일중에서 확장자로
// .test.ts 라고 작성된 파일을 컴파일에서 제외시키겠다
"include":[
"src/**/*"
],
"exclude":[
"**/*.test.ts"
],
"compilerOptions":{
"target": "ES6",
"outDir": "./dist", // 정하는 이름
"removeComments": true // 주석을 제거해서 변환하겠다
}
}
타입 스크립트
javascript에서 타입 검사가 추가된 확장 언어라고 생각하면 된다.
typescript는 javascript의 슈퍼셋
- typescript는 javascript와 다르게 런타임 환경이 존재하지 않습니다.
- 컴파일 언어(컴파일러가 존재)
- typescript -> javascript 로 변환
- 코드 실행은 자바스크립트로 합니다.
- 코드 작성단계에서 의도에 맞지 않게 코드를 작성한 경우에도 오류를 표시합니다.
문법
// 예약어 변수명 : 타입명 = 초기값
// any타입 뭐든 가능한 타입
let num : number = 20;
const str : string = "typescript";
const nan : number = NaN;
const _null : null = null;
const bool : bollean = true;
const _undefined : undefined = undefined;
const obj : object = {};
const arr : string[] = ["1", "2", "3"];
const fn = (a : number) => {
console.log(a);
}
const fn = (a : number, b : number) => {
console.log(a+b);
}
// unknown
// 안정성을 조금 더 보장
// 어떤 타입이든 할당은 가능하지만 조건문으로 검사하고 사용해야한다.
const _unknown : unknown = "";
if(typeof _unknown === "string")
console.log(_unknown.length);