타입스크립트 제가 한번 써봤습니다.

김병진 x Flitto

자바스크립트로 개발한다는 것

userService.insertUser(user, function(res) {
 ...
});
insertUser(user, callback) {
 return $http.post('/api/users', user);
}




front-end code

user-controller.js

user-service.js

back-end code

user-controller.js

user-service.js


router.route('/users')
.post(checkPermission, function(req, res, next) {

  userService.insertUser(req.body.user, function(err, result) {
    return res.send(result);
  });
})




insertUserService(options, callback) {

 userModel.insert(options.users, callback);
}

내 삶이 힘든 이유

테스트 케이스

문서화

welcome to hell

userService.insertUser(user, function(res) {
 debugger
});
insertUser(user, callback) {
 debugger
 return $http.post('/api/users', user);
}





router.route('/users')
.post(checkPermission, function(req, res, next) {
 
  userService.insertUser(req.user, function(err, result) {
    debugger
    return res.send(result);
  });
})




insertUserService(options, callback) {
 debugger
 userModel.insert(options.users, callback);
}

userService.insertUser()

INPUT

??????

OUTPUT 

??????????

문제점

해결책 1

if ( typeof data.username === 'string') .... 
if ( typeof data.username === 'string') .... 
if ( typeof data.username === 'string') .... 
if ( typeof data.username === 'string') .... 
if ( typeof data.username === 'string') .... 
if ( typeof data.username === 'string') .... 
if ( typeof data.username === 'string') .... 
if ( typeof data.username === 'string') .... 
if ( typeof data.username === 'string') .... 
if ( typeof data.username === 'string') .... 
if ( typeof data.username === 'string') .... 

저 어드민 개발 안 할래요

해결 2

개혁

typescript

function greeter(person: string) {
    return "Hello, " + person;
}

let user = [1,2,3];

greeter(user)

index.ts

    
  $ tsc index.ts
error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
    
  $ npm install -g typescript

front-end

INPUT

userService.insertUser()

INPUT

??????

OUTPUT 

??????????

interface

interface User {
 id?: string;
 username: string;
 email: string;
}

    interface combination


interface User {
 id?: string;
 username: string;
 email: string;
}


interface Settings {
 allow_email: string;
 allow_push: string;
 language: string;
}

interface UserWithSettings {
  user: User;
  settings: Settings;
}

    interface extends


interface Account {
 account_name: string;
 rrn: string;
}

interface Settings {
 allow_email: string;
 allow_push: string;
 language: string;
}

interface User extends Settings, Account {
 id?: string;
 username: string;
 email: string;
}
import {User} from '../interfaces/User';
...
userService.insertUser(user: User)
  .subscribe(res => {
    this.user = res;
  });
import {User} from '../interfaces/User';
...
insertUser(user: User) {
 return this.http.post('/api/users', {user});
}




front-end

user-controller.ts

user-service.ts

input type check

output

userService.insertUser()

INPUT

Interface User

OUTPUT 

??????????

output

userService.insertUser(user: User)
  .subscribe(res => {
    this.user = res;
  })
userService.insertUser(user: User): Observable<User> {
 return this.http.post<User>('/api/users', {user});
}




user-controller

user-service

output check

아하!

userService.insertUser()

INPUT

userInterface

OUTPUT 

Observable<userInterface>

node.js

with typescript

back-end

user-controller.ts

user-service.ts

import {User} from '../interfaces/User';
...
router.route('/users')
.post(checkPermission, (req, res, next) => {

  const user: User = {
    username: req.body.user.username,
    email: req.body.user.email
  }
  userService.insertUser(user) {
    return res.send(result);
  });
});


import {User} from '../interfaces/User';
...
insertUserService(user: User): Promise<User> {

 return userModel.insert(options.users);
}

DUPLICATED type 

 

Front-end

 

 

Back-end

 

 

UserInterface

 

 

UserInterface

 

shared type

 

Front-end

 

 

Back-end

 

 

UserInterface

 

interface User {
 id?: string;
 username: string;
 email: string;
}
import {User} from "../../../../shared/interfaces/user";

export class UserService {

  insertUser(user: User) {
  ...
  }
}
import {User} from "../../shared/interfaces/user";

export class UserService {

  insertUser(user: User) {
   ...
  }

}

back-end

front-end

shared type

shared

shared type

 

일관된 형태의 데이터로 프론트엔드와 백엔드 통신

 

more typescript 

decorator


function Logger(target) { 
  console.log(`${target.name} is declared`)
}

@Logger
class Greeter { 
  say() { 
    console.log('hi');
  }
}

  $ tsc index.ts --experimentalDecorators
Greeter is declared

index.ts

ts-express-decorator

framework for Express 

with TypeScript and Decorators

server setting

@ServerSettings({
  rootDir,
  mount: {
    '': `${rootDir}/**/**Controller.js`
  },
  port: config.port,
  acceptMimes: ['application/json'],
  componentsScan: [
    `${rootDir}/**/**Service.js`
  ],
  serveStatic: {
    '/': path.join(rootDir, '../public')
  }
})
export default class Server extends ServerLoader {
 ...
}

cross cutting concern

user-controller.ts


router.route('/users')
.post(checkPermission, (req, res, next) => {

  const user: User = {
    username: req.user.username,
    email: req.user.email
  }

  userService.insertUser(user) {
    return res.send(result);
  });
})


order-controller.ts


router.route('/orders')
.post(checkPermission, (req, res, next) => {

  const order: Order = {
    order_num: req.body.orders.order_num,
    type: req.body.orders.type
  }

  orderService.insertOrder(order) {
    return res.send(result);
  });
})


Before


router.route('/users')
.post(checkPermission, (req, res, next) => {

  const user: User = {
    username: req.body.user.username,
    email: req.body.user.email
  }

  userService.insertUser(user) {
    return res.send(result);
  });
})


@Controller('/users')
export default class UserController {

  @Post('')
  @Authenticated('users')
  async insertUser(
    @Required() @BodyParams('user') user: User
  ) {
    return this.userService(user);  
  }
}

after

cross cutting concern

@Service()
export default class UserService {
  constructor(private sequelize: SequelizeService) {
  }

  async insertUser(user: User): Promise<User> {
    ...
  }
}

dependency injection

user-service.ts


@Controller('/users')
export default class UserController {
  construct(private userService: UserService)
}

user-controller.ts

auto wired

힘들었던 점

아 내가 커미터다

why typescript?

oh my mistake

class UserService() {

    insertUser(user: User) {
        , 
    }
}

  server/userService.ts(9,1): error TS1128: Declaration or statement expected.

less doc & test case

export class AuthService {
  constructor(user: User);

  private _checkToken(): boolean;

  private _getToken(logger: Logger): Promise<Token>;

  signUp(user: User): Promise<User>;
 
  ...
}

auth.service.d.ts

4061 packages

https://www.npmjs.com/~types#packages

more readable & structuring code

@Controller('/users')

export default class UserController implements OnServiceReady {

  @Post('')
  @Authenticated('users')
  async insertUser(
    @Required() @BodyParams('user') user: User
  ) {
    return this.userService(user);  
  }

  $onServiceReady() {
   ...
  }

}
export abstract OnServiceReady {
  $onServiceReady (): void;
}

efficient development

newest specs

  • decorators
  • async, await

.

.

.

 

no typescript

  • 외주로 개발해주고 도망갈 생각이다.

  • 혼자 개발한다.

  • 리팩토링 할 일 없다.

  • 이미 문서화와 테스트 케이스가 너무 잘 되어있다.

  • 나만 쓰는 모듈 개발 중이다.

  • 일정에 쫓기고 있다.

함께 타입스크립트를 전 서비스에 도입할 개발자를 찾고 있습니다. 

 

jobs@flitto.com

end

q & A

사실 저도 잘 몰라요..  

 

bjkim.dev@gmail.com

우리 소통해요

typescript in front-end, back-end

By BJ Kim

typescript in front-end, back-end

  • 2,560