vsftpd의  사용모드를 passive모드로 변경하는 방법에 대해 알아봅니다.

 

 

 

0. vsFTPD 설치

 

우분투 서버에 vsFTPD를 설치하는 방법은 다음 글을 참고해 주시기 바랍니다.

 

 

 

1. Active 모드

 

vsFTPD를 처음 설치한 채 실행하면 기본적으로 액티브 모드로 실행됩니다. 액티브 모드에서의 동작은 다음과 같습니다.

 

출처: https://madplay.github.io/img/post/2019-03-09-ftp-active-passive-1.png

 

이 동작 순서를 좀 더 자세히 알아보겠습니다.

 

  • 1. 클라이언트가 서버의 21번 포트로 접속한 뒤 클라이언트가 사용할 두 번째 포트를 서버에 알려줍니다.
  • 2. 서버는 클라이언트의 요청에 대해 ACK을 날려줍니다.
  • 3. 서버의 20번 포트는 클라이언트가 알려준 두번째 포트로 접속을 시도합니다.
  • 4. 클라이언트는 서버의 요청에 대해 ACK을 날려줍니다.

이 과정을 보면 액티브 모드는 결국 클라이언트가 서버에 접속하는 것이 아닌 "서버가 클라이언트에 접속하는 것"임을 알 수 있습니다. 만약 클라이언트에 방화벽이 있다던가 외부의 접속을 허용하지 않는다던가 두 번째 포트가 이미 사용 중이라면 FTP 접속이 정상적으로 이루어지지 않을 것입니다. 이러한 문제를 해결하기 위해 나온 모드가 패시브 모드입니다.

 

 

 

2. Passive 모드

 

액티브 모드의 문제점이 서버가 클라이언트에 접속해서 발생했으니 패시브 모드는 클라이언트가 서버로 접속하는것임을 예상해 볼 수 있습니다. 패시브 모드가 동작하는 순서는 다음과 같습니다.

 

출처: https://madplay.github.io/img/post/2019-03-09-ftp-active-passive-2.png

 

  • 1. 클라이언트는 서버의 커맨드 포트로 접속합니다.
  • 2. 서버는 클라이언트가 사용할 두번째 포트를 클라이언트에게 알려줍니다.
  • 3. 클라이언트는 서버로 부터 받은 두 번째 포트로 서버에 접속합니다.
  • 4. 서버는 클라이언트의 접속에 대해 ACK을 날려줍니다.

차이점이 와닿으시나요? 패시브 모드에서는 "클라이언트가 서버에 접속"합니다. 앞의 액티브 모드에서 사용한 포트 대신 1024번 이후의 임의 포트를 데이터 채널 포트로 사용합니다.

 

 

 

3. 연결 방식에 따른 Port 사용 주의 사항.

 

액티브 모드는 클라이언트의 20번 포트에 연결할 수 없다면 데이터 채널을 연결할 수 없습니다. 따라서 액티브 모드에서 방화벽 설정으로 서버 측 20번 포트에 아웃바운드 허용, 클라이언트 측에는 인바운드 허용을 해주어야 합니다.

 

패시브 모드는 방화벽에서 서버측에서 사용하는 데이터 채널의 포트에 대해 인바운드 허용 설정이 필요합니다. 별도의 설정을 하지 않으면 설명한 바와 같이 1024 이후의 모든 포트를 사용하게 되어 모든 포트에 대해 인바운드 허용 설정이 필요하게 됩니다. 따라서 서버 측에서 데이터 채널 포트 범위를 지정해 특정 범위의 포트만 인바운드를 허용해 줍시다.

 

 

 

4. vsFTPD 액티브 모드에서 패시브 모드로 전환하기.

 

vsFTPD를 설치 한 후 설정 파일을 수정합니다.

 

> sudo vi /etc/vsftpd.conf

 

맨 아래 다음 설정을 추가합니다.

 

pasv_enable=Yes
pasv_max_port=11001
pasv_min_port=11050

 

위의 예시는 패시브 모드에서 데이터 채널 포트로 11001번부터 11050번까지 사용한다는 예시입니다. 원하는 포트로 변경해서 작성해 주시고 서비스를 재시작합니다

 

> sudo service vsftpd restart

 

이제 vsFTPSD는 패시브 모드로 동작합니다.

 

 

 

 

 

반응형

Loopback4를 이용해 Postgresql과 연결하는 방법에 대해 알아봅니다.

 

 

 

 

1. Loopback4 및 Postgresql 준비

 

다음 글을 참고해 Loopback4와 Postgresql을 미리 준비합니다.

 

 

2. Model 정의하기.

 

우선 먼저 모델을 정의해 봅시다. 여기서 정의한 모델은 Postgresql의 테이블과 같은 개념이 된다고 보시면 됩니다. 다음 명령어를 통해 User 모델을 생성합니다.

 

> lb4 model

 

? Model 클래스 이름: User
? 모델 기본 클래스를 선택하십시오. Entity (A persisted model with an ID)
? 추가(자유 양식) 특성을 허용합니까? No
Model User will be created in src/models/user.model.ts

특성을 User에 추가합니다.
완료되면 빈 특성 이름 입력

? 특성 이름 입력: id
? 특성 유형: number
? ID 특성이 id입니까? Yes
? id이(가) 자동으로 생성되었습니까? Yes

다른 특성을 User에 추가합니다.
완료되면 빈 특성 이름 입력

? 특성 이름 입력: user_email
? 특성 유형: string
? 필수입니까? Yes
? 기본값 [leave blank for none]: 

다른 특성을 User에 추가합니다.
완료되면 빈 특성 이름 입력

? 특성 이름 입력: user_name
? 특성 유형: string
? 필수입니까? Yes
? 기본값 [leave blank for none]: 

다른 특성을 User에 추가합니다.
완료되면 빈 특성 이름 입력

? 특성 이름 입력: 
   create src/models/user.model.ts
   update src/models/index.ts

Model User was created in src/models/

 

위의 명령을 따라 User 모델을 생성하면 .\src\models\user.model.ts파일이 자동으로 생성됩니다. id에 대해 우린 id값을 y로 선택해 id속성이 ture가 된 것을 확인할 수 있으며 자동생성 옵션을 켰기 때문에 generate도 true로 설정된 것을 확인할 수 있습니다. 이는 User테이블의 PK는 id가 될 것이며 이 값은 별도로 받지 않아도 자동으로 증가하는 값을 가질 것을 의미합니다.

 

 

 

3. Datasource 생성하기.

 

모델을 만들었으니 이제 우리가 사용할 DB가 Postgresql임을 알리는 Datasource를 생성해 봅니다. 다음명령어를 통해 ds라는 이름을 가지는 datasource를 생성합니다.

 

> lb4 datasource

 

? Datasource 이름: ds
? Select the connector for ds: PostgreSQL (supported by StrongLoop)
? Connection String url to override other settings (eg: postgres://username:password@localhost/database): postgresq://smoh:{PASSWORD}@{DBHOST}:{DBPORT}/lb4
? host: {DBHOST}
? port: {DBPORT}
? user: smoh
? password: [hidden]
? database: lb4
   create src/datasources/ds.datasource.config.json
   create src/datasources/ds.datasource.ts

 

위와같이 값을 입력해 ds를 생성해 주시면 됩니다. 가려진 위치에는 미리 준비한 postgresql의 정보를 채워 주시면 됩니다. 이 예시에서는 lb4라는 db를 사용할 예정입니다.

 

생성된 datasource는 ./src/datasources/ds.datasource.* 파일입니다. 만약 정보를 잘못 기입하셨어도 config.json에서 정보를 수정할 수 있습니다.

 

 

 

4. Repository 생성

 

Model과 Datasource가 준비되었으니 이제 Repository를 생성할 수 있습니다. 다음 명령어를 통해 DefaultCrudRepository를 생성해 보도록 하겠습니다.

 

> lb4 repository

 

? 데이터 소스를 선택하십시오. DsDatasource
? 저장소를 생성할 모드 선택 User
? 저장소 기본 클래스를 선택하십시오. DefaultCrudRepository (Legacy juggler bridge)
   create src/repositories/user.repository.ts
   update src/repositories/index.ts

Repository UserRepository was created in src/repositories/

 

그 후 ./src/repositories 로 이동하면 ds와 User을 이용해 user.repository.ts가 자동으로 생성된 것을 확인할 수 있습니다.

 

 

 

5. Controller 생성

 

Repository까지 준비되었으니 남은건 실제 동작을 구현하는 것입니다. 다음 명령어로 Controller를 추가합니다.

 

> lb4 controller

 

? Controller 클래스 이름: User
Controller User will be created in src/controllers/user.controller.ts

? 생성할 제어기는 어떤 유형입니까? REST Controller with CRUD functions
? 이 CRUD 저장소에서 사용할 모델 이름은 무엇입니까? User
? CRUD 저장소의 이름은 무엇입니까? UserRepository
? ID 특성의 이름은 무엇입니까? id
? ID의 유형은 무엇입니까? number
? 인스턴스를 새로 작성할 때 ID가 생략되었습니까? Yes
? CRUD 오퍼레이션의 기본 HTTP 경로 이름은 무엇입니까? /users
   create src/controllers/user.controller.ts
   update src/controllers/index.ts

Controller User was created in src/controllers/

 

이제 ./src/controllers/user.controller.ts에서 자동으로 생성된 컨트롤러를 확인할 수 있습니다. 여기에 엔드포인트와 원하는 작업을 구현하시면 됩니다.

 

 

 

6. DB Migrate

 

잠깐 뒤돌아봅시다. 우리가 지금까지 DB작업을 한적이 있었나요? 모델을 만들었는데 DB에 User테이블은 있나요? 물론 직접 DB에 붙어서 수동으로 테이블을 만들어 줄 수도 있습니다만 Loopback은 그럴 필요 없이 자동으로 모델에 맞게 테이블까지 만들어줍니다!

 

다만 이 기능은 공식 문서에도 불안정하다고 적혀있으므로 사용에 주의가 필요합니다. 직접 DB의 데이터를 건드려 모델과 다른점을 수정하기 때문에 데이터가 유실되거나 심지어 테이블 정보가 변경될 수 있습니다.

 

마이그레이션을 하는 방법은 여러가지가 있습니다. 프로그램이 시작될 때 자동으로 진행되게 하는 방법이 있고 명시적으로 개발자가 직접 진행하는 방법이 있습니다. 실무에선 어떻게 사용될지 모르겠지만 일단 DB작업이므로 개발자가 직접 수행하는 방법에 대해서 설명하도록 하겠습니다.

 

다음 명령어를 차근차근 수행해 주세요

 

> npm run clean

 

> pg-app@1.0.0 clean /Users/smoh/Documents/Dev/Loopback/pg-app
> lb-clean dist *.tsbuildinfo .eslintcache

 

위 명렁을 수행하고 나면 ./dist 폴더가 사라진 것을 확인할 수 있습니다.

 

> npm run build

 

> pg-app@1.0.0 build /Users/smoh/Documents/Dev/Loopback/pg-app
> lb-tsc

 

다시 dist 폴더에 파일이 생성된 것을 확인할 수 있습니다. 클린 후 다시 빌드함으로써 지금까지 우리가 작업한 내용을 반영시켜 줍니다.

 

진짜 마이그레이션을 진행하기 전에 ./dist/migrate.js파일을 확인해 봅시다.

 

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const application_1 = require("./application");
async function migrate(args) {
    const existingSchema = args.includes('--rebuild') ? 'drop' : 'alter';
    console.log('Migrating schemas (%s existing schema)', existingSchema);
    const app = new application_1.PgAppApplication();
    await app.boot();
    await app.migrateSchema({ existingSchema });
    // Connectors usually keep a pool of opened connections,
    // this keeps the process running even after all work is done.
    // We need to exit explicitly.
    process.exit(0);
}
exports.migrate = migrate;
migrate(process.argv).catch(err => {
    console.error('Cannot migrate database schema', err);
    process.exit(1);
});
//# sourceMappingURL=migrate.js.map

 

중간에 매개변수를 확인하는 로직이 보이시나요? existingSchema의 값을 정할 때 "--rebuild" 옵션의 여부에 따라 이미 테이블이 존재할 때 동작을 결정할 수 있습니다. 

 

진짜 마이그레이션을 수행해봅시다.

 

> npm run migrate -- --rebuild 혹은 >npm run migrate

 

> pg-app@1.0.0 migrate /Users/smoh/Documents/Dev/Loopback/pg-app
> node ./dist/migrate "--rebuild"

Migrating schemas (drop existing schema)

 

--rebuild 옵션을 주어 이미 테이블이 존재했으면 drop후 재생성 한다는 메시지가 출력되었습니다. 이제 실제 DB로 가 보면 테이블이 모델에 맞게 자동으로 생성되었음을 확인할 수 있습니다.

 

 

이제 준비가 끝났습니다. 다음 명령어를 통해 Loopback을 실행시킵시다.

 

> npm start

 

 

 

7. 테스트

 

실행시킨 후 http://localhost:3000/explorer/ 로 이동하게 되면 꽤나 깔끔한 페이지가 보입니다. 우리가 생성한 컨트롤러에 맞춰서 Loopback이 자동으로 이 페이지를 만들어 줍니다.

 

 

 

postman을 통해 테스트를 진행해도 되지만 이 페이지에 보이는 컨트롤러를 직접 클릭해 테스트해 볼수도 있습니다.

 

 

동작중 하나를 클릭 후 Try it out을 눌러보세요.

 

 

그러면 위와같이 where 조건을 수정할 수 있게 됩니다. 동작에 맞게 적당히 수정 후 아래의 Execute 버튼을 눌러보세요.

 

 

아래의 Server response 영역에 처리 결과를 바로 확인해 볼 수 있습니다.

 

 

 

 

 

반응형

 

Typescript에서의 모듈과 그 사용법에 대해 알아봅니다.

 

 

 

1. 모듈(Module)

 

타입스크립트에서 index.ts와 같은 소스파일을 모듈이라고 합니다. 앞선 글에서 우리는 index.ts와 makePerson.ts파일 두 개를 만들었으므로 두 개의 모듈을 만든 셈입니다.

 

물론 하나의 소스코드에 모두 다 구현해도 됩니다. 하지만 유지보수 등 소스 코드 관리를 위해서 모듈마다 특정 기능을 구현하는 방식으로 소스코드를 분할하는 게 일반적인 방식입니다. 이러한 작업을 모듈화라고도 합니다.

 

index.ts 모듈에서 어떻게 makePerson.ts 모듈의 정보를 알고 가져다 쓸 수 있는 걸까요? 앞서 작성한 코드를 보면 아시겠지만 한 모듈이 다른 모듈에 어떤 내용이 있는지 서로 알게 해 주기 위해 타입스크립트에서는 export와 import라는 키워드를 제공합니다.

기능을 제공하는 모듈에서 export를, 기능을 사용하는 모듈에서 import를 사용해 다른 모듈의 기능을 가져다 쓸 수 있습니다.

 

 

 

2. 모듈화 예시

 

이제 실제 하나의 index.ts 모듈이 어떻게 모듈화가 이루어지는지 보도록 합니다. 이전 글을 참고하여 타입스크립트 프로젝트를 생성합니다.

 

다음처럼 index.ts파일에 모든 기능을 다 넣어두게 코딩합니다.

 

//// ./src/index.ts

let MAX_AGE = 100;

interface IPerson{
    name: string,
    age: number,
};

class Person implements IPerson{
    constructor(public name: string, public age: number){

    }
};

function makeRandomNumber(max: number = MAX_AGE): number{
    return Math.ceil((Math.random() * max));
};

const makePerson = (name: string, age = makeRandomNumber()) => ({name, age});

const testMakePerson = ():void => {
    let jane: IPerson = makePerson('Jane');
    let jack: IPerson = makePerson('Jack');
    console.log(jane, jack);
};

testMakePerson();

 

위와 같이 코딩했으면 다음 명령어를 통해 한번 실행해 봅시다.

 

> npm run dev

 

 

정상적으로 실행이 되시나요? 이제 이 모듈을 몇 가지 내용에 맞춰 모듈화 작업을 진행해 보겠습니다. 

 

우선 위에 작성한 코드를./src/person/Person.ts로 먼저 한번 나눠보겠습니다.

 

//// ./src/person/Person.ts

let MAX_AGE = 100;

export interface IPerson{
    name: string,
    age: number,
};

class Person implements IPerson{
    constructor(public name: string, public age: number){

    }
};

function makeRandomNumber(max: number = MAX_AGE): number{
    return Math.ceil((Math.random() * max));
};

export const makePerson = (name: string, age = makeRandomNumber()) => ({name, age});

 

//// ./src/index.ts

import {IPerson, makePerson} from './person/Person';

const testMakePerson = ():void => {
    let jane: IPerson = makePerson('Jane');
    let jack: IPerson = makePerson('Jack');
    console.log(jane, jack);
};

testMakePerson();

 

이전 코드와 달라진 점이 보이시나요? 앞서 설명한 것처럼 한 모듈에서 다른 모듈을 사용하기 위해선 위처럼 export와 import를 사용해 어떤 기능을 내보내고 가져와 쓸지 명시해 줘야 합니다.

 

 

 

3. export default

 

타입스크립트는 자바스크립트와의 호환성을 위해 export default 키워드를 제공합니다. 이 키워드는 한 모듈에서 단 하나의 기능에 붙일 수 있습니다. 이렇게 내보내진 기능은 import문으로 불러올 때 중괄호 없이 사용 가능하며 export 키워드와 함께 사용될 수 있습니다.

 

위의 코드를 아래와 같이 수정해 줍니다.

 

//// ./src/person/IPerson.tx

export default interface IPerson{
    name: string,
    age: number,
};

 

//// ./src/utils/makeRandomNumber.ts

let MAX_AGE = 100;

export function makeRandomNumber(max: number = MAX_AGE): number{
    return Math.ceil((Math.random() * max));
};

 

//// ./src/person/Person.ts

import IPerson from './IPerson';
import {makeRandomNumber} from '../utils/makeRandomNumber';


export default class Person implements IPerson{
    constructor(public name: string, public age: number){

    }
};

export const makePerson = (name: string, age = makeRandomNumber()) => ({name, age});

 

//// ./src/index.ts

import IPerson from './person/IPerson';
import Person, {makePerson} from './person/Person';

const testMakePerson = ():void => {
    let jane: IPerson = makePerson('Jane');
    let jack: IPerson = makePerson('Jack');
    console.log(jane, jack);
};

testMakePerson();

 

IPerson은 export default를 사용해 가져올 때 중괄호가 생략된 것을 확인할 수 있습니다. Person.ts에서는 export와 export default가 동시에 사용되어 index.ts에서 Person을 가져올 때 Person은 중괄호가 없고 makePerson은 중괄호가 있는 모습을 확인할 수 있습니다.

 

위의 파일을 실행해보세요.

 

 

모듈화를 통해 파일이 쪼개졌지만 여전히 잘 동작하는 것을 확인할 수 있습니다.

 

 

 

 

 

반응형

타입 스크립트와 타입 스크립트를 개발하기 위한 준비를 합니다.

 

 

 

1. Typescript

 

TypeScript는 오픈 소스 순수 객체 지향 프로그래밍 언어입니다.  TypeScript는 Apache2 라이선스에 따라 Microsoft에서 개발 및 유지 보수됩니다.

TypeScript의 위치는 위 그림과 같습니다. Javascript로 컴파일 되는 Javascript의 수퍼셋이라고 합니다. 컴파일이 필요하므로 타입 스크립트를 브라우저에서 바로 사용할 순 없습니다. 

타입 스크립트는 컴파일이 필요한 새로운 언어입니다. 대부분의 사람들이 JS를 사용할 수 있는데 굳이 새로운 언어를 다시 배워야 하는 것에 대한 거부감이 타입 스크립트 도입에 가장 큰 결점일 것입니다.

그러나 타입 스크립트를 도입하게 되면 그동안 js에서 느꼈던 결점을 대부분 보완해 줍니다. 인텔리센스를 통해 자동완성 기능을 제공해주며 명시적 형식의 선언으로 인해 컴파일 단계에서 오류를 줄일 수 있습니다. 또한 C# 및 Jave와 유사한 문법 스타일을 갖고 있으며 큰 규모의 js 프로젝트의 경우 복잡도를 많이 낮출 수 있습니다.

 

 

 

2. 타입스크립트 컴파일러 설치

 

이제 타입스크립트를 사용하기 위한 준비를 해봅시다. 물론 이 과정을 수행하기 전에 NodeJS를 먼저 설치해야 합니다. NodeJS설치 과정은 별도로 다루지 않겠습니다.

 

파워 셸 또는 터미널을 열고 다음 명령어를 수행합니다.

 

> npm i -g typescript

 

설치가 완료되었다면 다음 명령어를 통해 설치된 타입 스크립트의 버전을 확인할 수 있을 것입니다.

 

> tsc -v

 

 

이제 설치한 타입 스크립트 컴파일러를 이용해 ts파일을 js파일로 컴파일해봅시다. hello.ts파일을 만든 뒤 다음과 같이 코드를 작성해 주세요.

 

console.log(`Hello world!`);

 

이제 다음 명령어를 실행시켜 ts를 js로 컴파일 해 봅시다.

 

> tsc hello.ts

 

 

파일 목록에 hello.js가 생성된 것이 보이시나요? 제대로 컴파일되었습니다. 컴파일된 js파일은 다음 명령어로 실행할 수 있습니다.

 

> node hello.js

 

 

 

3. ts-node 사용하기.

 

위의 tsc는 ts를 js로 변환만 해줍니다. 변환과 실행까지 한 번에 하기 위해 ts-node를 사용해 봅시다. 다음 명령어를 통해 ts-node를 설치합니다.

 

> npm i -g ts-node

 

마찬가지로 다음 명령어를 통해 버전을 확인 해 제대로 설치되었는지 알 수 있습니다.

 

> ts-node -v

 

 

이제 ts-node를 사용해 봅시다. 다음 명령어를 실행하면 자동으로 ts파일을 js파일로 컴파일할 뿐 아니라 컴파일된 js파일을 실행시켜줍니다.

 

> ts-node hello.ts

 

 

 

4. 타입스크립트 프로젝트 생성하기.

 

타입스크립트 프로젝트는 NodeJS 프로젝트를 만든 뒤 개발 언어를 ts로 설정하는 순서로 생성합니다. 우선 원하는 폴더를 생성, 이동 한 뒤 NodeJS 프로젝트를 생성합니다.

 

> npm init --y

 

 

--y 옵션을 주고 프로젝트를 생성하면 위와 같은 기본 내용으로 알아서 프로젝트를 생성해 줍니다.

 

타입스크립트 관련 패키지 설치에 앞서 알아두어야 할 사항이 있습니다. 타입스크립트는 기본적으로 ESNext 자바스크립트 문법을 포함하지만 자바스크립트와는 완전히 다른 언어입니다.

타입스크립트에는 명시적 형식이 존재하기 때문에 추가적으로 타입 라이브러리가 필요합니다. 예를 들어 Promise와 같은 타입을 사용하기 위해서는 @types/node라는 패키지를 설치해야 합니다. 

 

이제 타입스크립트 관편 패키지를 설치합니다.

 

>npm i -D typescript ts-node @types/node

 

앞서 우린 typescript와 ts-node 패키지를 글로벌로 설치하였습니다. 우리의 개발환경에선 이 두 패키지를 별 문제 없이 사용할 수 있겠지만 만약 다른 유저가 우리의 프로젝트를 체크아웃 받아서 사용할 경우에 문제가 생길 수 있습니다. 따라서 -D 옵션을 주어 설치해 package.json에 패키지를 동록하도록 합니다.

 

이제 타입스크립트 컴파일러 설정파일을 생성합니다. 다음 명령어를 실행해 tsconfig.json파일을 생성합니다.

 

>tsc --init

 

tsconfig.json에는 수많은 옵션이 있지만 그 중에 우리는 다음과 같은 옵션만 우선 사용하도록 합니다.

 

{
  "compilerOptions": {
    /* Basic Options */
    "target": "es5",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist",
    "downlevelIteration": true,

    /* Strict Type-Checking Options */
    "noImplicitAny": false,

    /* Module Resolution Options */
    "moduleResolution": "node",
    "baseUrl": "./",
    "esModuleInterop": true,

    "paths": {"*": ["node_modules/*"]}
  },
  "include": ["src/**/*"]
}

.

위의 설정에서 "include"은 해당 항목 내에 타입스크립트 파일이 존재함을 알려주는 항목입니다. 즉 우리는 ./src폴더 내에 타입스크립트 파일을 위치시켜야 합니다. 

 

다음과 같이 indes.ts파일과 makePerson.ts파일을 작성합니다. 생성할 폴더에 주의하세요.

 

//// ./src/utils/makePerson.ts
export function makePerson(name: string, age: number){
    return {name: name, age: age};
}

export function testMakePerson(){
    console.log(
        makePerson('Jane', 22),
        makePerson('Jack', 23)
    );
}

 

//// ./src/index.ts
import {testMakePerson} from './utils/makePerson';

testMakePerson();

 

다음은 node 실행 명령을 수정 할 차례입니다. 타입스크립트는 ts-node를 사용해 결국 js파일이 생성됩니다. 이 생성된 js파일은 node로 실행 되어야 합니다. 이를 위해 다음과 같이 package.json의 main과 scripts 항목을 수정합니다.

 

{
 ...
  "main": "src/index.js",
  "scripts": {
    "dev": "ts-node src",
    "build": "tsc && node dist"
  },
  ...
}

 

이제 명령어를 실행시켜 봅시다. 

 

> npm run dev

 

 

바로 ts파일이 실행되는 모습을 볼 수 있습니다. 이번에는 build 명령어를 실행 해 js파일을 만들어 봅시다.

 

> npm run build

 

위와 똑같이 ts파일이 실행되며 앞서 tsconfig에 정의한 dist 경로에 js파일이 함께 생성됨을 확인할 수 있습니다.

 

 

 

이제 우린 타입스크립트가 무엇인지 알았으며 개발환경을 준비하였습니다. 본격적으로 개발을 시작해 봅시다.

 

 

 

 

 

반응형

'Programming > Typescript' 카테고리의 다른 글

[Loopback] MongoDB 연결하기.  (0) 2020.11.12
[LoopBack] 02. Loopback4와 Postgresql 연결하기  (0) 2020.04.23
[Typescript] 02. 모듈  (0) 2020.04.22
[LoopBack] 01. 루프백 사용하기  (0) 2020.04.13

 

1. 문제 및 예시

 

Given an array nums of n integers where n > 1,  return an array output such that output[i] is equal to the product of all the elements of nums except nums[i].

 


Example:
Input:  [1,2,3,4]
Output: [24,12,8,6]
Constraint: It's guaranteed that the product of the elements of any prefix or suffix of the array (including the whole array) fits in a 32 bit integer.

 

 

 

2. 풀이

 

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var productExceptSelf = function(nums) {
    let result = [];
    let lp = 1, rp = 1;
    for(let i = 0; i < nums.length; i ++){
        result.push(lp);
        lp *= nums[i];
    }
    for(let i = nums.length -1; i >= 0; i --){
        result[i] *= rp;
        rp *= nums[i];
    }
    return result;
}

** 정방향으로 한번, 역방향으로 한번.

 

 

 

3. 결과

 

18 / 18 test cases passed.
Status: Accepted
Runtime: 80 ms
Memory Usage: 42.2 MB

 

 

 

 

반응형

 

1. 문제 및 예시

 

Given a binary array, find the maximum length of a contiguous subarray with equal number of 0 and 1.

 


Example 1:
Input: [0,1]
Output: 2
Explanation: [0, 1] is the longest contiguous subarray with equal number of 0 and 1.


Example 2:
Input: [0,1,0]
Output: 2
Explanation: [0, 1] (or [1, 0]) is a longest contiguous subarray with equal number of 0 and 1.

 


** The length of the given binary array will not exceed 50,000.

 

 

 

2. 풀이

 

/**
 * @param {number[]} nums
 * @return {number}
 */
var findMaxLength = function(nums) {
    const map = {};
    map[0] = -1;
    let sum = 0, max = 0;
    for (let i = 0; i < nums.length; i++) {
        sum += nums[i] == 1 ? 1 : -1;
        if (sum in map) { max = Math.max(max, i - map[sum]); }
        else { map[sum] = i; }
    }
    return max;
};

** 변수를 둬서 1을 만나면 1, 아니면 -1을 더함.

** 변수값과 동일한 값을 만나면 그 사이의 하위배열은 0과 1의 수를 가져야 합니다.

 

 

 

3. 결과

 

555 / 555 test cases passed.
Status: Accepted
Runtime: 140 ms
Memory Usage: 46.2 MB

 

 

 

 

 

반응형

루프백을 사용하는 방법에 대해 알아봅니다.

 

 

 

 

1. Loopback

 

루프백을 한마디로 설명하자면 다음과 같습니다.

 

LoopBack은 Node.js에서 API 및 마이크로 서비스를 구축하기 위한 플랫폼입니다.

 

또한 공식 문서에서 추가적으로 루프백에 대해 다음과 같이 설명하고 있습니다.

 

LoopBack은 Express를 기반으로하는 확장 성이 뛰어난 오픈 소스 Node.js 및 TypeScript 프레임 워크로, 데이터베이스 및 SOAP 또는 REST 서비스와 같은 백엔드 시스템으로 구성된 API 및 마이크로 서비스를 신속하게 작성할 수 있습니다.

 

아래 다이어그램은 어떻게 LoopBack이 들어오는 요청과 나가는 결과들 사이에 다리 역할을 하고 있는지 보여줍니다. 또한 루프백이 제공하는 다양한 기능들도 보여줍니다.

 

 

이 글을 작성하는 시점에서 최신 버전인 Loopback4를 사용하도록 하겠습니다. Loopback3은 현재 여전히 마이그레이션 중이며 몇몇 기능은 루프백 4에서 동작하지 않을 수 있습니다. 이러한 차이점에 대해선 별도로 다루지 않으며 이 글을 참고해 주시기 바랍니다.

 

 

 

2. LoopBack4 시작하기

 

루프백4는 NodeJs 버전 10 이상을 요구합니다. 미리 설치해 줍시다.

 

NodeJs를 설치하고 난 뒤 아래의 명령어를 통해 LoopBack4 CLI를 설치합니다.

 

> npm i -g @loopback/cli

 

이 LoobBack CLI 도구는 프로젝트를 스캐폴딩 하고 TypeScript 컴파일러를 구성하며 필요한 모든 종속성을 설치합니다. 이제 다음과 같이 명령어를 입력해 프로젝트를 생성합니다.

 

> lb4 app

 

 

옵션을 선택해 프로젝트를 구성합니다. 구성이 완료되면 만들어진 프로젝트 폴더로 이동해 루프백을 실행합니다.

 

> cd loopback

> npm start

 

정상적으로 실행되었는지 확인해보기 위해 http://localhost:3000/ping 로 이동합니다. "greeting":"Hello from LoopBack"이 포함된 json 응답이 보이면 정상적으로 실행된 것입니다.

 

 

 

3. 컨트롤러 추가하기.

 

이제 간단하게 새로운 컨트롤러를 추가해 봅시다. 일단 Ctrl + C를 눌러 실행 중인 루프백을 먼저 종료해 줍니다. 그런 뒤 다음 명령어를 통해 새로운 컨트롤러를 추가합니다.

 

> lb4 controller

 

 

안내에 따라 컨트롤러 이름을 정한 뒤 유형을 선택합니다. 예시에서는 빈 컨트롤러를 선택하였습니다.

 

이제 디렉터리에 우리가 생성한 컨트롤러가 추가된 것을 확인할 수 있습니다. .\src\controllers\hello.controller.ts 파일을 열어보세요.

 

// Uncomment these imports to begin using these cool features!

// import {inject} from '@loopback/context';


export class HelloController {
  constructor() {}
}

 

빈 컨트롤러를 선택했기 때문에 아무런 동작을 하지 않습니다. 이제 여기에 hello에 대한 응답으로 "Hello world!"를 보내주도록 코드를 작성해 봅시다.

 

import { get } from "@loopback/rest";

export class HelloController {
    @get("/hello")
    hello(): string {
        return "Hello world!";
    }
}

 

이제 다시 루프백을 실행 후 http://localhost:3000/hello 로 이동합니다. 

 

 

정상적으로 보이시나요? 이렇게 원하는 컨트롤러를 추가해줄 수 있습니다.

 

 

 

 

 

반응형

 

1. 문제 및 예시

 

We have a collection of stones, each stone has a positive integer weight.

Each turn, we choose the two heaviest stones and smash them together.  Suppose the stones have weights x and y with x <= y.  The result of this smash is:

  • If x == y, both stones are totally destroyed;
  • If x != y, the stone of weight x is totally destroyed, and the stone of weight y has new weight y-x.

At the end, there is at most 1 stone left.  Return the weight of this stone (or 0 if there are no stones left.)

 

 

Example 1:
Input: [2,7,4,1,8,1]
Output: 1
Explanation: 
We combine 7 and 8 to get 1 so the array converts to [2,4,1,1,1] then,
we combine 2 and 4 to get 2 so the array converts to [2,1,1,1] then,
we combine 2 and 1 to get 1 so the array converts to [1,1,1] then,
we combine 1 and 1 to get 0 so the array converts to [1] then that's the value of last stone.

 

 

** 1 <= stones.length <= 30
** 1 <= stones[i] <= 1000

 

 

 

2. 풀이

 

/**
 * @param {number[]} stones
 * @return {number}
 */
var lastStoneWeight = function(stones) {
  while(stones.length > 1){
    let s1 = Math.max(...stones);
    stones.splice(stones.indexOf(s1), 1);
    let s2 = Math.max(...stones);
    stones.splice(stones.indexOf(s2), 1);
    if(s1 != s2) {
        stones.push(s1-s2);
    }
  }
  return (stones.length > 0) ? stones[0] : 0;
};

 

 

 

3. 결과

70 / 70 test cases passed.
Status: Accepted
Runtime: 96 ms
Memory Usage: 33.8 MB

 

 

 

반응형

 

1. 문제 및 예시

 

Given a binary tree, you need to compute the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.

 

 

Example:
Given a binary tree


Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3].

 

** The length of path between two nodes is represented by the number of edges between them.

 

 

 

2. 풀이

 

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var diameterOfBinaryTree = function(root) {
    if(!root) return 0;
    let result = -1;
    let getH = function (node, h) {
        if(!node) return 0;
        let lh = getH(node.left);
        let rh = getH(node.right);
        result= Math.max(result, 1 + lh + rh);
        return 1 + Math.max(lh, rh);
    }
    getH(root);
    return result -1;
};

** 좌측 노드의 최대 깊이와 우측 노드의 최대 깊이를 구해 더한다.

 

 

3. 결과

 


106 / 106 test cases passed.
Status: Accepted
Runtime: 84 ms
Memory Usage: 37.4 MB

 

 

 

반응형

 

1. 문제 및 예시

 

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

  • push(x) -- Push element x onto stack.
  • pop() -- Removes the element on top of the stack.
  • top() -- Get the top element.
  • getMin() -- Retrieve the minimum element in the stack.

 

 

Example:

 

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> Returns -3.
minStack.pop();
minStack.top();      --> Returns 0.
minStack.getMin();   --> Returns -2.

 

 

 

2. 풀이

 

/**
 * initialize your data structure here.
 */
var MinStack = function() {
    this.st = [];
};

/** 
 * @param {number} x
 * @return {void}
 */
MinStack.prototype.push = function(x) {
    this.st.push(x);
};

/**
 * @return {void}
 */
MinStack.prototype.pop = function() {
    return this.st.pop();
};

/**
 * @return {number}
 */
MinStack.prototype.top = function() {
    return this.st[this.st.length-1];
};

/**
 * @return {number}
 */
MinStack.prototype.getMin = function() {
    return this.st.reduce((a, b)=> a > b ? b : a);
};

/** 
 * Your MinStack object will be instantiated and called as such:
 * var obj = new MinStack()
 * obj.push(x)
 * obj.pop()
 * var param_3 = obj.top()
 * var param_4 = obj.getMin()
 */

 

 

 

3. 결과

 


18 / 18 test cases passed.
Status: Accepted
Runtime: 140 ms
Memory Usage: 44.3 MB

 

 

 

반응형

+ Recent posts