반응형

이전 포스팅에서는

아주 기본적인 형태의 라우팅을 적용해보았다.

(+ 라우팅이 무엇인지)

🔗 이전 포스팅 링크 : https://woojin.tistory.com/25

 

[Express] 간단한 라우팅 적용하기 (Typescript 사용) #Node.js #Express

이전 포스팅에 이어서, 간단한 라우팅을 적용해보자. (이전 포스팅) Express 간단한 서버 세팅 👇 https://woojin.tistory.com/24 라우팅(Routing)이란? 애플리케이션 엔드포인트(URI)의 정의, 그리고 URI가 클

woojin.tistory.com

 

이번 포스팅에서는

유지보수가 용이하도록

라우터 관련 코드를 적절히 분리시켜보자.

 


 

디렉토리 구조 만들기

위와 같이 디렉토리 구조를 설계한다.

빈껍데기인 파일만 만들어 놓아도 된다.

 

src/api : 블로그 관련 api를 묶어놓은 폴더

src/router : api 폴더에 있는 api들의 라우팅을 담당하는 폴더

 

blog.ts, signup.ts, user.ts는 간단한 api를 구현한 것으로, 아래에서 상세 내용을 다루겠다.

 

 

 

디렉토리 구조 설명

대략적인 디렉토리 구조를 설명하고 넘어가보려 한다.

 

localhost:8080/api/user/1 에 GET 메서드를 요청한다면,,

 

1. index.ts로 이동한다.

2 .api를 보고 router/index.ts로 이동한다.

3. user를 보고 router/userRouter.ts로 이동한다.

4. get 메서드를 보고 파라미터(userId : 1)를 가지고 api/user.ts 에서 정의한 selectUser로 이동한다.

5. api/user.ts에서 정의한 selectUser를 실행한다.

 

쉽게 말하면,

api에서는 api 기능에 대한 내용을 다루고,

router에서는 메서드 종류와 요청에 대한 분기를 다룬다.

 

내용에 따라 폴더가 분리되어 있어, 쉽게 위치를 파악하고 유지보수할 수 있다.

 

 

api 폴더 작성

// blog.ts

import { Request, Response } from "express";

const selectPost = async (req: Request, res: Response) => {
    const param = req.params;

    return res.status(200).json({
        status: 200,
        message: "포스팅 조회 성공",
        posting_number: param.blogId
    });
};

const likePost = async (req: Request, res: Response) => {
    const param = req.params;

    return res.status(200).json({
        status:200,
        message: "좋아요 성공",
        posting_number: param.blogId
    });
};

export { selectPost, likePost };

blog.ts에서는 selectPost와 likePost 두 개의 함수를 정의한다.

req.params는 뒤에서 정의할 request url 상 파라미터를 가져오는 것이다.

 

// signup.ts

import { Request, Response } from 'express';

const signup = async (req: Request, res: Response) => {
    return res.status(201).json({
        status: 201,
        message: "회원가입 성공",
    });
}

export default signup;
// user.ts
import { Request, Response } from "express";

const selectUser = async (req: Request, res: Response) => {
    const param = req.params

    return res.status(200).json({
        status: 200,
        message: "유저 조회 성공",
        data: param
    });
};

export default selectUser;

json 형식의 메세지와 상태코드를 응답(response)한다.

 

 

router 폴더 작성

// blogRouter.ts

import express, { Router } from "express";
import { likePost, selectPost } from "../api/blog";

const router: Router = express.Router();

router.get('/:blogId', selectPost);
router.get('/like/:blogId', likePost);

export default router;

Router를 따로 import해놓고, express.Router()를 사용하는 것은

에어비엔비 표준 규격을 맞추기 위함이라고 한다.

 

코드에서 보이다시피, 특정 엔드포인트에 특정 메서드가 요청되면, api 폴더에 작성한 파일의 함수가 불러와진다.

 

엔드포인트 뒤에 ':blogId'와 같은 것은 url로 전달받은 파라미터를 저장할 변수명이다.

예를 들어 블로그 id가 3인 포스팅을 'likePost'하려고 한다면,

localhost:8080/api/blog/like/3 과 같이 요청을 받아

3을 'blogId'에 저장해서 likePost 함수가 있는 api/blog.ts로 전달하는 것이다.

 

// signupRouter.ts

import express, { Router } from 'express';
import signup from '../api/signup';

const router: Router = express.Router();

router.post('/', signup);

export default router;
// userRouter.ts

import express, { Router } from "express";
import selectUser from "../api/user";

const router: Router = express.Router();

router.get('/:userId', selectUser);

export default router;

 

마지막으로 index.ts를 만들어준다 (router 폴더 내부에 있는 index.ts)

// router/index.ts

import express, { Router } from "express";

import userRouter from "./userRouter";
import blogRouter from "./blogRouter";
import signupRouter from "./signupRouter";

const router: Router = express.Router();

router.use('/user', userRouter);
router.use('/blog', blogRouter);
router.use('/auth', signupRouter);

export default router;

위에서 작성한 파라미터 앞단에 붙는 url을 정의한다.

/user는 userRouter로 이동하고 (..)

 

이렇게 정의한 router는,

아래에서 루트 디렉토리에 있는 index.ts로 전달한다.

 

 

index.ts(루트 디렉토리) 작성

// index.ts

import express, { Request, Response, NextFunction } from 'express';
import apiRoute from './router/index'; // export한 이름과 다르게 import할 수 있다!

const app = express();

app.use(express.json());

app.use('/api', apiRoute);

app.get('/', (req: Request, res: Response, next: NextFunction) => {
    res.send('Hi! This is server tutorial.');
});

app.listen('8080', () => {
    console.log(`
    #############################################
        🛡️ Server listening on port: 8000 🛡️
    #############################################
    `);
});

 

이제 라우팅 구조가 명확하게 보일 것이다.

아래 예시들에 설명을 달아보자.

 

1. localhost:8080/ (get)

index.ts의 app.get에 따라 'Hi! This is server tutorial.'이 response로 보내진다.

실행 결과는 위와 같다.

 

2. localhost:8080/api/user/1 (get)

index.ts에서,

app.use('/api', apiRoute) 를 보고 apiRoute를 import한 ./router/index.ts로 이동한다.

 

./router/index.ts에서,

router.use('/user', userRouter)에 의해 userRouter를 import한 ./router/userRouter.ts로 이동한다.

 

./router/userRouter.ts에서,

router.get('/:userId', selectUser)로 이동해 userId:1 을 selectUser로 전달한다.

 

./api/user.ts에서,

selectUser가 실행되고, userId:1 은 req.params를 통해 전달된다.

실행 결과는 위와 같다.

 

 

 

간단한 라우팅 구조에서

유지보수가 용이한 라우터 분리 구조로 수정해보았다.

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
반응형

 

 

이전 포스팅에 이어서, 간단한 라우팅을 적용해보자.

 

(이전 포스팅)

Express 간단한 서버 세팅 👇

https://woojin.tistory.com/24

 

 

 

 


라우팅(Routing)이란?

애플리케이션 엔드포인트(URI)의 정의, 그리고 URI가 클라이언트 요청에 응답하는 방식을 말한다.

route의 동명사형인데, 이거 콩글리시 아닌가? (콩글리시 죽어)

route는 '루트'로 발음한다. 우리가 흔히 아는 '경로'라는 단어가 맞다.

 

쉽게 말하면, 요청이 URI에 들어오면 그것을 어느 경로로 분기시켜줄지 즉, 어디로 보내줄지 정의하는 것을 의미한다.

 

 

Express의 라우팅 기본 구조

src 폴더에 index.ts가 있다.

src 폴더에 api 폴더를 만든다.

api 폴더에는 index.ts, user.ts를 생성한다.

 

localhost:8000/api/user에 클라이언트의 요청이 들어오면

우선 ../src/index.ts 로 간다.

그곳에서 분기되어 ..src/api/index.ts 로 간다.

그곳에서 user.ts로 가라는 명령을 받아 ..src/api/user.ts로 이동한다.

 

즉, 요청은 가장 처음 ../src/index.ts 로 이동하고,

각 폴더의 index.ts 들의 명령을 통해 정확한 위치로 이동하여 요청을 수행한다.

 

자, 이제 간단한 라우팅을 구현해보자.

 

 

assignment/src/api/user.ts 생성

* 프로젝트명 : assignment

// user.ts
import express, { Request, Response, Router } from 'express';

const router: Router = express.Router();

router.get('/', (req: Request, res: Response) => {
    return res.status(200).json({
        status: 200,
        message: '유저 조회 성공'
    });
});

module.exports = router;

 

 

get 메서드를 간단하게 정의했다.

json response를 router 객체에 담고, 모듈로 반환한다.

 

 

assignment/src/api/index.ts 생성

// index.ts

import express, { Router } from 'express';

const router: Router = express.Router();

router.use('/user', require('./user'));

module.exports = router;

/api/user 엔드포인트에 요청이 들어오면 user.ts 파일을 실행하게 한다.

 

 

assignment/src/index.ts 수정

/api 로 요청이 들어왔을 때, src/api/index.ts 로 보내 올바르게 분기하도록 설정해야한다.

지금은 요청이 src/index.ts 로 들어왔을 때, src/api/index.ts로 보내주도록 설계되지 않았다.

 

// ../src/index.ts

import express, { Request, Response, NextFunction } from 'express';

const app = express();

app.use(express.json());	// request body를 express에서 json으로 받아 온다.

app.use('/api', require('./api'));	// /api 엔드포인트에 요청이 들어오면 api 폴더로 분기한다.

app.get('/', (req: Request, res: Response, next: NextFunction) => {
    res.send('Hi! This is my first express server. My name is Woojin.');
});

app.listen('8000', () => {
    console.log(`
    #############################################x
        🛡️ Server listening on port: 8000 🛡️
    #############################################    
    `)
})

 

 

localhost:8000/api/user 접속

브라우저에 localhost:8000/api/user 를 입력하고 접속한다.

서버가 구동되고 있지 않다면, 터미널에 'yarn run dev'를 실행시킨다.

 

../src/api/user.ts에 작성한 대로 응답을 받을 수 있다.

이렇게 간단한 라우팅이 완료되었다.

 

 

 

 

반응형
반응형

Express로 아주 간단한 서버를 만들어 초기 세팅하고,

라우팅하는 방법까지 정리해보고자 한다.

(라우팅은 다음 포스팅에서)

 

 


package.json 생성

// terminal 실행
yarn --init

위와 같이 이름만 입력하고 엔터로 넘기면 package.json 파일이 생성된다.

 

 

Express, TS용 모듈 설치

yarn add express	// express 설치
yarn add -D @types/node @types/express	// typescript용 모듈 설치
yarn add -D nodemon	// 서버 코드 변경 시 자동 재시작을 도와주는 nodemon

 

 

Express 세팅하기

tsc --init	// tsconfig.json 파일 생성

Typescript를 JS로 컴파일하는 옵션을 설정할 수 있다.

 

 

서버 만들기

express 프로젝트 폴더 내부에 src 폴더를 만들고,

그 내부에 index.ts 파일을 만든다.

import express, { Request, Response, NextFunction } from 'express';

const app = express();	// express 객체 받아옴

app.get('/', (req: Request, res: Response, next: NextFunction) => {
    res.send('Hi! This is my first express server');
});	// HTTP GET method 정의

app.listen('8000', () => {
    console.log(`
    #############################################
        🛡️ Server listening on port: 8000 🛡️
    #############################################    
    `)
})	// 8000번 포트에서 서버 실행

 

프로젝트 루트 디렉토리에 'nodemon.json' 파일을 만든다.

// nodemon.json

{
    "watch": [ 
        "src",
        ".env"
    ], 
    "ext": "js,ts,json",
    "ignore": [
        "src/**/*.spec.ts"  
    ],
    "exec": "ts-node  --transpile-only ./src/index.ts"
}

nodemon은 서버 실행 및 동기화 역할이다.

watch: 변경 감지 경로

ext: 변경 감지 확장자

ignore: 변경 감지 제외

exec: nodemon 수행 명령

 

nodemon을 실행하기 위해 'package.json'을 수정하자.

// package.json

{
  "name": "express-example",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "dev": "nodemon",
    "build": "tsc && node dist"
  },
  "devDependencies": {
    "@types/express": "^4.17.13",
    "@types/node": "^17.0.25",
    "nodemon": "^2.0.15"
  },
  "dependencies": {
    "express": "^4.17.3"
  }
}

dev 명령어로 nodemon을 실행하고

build 명령어로 dist 폴더에 js로 트랜스파일된 파일을 만들 수 있다.

yarn run dev	// nodemon으로 서버 실행
yarn run build	// 프로젝트 build

 

 

 

서버 실행

터미널에 yarn run dev 를 실행한다.

서버가 실행된다.

 

브라우저에 'localhost:8000'을 입력하고 접속한다.

 

서버가 정상적으로 구동되고 있음을 확인할 수 있다.

 

nodemon의 정상 구동을 확인하기 위해

../src/index.ts 에서 코드를 수정해본다.

// ../src/index.ts

import express, { Request, Response, NextFunction } from 'express';

const app = express();

app.get('/', (req: Request, res: Response, next: NextFunction) => {
    res.send('Hi! This is my first express server. My name is Woojin.');
});	// 문구 수정!!

app.listen('8000', () => {
    console.log(`
    #############################################
        🛡️ Server listening on port: 8000 🛡️
    #############################################    
    `)
})

수정 후 저장하면, 터미널에서 서버가 재시작됨을 볼 수 있다.

 

브라우저도 새로고침해보자.

정상적으로 변경되었다!

 

 

 

프로젝트 build

터미널에 아래와 같이 실행시킨다.

yarn run build

dev와 마찬가지로 서버가 실행되는데,

'dist'라는 폴더가 생성됨을 확인할 수 있다.

dist 폴더에는 TS로 작성한 파일이 JS로 트랜스파일되어 저장되는데, node로 서버를 실행하기 위함이다.

실제 서버를 배포할 때는 dist 폴더에 있는 파일들이 배포된다.

(참고 : 트랜스파일이란 비슷한 추상화 수준을 가진 언어로의 컴파일, 컴파일에 속하는 개념)

 

 

 

 

 

 

다음 포스팅에서는, 방금 세팅한 서버를 기반으로

라우팅을 정의해보자.

 

 

 

 

 

반응형
반응형

Yarn이란?

npm을 보완하여 나온 패키지 매니저

 

Express란?

Node.js를 위한 서버 프레임워크

 

 

 

 

1. 설치

npm install yarn -g

yarn --version

yarn을 글로벌로 설치하고, 설치 완료 여부 확인을 위해 버전을 확인한다.

package.json 파일이 설치되었을 것이다.

 

 

2. 초기 세팅

폴더를 생성한 후 디렉토리를 이동한 다음, yarn --init 을 실행한다.

name만 입력해주고 나머지는 enter로 넘긴다.

 

3. Express 설치

yarn add express	// express 설치
yarn add -D @types/node @types/express	// typescript용 모듈 설치
yarn add -D nodemon	// 서버 코드 변경 시 자동 재시작을 도와줌
tsc --init 	// tsconfig.json 파일 생성, TS를 JS로 컴파일하는 옵션 설정

기본으로 생성되는 tsconfig.json을 수정할 필요가 있다. (검색 참고)

// tsconfig.json

{
  "compilerOptions": {
    "target": "es6", // 어떤 버전으로 컴파일
    "allowSyntheticDefaultImports": true, // default export가 없는 모듈에서 default imports를 허용
    "experimentalDecorators": true, // decorator 실험적 허용
    "emitDecoratorMetadata": true, // 데코레이터가 있는 선언에 대해 특정 타입의 메타 데이터를 내보내는 실험적인 지원
    "skipLibCheck": true, // 정의 파일 타입 체크 여부
    "moduleResolution": "node", // commonJS -> node 에서 동작
    "module": "commonjs", // import 문법
    "strict": true, // 타입 검사 엄격하게 
    "pretty": true, // error 메시지 예쁘게
    "sourceMap": true, // 소스맵 파일 생성 -> .ts가 .js 파일로 트랜스 시 .js.map 생성
    "outDir": "./dist", // 트랜스 파일 (.js) 저장 경로
    "allowJs": true, // js 파일 ts에서 import 허용
    "esModuleInterop": true, // ES6 모듈 사양을 준수하여 CommonJS 모듈을 가져올 수 있게 허용
    "typeRoots": [
      "./src/types/express.d.ts", // 타입(*.d.ts)파일을 가져올 디렉토리 설정
      "./node_modules/@types" // 설정 안할시 기본적으로 ./node_modules/@types
    ]
  },
  "include": [
    "./src/**/*" // build 시 포함
  ],
  "exclude": [
    "node_modules", // build 시 제외
    "tests"
  ]
}

 

 

4. nodemon.json 생성

// nodemon.json

{
    "watch": [
        "src",
        ".env"
    ],
    "ext": "js,ts,json",
    "ignore": [
        "src/**/*.spec.ts"  
    ],
    "exec": "ts-node  --transpile-only ./src/index.ts"
}

watch : 변경 감지 경로

ext : 변경 감지 확장자

ignore : 변경 감지 제외

exec : nodemon 수행 명령

 

5. package.json 수정

// package.json

{
  "name": "express-example",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "dev": "nodemon",
    "build": "tsc && node dist"
  },
  "devDependencies": {
    "@types/express": "^4.17.13",
    "@types/node": "^17.0.25",
    "nodemon": "^2.0.15"
  }
}

 

6. src폴더와 그 내부에 index.ts 생성

// index.ts

import express, { Request, Response, NextFunction } from 'express';

const app = express();

app.get('/', (req: Request, res: Response, next: NextFunction) => {
    res.send('Hi! This is my first express server');
});

app.listen('8000', () => {
    console.log(`
        #############################################
        🛡️ Server listening on port: 8000 🛡️
        #############################################  
    `);
})

 

7. 서버 실행 및 프로젝트 빌드

yarn run dev	// package.json에 따라 nodemon 실행

yarn run build	// package.json에 따라 tsc && node dist 실행

build 명령어 실행 시 'dist' 폴더가 생성된다.

그 안에는 ts를 js로 변환한 트랜스 파일, sourceMap 파일이 생성된다.

 

지금까지 세팅한 결과로 위와 같은 디렉토리 구조가 생성된다.

반응형
반응형

비동기처리란?

작업의 완료 유무와 상관 없이 동시에 다음 작업을 수행하는 처리 방식을 말한다.

(동기처리는 작업이 반드시 순서대로만 수행되는 처리 방식을 의미한다.)

 

비동기처리를 수행하기 위해 TS(JS)에서는 Callback Function, Promise, Async-Await 등을 이용한다.

각각이 비동기처리를 어떻게 수행하는지 순서대로 알아보자.

 

 

 

 

Callback Function

Callback Function은 특정 조건에서 실행되는 함수로, 특정 시간에 도달하거나 특정 이벤트가 실행됐을 때 시스템에서 호출하는 함수이다.

간략하게만 알아보자.

console.log('Hello Server');

setTimeout((): void => {
    console.log('Hello Server 2');
}, 3000); // 3초

console.log('Hello Server 3');

setTimeout()은 특정 시간 이후에 콜백 함수를 실행시키는 내장함수이다.

만약 위 코드가 동기처리된다면 Hello Server, 2, 3 순서대로 출력되겠지만,

위와 같이 비동기처리된다.

 

위와 같은 Callback Function을 활용한 비동기처리의 문제점은 코드가 복잡해진다는 점이다.

let serverList: string[] = [];

setTimeout((name): void => {
    serverList = [...serverList, name];
    console.log(serverList);
    
    setTimeout((name): void => {
        serverList = [...serverList, name];
        console.log(serverList);
        
        setTimeout((name): void => {
            serverList = [...serverList, name];
            console.log(serverList);
        }, 500, 'name3');
    }, 500, 'name2');   
}, 500, 'name1');

위와 같은 코드는 안으로 심하게 파여 흔히 '콜백 지옥(Callback hell)'이라고 표현한다.

 

 

 

 

Promise

Callback hell을 극복하기 위해 만들어진 것으로,

Pending(대기), Fulfilled(이행), Rejected(실패)의 3가지 상태가 있다.

const condition: boolean = true;

const promise = new Promise((resolve, reject) => {
    if (condition) {
        setTimeout(() => {
            resolve('Success');    
        }, 1000);
        // resolve('Success');
    } else {
        reject(new Error('Error! Condition is false'))
    }
});

promise
    .then((resolveData): void => console.log(resolveData))
    .catch(err => console.log(err));

console.log('test')

위 코드를 통해 Promise의 사용 방법을 알 수 있다.

우선 new Promise를 통해 promise에 Promise 객체를 할당한다.

condition이 true이므로 setTimeout이 실행되는데,

promise.then에서 then은 resolve가 실행된 이후 호출된다.

 

즉 setTimeout()의 조건에 따라 1초가 지나기 전이 Pending(대기)상태이고, 이때제일 아래에 있는 console.log('test')가 실행된다.

이후 1초가 지나 resolve가 실행되면 'Success' 값이 then으로 전달되고 그것을 resolveData라는 매개변수로 받는다.

이 상태가 Fulfilled(이행) 상태이다.

 

만약 condition이 false였다면 reject가 실행되었을 것이고, catch가 호출된다. 이 상태를 Rejected(실패) 라고 한다.

 

조금 복잡한 형태의 코드를 통해

Promise Chaining에 대해 알아보자. (Promise를 연쇄적으로 호출하는 것을 의미한다.)

 

const restaurant = (callback: () => void, time: number) => {
    setTimeout(callback, time);
};

const order = (): Promise<string> => {		// order 객체가 string을 반환하는 Promise 객체라는 뜻이다.
    return new Promise((resolve, reject) => {	// Promise 객체를 반환한다
        restaurant(() => {						// Promise 내부 함수
            console.log('레스토랑 진행 상황 - 음식 주문');
            resolve('음식 주문 시작');			
        }, 1000);
    });
}

const cook = (progress: string): Promise<string> => {	// cook은 string인 progress라는 매개변수를 받아, string을 반환하는 Promise 객체라는 뜻.
    return new Promise((resolve, reject) => {
        console.log('레스토랑 진행 상황 - 음식 조리');
        restaurant(() => {
            resolve(`${progress} -> 음식 조리 중`);	// 전달받은 progress에 텍스트를 붙여 resolve로 반환한다.
        }, 2000);
    });
}

const serving = (progress: string): Promise<string>  => {
    return new Promise((resolve, reject) => {
        console.log('레스토랑 진행 상황 - 음식 서빙');
        restaurant(()=> {
            resolve(`${progress} -> 음식 서빙 중`);
        }, 3000);
    });
}

const eat = (progress: string): Promise<string> => {
    return new Promise((resolve, reject) => {
        console.log('레스토랑 진행 상황 - 음식 먹기');
        restaurant(() => {
            resolve(`${progress} -> 음식 먹는 중`);
        }, 4000);
    });
}

order()	// order() 실행
    .then(progress => cook(progress))	// order 내부에서 resolve가 실행되어 반환되는 값이 progress로 들어간다. progress => cook(progress)는 함수를 then의 매개변수로 넣은 것.
    .then(progress => serving(progress))
    .then(progress => eat(progress))
    .then(progress => console.log(progress));

progress가 연쇄적으로 전달된다.

 

 

 

 

Async - Await

ES2017부터 제공되는 혁신적인 기능! 이라고 한다.

코드를 짤 때 동기식 코드처럼 사고하고 코딩할 수 있게 되었다.

 

우선 함수 선언부터 알아보자.

// 함수 표현식
const asyncFunction1 = async () => {

}

// // 함수 선언식
async function asyncFunction2() {

}

위와 같이 선언한다.

 

let asyncFunc1 = (msg: string): Promise<string> => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(`asyncFunc1 - ${msg}`);
        }, 1000);
    });
};

let asyncFunc2 = (msg: string): Promise<string> => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(`asyncFunc2 - ${msg}`);
        }, 1500);
    });
};

const asyncMain = async (): Promise<void> => {
    let result = await asyncFunc1('hi');
    console.log(result);
    result = await asyncFunc2('hello');
    console.log(result);
};

const promiseMain = (): void => {
    asyncFunc1('hi').then((result: string) => {
        console.log(result);
        return asyncFunc2('hello');
    }).then((result: string) => {
        console.log(result);
    });
};

asyncMain();

promiseMain();

await은 Promise가 실행될 때까지 기다리겠다는 뜻이다.

익숙한 동기 코드의 실행 방식처럼, 실행 여부를 then, catch로 분기하지 않고 코딩할 수 있다.

 

 

 

 

 

 

반응형
반응형

 

사실 엄밀히 말하면 서버 배포는 아니고,

(미안하다.. 이 글 보여주려고 어그로 끌었다..)

ngrok를 사용하면, 나의 로컬 서버에 다른 사람이 방화벽을 뚫고 접근할 수 있다.

 

서버 배포 전, postman 등을 통해 mock 기능으로 api 테스트를 클라이언트 개발자와 진행할 수 있지만,

아무래도 로컬에 (백엔드 개발자가 만진) DB와 직접 연결되어 테스트해보는 것이 더 나을 수 있다.

 

방법은 무척 간단하니 쉽게 따라해보자.

 

 

 

1. ngrok 다운로드

다운로드 주소 : https://ngrok.com/download

주소에 접근하면 brew, 직접 다운로드 두 가지 방법 중 선택하라고 한다.

 

회원가입을 통해 token을 발급받을 수 있다. (Google, Github 소셜 회원가입 지원)

 

실행할 환경의 'ngrok http <포트번호>' 실행하면 되는데,

아래에서 실행해보자

 

 

 

 

2. 장고 서버 실행 및 배포

개발 중인 환경으로 이동하여 장고 서버를 실행해준다.

이때 0.0.0.0:8000 주소로 실행한다. (현재 요청된 호스트의 IP나 이름에 관계없이 실행한다는 뜻)

% python manage.py runserver 0.0.0.0:8000

 

* 실행이 되지 않는다면 프로젝트 앱 디렉토리의 'settings.py'에서 아래와 같이 수정한다.

(모든 주소의 호스팅을 허용)

...
ALLOWED_HOSTS = ['*']
...

 

터미널에서 ngrok를 실행한다.

% ngrok http 8000 // 8000은 장고 포트 번호

위와 같이 뜨면 정상. (임시주소라 상관없지만 그냥 가려봄.)

빨간색 박스로 가둔 'https ..' 주소를 복사하여 사용하면 된다!

 

핸드폰으로 주소에 접속하여 확인해보자.

 

 

아래는 핸드폰으로 해당 주소에 접속한 화면이다.

 

 

url을 추가하면 원하는 api에 접근 가능하다.

헤더에 인증정보를 입력하지 않아 동작하지 않지만,

앱 개발자가 알아서 할 것이라 믿는다 ㅎㅎ

 

 

 

 

반응형
반응형

 

타입스크립트(Typescript)란?

자바스크립트(Javascript)와 100% 호환되는 언어로, 이름처럼 type이 중요해진 JS라고 볼 수 있다.

 

JS에서는 변수의 타입을 지정하지 않는다.

let a = 3;
const PI = 3.14;

이 경우, 변수의 타입이 잘못 지정되거나 외부에서 받아온 데이터의 타입을 잘못 이해해서 오류가 발생할 수 있다.

따라서, 사용하기는 조금 까다롭지만 안전한 프로그래밍이 가능한 TS의 인기가 올라가고 있다.

 

JS의 사용이 자유로워 제한하는 문법이 추가된 TS, 문법이 다소 딱딱한 JAVA를 사용하는 Kotlin이 대화를 나누고 있다.

 

 

 

 

타입 지정하기

let isDone: boolean = true;
const str: string = 'hello';
let num: number = 2;

위와 같이 '변수명: 타입' 으로 타입을 지정한다.

 

let array: number[] = [1, 2, 3]; // Array<number>

const strArr: Array<string> = ['hello', 'world'];

const objArr: Array<object> = [
    { item: 'value1' },
    { item: 'value2' }
];

배열은 위와 같이 지정한다.

 

objArr.map((obj: any) => {
    console.log(`item: ${obj.item}`)
})

/*
*item: value1
*item: value2
*/

map() 메서드를 활용하면 배열을 순회하면서 요소들을 호출한다.

'any'는 모든 타입을 사용할 수 있다.

 

const foo = (obj: object): void => {
    console.log(obj);
};

const boo = (obj: Object): void => {
    console.log(obj);
}

// foo('hi')
// Argument of type 'string' is not assignable to parameter of type 'object'.

boo('hi')

object : 원시 타입(숫자형, 문자형, 불린형)이 아닌 타입만 할당 가능

Object : 모든 타입 할당 가능

 

function foo2(a: number, b: number): number {
    return a + b;
};

const boo2 = (a: number, b: number): number => {
    return a + b;
};

함수의 타입 지정은 위와 같이 한다.

 

let a: null = null;
// let x: null = 2;

let b: undefined = undefined;
// let y: undefined = null;

null과 undefined도 타입으로 지정해야하고, 동일한 이름의 값만 할당할 수 있다.

 

let myName: any = 'Woojin';
let myNameLength: number = (<string>myName).length;

let yourName: any = 'Lee';
let yourNameLength: number = (yourName as string).length;

any는 상술했듯 모든 타입을 할당할 수 있는데, 대신 변수의 타입을 알 수 없다.

length 함수처럼 특정 타입이어야만 사용할 수 있는 것들에는 위와 같은 방법으로 타입을 지정해줘야 한다.

(근데 지정 안해도 typeof 로는 string이라고 나온다. 뭐지?)

 

 

 

 

인터페이스(interface)

객체 지향 언어인 JS.

객체(Object)를 많이 사용하는데, TS에서는 객체의 타입을 지정할 수 있는 방법을 만들어놓았다.

인터페이스는 객체의 프로퍼티들의 타입을 지정해놓은 틀의 개념이다.

 

interface Closet {
    name: string;
    shirt: number;
    pants: number;
    sunglass?: number; <?: 선택적 프로퍼티>
    hat?: number;
}

위와 같이 객체에 지정할 인터페이스를 선언한 다음,

 

const ohmygirl: Array<Closet> = [
    {
        name: '효정',
        shirt: 5,
        pants: 2,
    },
    {
        name: '아린',
        shirt: 2,
        pants: 8,
        hat: 2 // 선택적!
    }
];

객체의 타입을 위에서 선언한 인터페이스로 지정한다.

선택적 프로퍼티는 인터페이스 선언 시 물음표를 키값에 붙인다.

해당 프로퍼티는 객체에 존재해도 되고 안 해도 된다.

 

interface Sopt {
    season: number;
    group: string[];
    part: string[];
    president: string;
    introduce(number): string[];
}

const currentSopt: Sopt = {
    season: 30,
    group: ['YB', 'OB'],
    part: ['King the Server', 'Plan', 'Design', 'Android', 'Web', 'iOS'],
    president: 'Kim',
    introduce: function (this.season) {	// 매개변수는 예시를 위해 넣었고, 쓰이지 않음.
        return this.part.map(name => {
            console.log(`솝트 내 파트는 ${name} 등이 있어요!`);
            return `솝트 내 파트는 ${name} 등이 있어요!`
        });
    }
};

위와 같이 인터페이스에 '함수'도 프로퍼티로 넣을 수 있다.

매개변수와 결과값의 타입을 지정해야 한다.

매개변수가 없다면 공백을, 결과값이 없다면 void를 넣어준다.

 

interface Member {
    name: string;
    group: string;
}

interface Dinner {
    member: Member[];
    shuffle: {
        (array: Array<Member>): Array<Member>;
    };
    organize: {
        (array: Array<Member>): void;
    };    
}

위의 코드처럼, 인터페이스 안에 인터페이스를 사용할 수도 있다.

shuffle이라는 함수의 매개변수 array의 타입을 Array<Member>로 지정하고

그 결과값도 Array<Member>로 지정한 코드이다.

 

 

* 본 포스팅은 SOPT 30th 서버 파트 1주차 세미나를 듣고 복습을 위해 작성한 글입니다.

서팟장 잡채님 만세.

 

반응형
반응형

자바스크립트 기초 문법 정리.

 

참고 강좌 영상 : https://youtu.be/DuXA1t6hl3U

 

위 강좌 #12 ~ #14 를 참고하였다.

 

 


객체(Object)

아래와 같은 자료형을 객체라고 한다.

const superman = {
    name: 'clark',
    age: 33,
}

name을 key,

'clark'를 value(값)라고 한다.

* 마지막 쉼표는 선택이지만, 수정 시 용이하므로 넣는 걸로.

(근데 이제 JSON에서는 저러면 오류난다. 개빡쳐)

 

객체의 값을 CRUD하는 법

// Create
superman.gender = 'male';
superman['hairColor'] // 'black';

// Read
superman.name // 'clark';
superman['age'] // 33;

// Update
superman.name = 'tom';
superman['age'] = 17;

// Delete
delete superman.hairColor;

 

const superman = {
	name: 'clark',
    age: 33
}

superman.birthday; // undefined
'birthday' in superman; // false
'age' in superman; // true

프로퍼티 존재 여부 확인은 'in'을 이용하는데, key에만 사용할 수 있다.

 

 

for ... in 반복문

객체의 프로퍼티를 순회하면서 반복문을 사용할 수 있다.

for(let key in superman){
    console.log(key);
    console.log(superman[key]);
}

// name
// clark
// age
// 33

key가 'name'꼴의 string 자료형이라,

superman.key 는 사용할 수 없다.(superman.'name' 이 되니까.)

 

makeObject = (name, age) => {
    return {
        name: name,
        age: age,
    }
}

const Mike = makeObject('Mike', 30);
console.log(Mike);

name, age 인자를 받아 객체를 생성하는 함수이다.

 

method : 객체 프로퍼티로 할당된 함수

객체 안에는 값 뿐만 아니라 함수도 저장할 수 있다.

const superman = {
    name: 'clark',
    age: 33,
    fly: function() {
        console.log('fly');
    }
};

superman.fly();

// 축약형
const superman = {
    name: 'clark',
    age: 33,
    fly() {
        console.log('fly');
    }
};

 

메서드와 this

let boy = {
    name: 'Mike',
    showName: function() {
        console.log(boy.name);
    }
};

boy.showName();

위 코드는 문제가 있다.

let man = boy;

이렇게 선언하면 man 객체는 boy와 같은 정보를 가진다. 그런데,

boy = null;
man.showName();

boy에 null을 넣고 man에서 showName() 메서드를 실행하면 오류가 발생한다.

메서드 정의에서 boy.name을 불러올 수 없기 때문이다.

이때 boy.name 대신 this.name을 사용할 수 있다.

 

let boy = {
    name: 'Mike',
    showName: function() {
        console.log(this.name);
    }
};

객체 안에서 this는 객체를 담고 있다.

하지만 화살표 함수는 this를 가지고 있지 않다.

화살표 함수 내부에서 this를 사용하면 global, widnow와 같은 전역 변수를 불러온다.

따라서 객체의 메서드는 화살표 함수를 사용하지 않는 것이 좋다.

 

 

 

 

배열(Array)

numbers = [1, 2, 3, 4, 5];

numbers.length // 5 (요소 개수)

numbers.push(6); // 끝에 요소 추가
console.log(numbers); // [1, 2, 3, 4, 5, 6]

numbers.pop(); // 끝 요소 삭제
console.log(numbers); // [1, 2, 3, 4, 5]

numbers.unshift(-1, 0); // 앞에 요소 추가 (복수 가능)
console.log(numbers); // [-1, 0, 1, 2, 3, 4, 5]

numbers.shift(); // 앞 요소 삭제
console.log(numbers); // [0, 1, 2, 3, 4, 5]

 

배열을 쓰는 대표적 이유 중 하나는 반복문인데, 용례는 아래와 같다.

let days = ['mon', 'tue', 'wed'];

// 반복문 : for
for(let index = 0; index < days.length; index++){
	...
};

// 반복문 : for ... of
for(let day of days){
	...
};

 

 

 

 

 

 

 

 

 

 

 

반응형

+ Recent posts