타입스크립트 제가 한번 써봤습니다.
김병진 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,528