SlideShare a Scribd company logo
1 of 74
Download to read offline
lumi.kim(김아름)

2019.11.13

@kakao_fe_meetup
바닥부터시작하는
Vue테스트와리팩토링
🚨주의🚨
테스트방법은다양하고,
오늘제시하는생각들이정답이아닐수있으나
더좋은테스트와테스트하기좋은코드를탐구하는과정공유
오늘말하고싶은것
1. Vue컴포넌트테스트작성방법

2. 테스트를작성하면서했던생각들
3.FE파트안에서관련주제로나눈대화
발표대상
-Vue에대한기본문법을알고있는개발자
- 간단한유닛테스트작성경험이있는개발자
- Vue컴포넌트테스트는어떻게시작하면좋을지궁금한개발자
-그냥듣고싶은개발자
KakaoforBusiness
카카오비즈니스사용자를위한통합비즈니스플랫폼
Kakao for Business
카카오 비즈니스 사용자를 위한
통합 비즈니스 플랫폼
KakaoforBusiness
카카오비즈니스사용자를위한통합비즈니스플랫폼
Nuxt기반의 Vue컴포넌트로
개발된 프로젝트



어느 날부터 혼자 FE운영 시작!
그러던 어느 날….


기존에구현되어있었던덩치가큰몇몇코드들.





기존에구현되어있었던덩치가큰몇몇코드들.



유지보수가계속들어오면서,덩치를키움.

기존구조를유지하며기능추가를하다보니점점수정하는비용이커짐.



기존에구현되어있었던덩치가큰몇몇코드들.



유지보수가계속들어오면서,덩치를키움.

기존구조를유지하며기능추가를하다보니점점수정하는비용이커짐.

나:

더수정되기전에리팩토링으로(=제눈에익숙한코드로)바꾸고싶어요!





기존에구현되어있었던덩치가큰몇몇코드들.



유지보수가계속들어오면서,덩치를키움.

기존구조를유지하며기능추가를하다보니점점수정하는비용이커짐.

나:

더수정되기전에리팩토링으로(=제눈에익숙한코드로)바꾸고싶어요!



P:

QA를거치고운영되는코드는귀한코드.

테스트코드없이리팩토링어떻게보장?(가시적으로,안정성면으로)

테스트코드와함께리팩토링하는게좋을것?


기존에구현되어있었던덩치가큰몇몇코드들.



유지보수가계속들어오면서,덩치를키움.

기존구조를유지하며기능추가를하다보니점점수정하는비용이커짐.

나:

더수정되기전에리팩토링으로(=제눈에익숙한코드로)바꾸고싶어요!



P:

QA를거치고운영되는코드는귀한코드.

테스트코드없이리팩토링어떻게보장?(가시적으로,안정성면으로)

테스트코드와함께리팩토링하는게좋을것?
(+기능추가와리팩토링은같이하지말것)
운영 중인 (귀한) 코드를 리팩토링 할 때
테스트코드가 안정성을 도와주고,
추상적인 진행상황을 가시적으로 측정가능하게 해줌!
테스트를 시작한 이유 1
운영업무 외의 도전과제.

(프로젝트 코드를 대상, 컴포넌트 테스트 학습과 적용)


테스트가 없던 프로젝트에 환경을 마련하고 

추후 추진력있게 테스트를 작성할 수 있는 기반 마련.
테스트를 시작한 이유 2
환경구성
컴포넌트
생성 테스트
컴포넌트
데이터 테스트
컴포넌트 간
테스트
환경구성
│ …
├─pages (component)
│ │ …
│ ├─inspection
│ │ inspectionDetail.vue
│ │ …
├─tests
│ ├─mock
│ │ approvedDetails.js
│ │ rejectedDetails.js
│ │ …
│ ├─specs
│ │ inspectionDetail.spec.vue
│ │ …
│ │ _setup.js
│ │ …
👈 테스트 당할 파일
pages/…/inspectionDetail.vue
│ …
├─pages (component)
│ │ …
│ ├─inspection
│ │ inspectionDetail.vue
│ │ …
├─tests
│ ├─mock
│ │ approvedDetails.js
│ │ rejectedDetails.js
│ │ …
│ ├─specs
│ │ inspectionDetail.spec.vue
│ │ …
│ │ _setup.js
│ │ …
👈 테스트 당할 파일
pages/…/inspectionDetail.vue
👈 테스트코드 작성 파일
tests/…/inspectionDetail.spec.js
│ …
├─pages (component)
│ │ …
│ ├─inspection
│ │ inspectionDetail.vue
│ │ …
├─tests
│ ├─mock
│ │ approvedDetails.js
│ │ rejectedDetails.js
│ │ …
│ ├─specs
│ │ inspectionDetail.spec.vue
│ │ …
│ │ _setup.js
│ │ …
👈 테스트 당할 파일
pages/…/inspectionDetail.vue
👈 테스트코드 작성 파일
tests/…/inspectionDetail.spec.js
👈 mock data (가짜 response data)
테스트작성파일셋팅
test('앱정보가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const option = {
    mocks: {
      $route: {
        query: { appId: null, appName: null },
      },
    }
  }
  // when
  const wrapper = mount(InspectionPage, option)
  // then
  // ...
})
test('검수상태 조회 후 응답데이터가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const option = {
    mocks: {
      $route: {
        query: { appId: FAKE_APP_ID, appName: FAKE_APP_NAME },
      },
    }
  }
  // when
  const wrapper = mount(InspectionPage, option)
  // then
  // ...
})
import test from 'ava'
import sinon from 'sinon'
import { mount, shallowMount } from '@vue/test-utils'
import InspectionPage from '~/pages/.../inspectionDetail.vue'
import InspectionErrorModal from '~/components/.../InspectionErrorModal.vue'
import statesMock from '~/tests/mock/states'
import approvedDetailsMock from '~/tests/mock/approvedDetails'
import rejectedDetailsMock from '~/tests/mock/rejectedDetails'
import {
  // 검수 종류
  INSPECTION_TYPES,
  // 검수 진행상태
  WAITING_INSPECTION,
  OPEN_INSPECTION,
  ...
} from '~/../constants'
test('...', t => {
})
👇 테스트도구 & 테스트할 컴포넌트
import test from 'ava'
import sinon from 'sinon'
import { mount, shallowMount } from '@vue/test-utils'
import InspectionPage from '~/pages/.../inspectionDetail.vue'
import InspectionErrorModal from '~/components/.../InspectionErrorModal.vue'
import statesMock from '~/tests/mock/states'
import approvedDetailsMock from '~/tests/mock/approvedDetails'
import rejectedDetailsMock from '~/tests/mock/rejectedDetails'
import {
  // 검수 종류
  INSPECTION_TYPES,
  // 검수 진행상태
  WAITING_INSPECTION,
  OPEN_INSPECTION,
  ...
} from '~/../constants'
test('...', t => {
})
테스트작성파일셋팅
👆 가짜 Response Data 준비
👈 상수
import test from 'ava'
import sinon from 'sinon'
import { mount, shallowMount } from '@vue/test-utils'
import InspectionPage from '~/pages/.../inspectionDetail.vue'
import InspectionErrorModal from '~/components/.../InspectionErrorModal.vue'
import statesMock from '~/tests/mock/states'
import approvedDetailsMock from '~/tests/mock/approvedDetails'
import rejectedDetailsMock from '~/tests/mock/rejectedDetails'
import {
  // 검수 종류
  INSPECTION_TYPES,
  // 검수 진행상태
  WAITING_INSPECTION,
  OPEN_INSPECTION,
  ...
} from '~/../constants'
test('...', t => {
})
테스트작성파일셋팅
👈 여기부터
테스트 코드 작성
컴포넌트 생성
테스트
서비스검수신청진입페이지.
시나리오
페이지 진입시 (페이지 컴포넌트 생성)
아이디정보가 없으면,
에러 모달을 보여준다.
첫번째테스트작성…
test('앱정보가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const option = {
    mocks: {
      $route: {
        query: { appId: null, appName: null },
      },
    }
  }
  // when
  const wrapper = mount(InspectionPage, option)
  // then
  // ...
})
test('검수상태 조회 후 응답데이터가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const option = {
    mocks: {
      $route: {
        query: { appId: FAKE_APP_ID, appName: FAKE_APP_NAME },
      },
    }
  }
  // when
  const wrapper = mount(InspectionPage, option)
  // then
  // ...
})
test('아이디정보가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const option = {
    mocks: {
      $route: {
        query: { id: null },
      },
    }
  }
  // when
  const wrapper = mount(InspectionPage, option)
  // then
  // ...
})
test('검수상태 조회 후 응답데이터가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const FAKE_ID = 123456
  const option = {
    mocks: {
      $route: {
        query: { id: FAKE_ID },
      },
    }
  }
  // when
  const wrapper = mount(InspectionPage, option)
  // then
  // ...
})
test('아이디정보가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const option = {
    mocks: {
      $route: {
        query: { id: null },
      },
    }
  }
  // when
  const wrapper = mount(InspectionPage, option)
  // then
  // ...
})
test('검수상태 조회 후 응답데이터가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const FAKE_ID = 123456
  const option = {
    mocks: {
      $route: {
        query: { id: FAKE_ID },
      },
    }
  }
  // when
  const wrapper = mount(InspectionPage, option)
  // then
  // ...
})
두번째테스트작성… 💣중복발생💣
function createOption (query) {
  const FAKE_ID = 123456
  const defaultQuery = { id: FAKE_ID }
  
  return {
    mocks: {
      $route: {
        query: query || defaultQuery
      },
    },
  }
}
test('아이디정보가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const option = createOption({ id: null })
  // ...
})
test('검수상태 조회 후 응답데이터가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const option = createOption()
  // ...
})
💣중복제거 💣=>컴포넌트생성조건옵션유틸추가
import InspectionPage from '~/pages/.../inspectionDetail.vue'
test('검수상태 조회 후 응답데이터가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const EMPTY_RESPONSE = null
  const requestInspectionStateStub =
    sinon.stub(InspectionPage.methods, 'requestInspectionState').callsFake(() => EMPTY_RESPONSE)
  // when
  const option = createOption()
  const wrapper = mount(InspectionPage, option)
  // then
  t.true(requestInspectionStateStub.calledOnce)
  t.is(wrapper.vm.showErrorModal, true)
})
test('검수상태 조회결과가 [심사 대기상태] 일 경우, 페이지 진입시 에러모달을 보여준다.', t => {
  // given
  const MOCK_WAITING_STATE = statesMock[WAITING_INSPECTION]
  const requestInspectionStateStub =
    sinon.stub(InspectionPage.methods, 'requestInspectionState').callsFake(() => MOCK_WAITING_STA
TE)
  // when
  const option = createOption()
  const wrapper = mount(InspectionPage, option)
  // then
  t.true(requestInspectionStateStub.calledOnce)
  t.is(wrapper.vm.showErrorModal, true)
})
그다음은,mount되기전로직테스트
import InspectionPage from '~/pages/.../inspectionDetail.vue'
test('검수상태 조회 후 응답데이터가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const EMPTY_RESPONSE = null
  const requestInspectionStateStub =
    sinon.stub(InspectionPage.methods, 'requestInspectionState').callsFake(() => EMPTY_RESPONSE)
  // when
  const option = createOption()
  const wrapper = mount(InspectionPage, option)
  // then
  t.true(requestInspectionStateStub.calledOnce)
  t.is(wrapper.vm.showErrorModal, true)
})
test('검수상태 조회결과가 [심사 대기상태] 일 경우, 페이지 진입시 에러모달을 보여준다.', t => {
  // given
  const MOCK_WAITING_STATE = statesMock[WAITING_INSPECTION]
  const requestInspectionStateStub =
    sinon.stub(InspectionPage.methods, 'requestInspectionState').callsFake(() => MOCK_WAITING_STA
TE)
  // when
  const option = createOption()
  const wrapper = mount(InspectionPage, option)
  // then
  t.true(requestInspectionStateStub.calledOnce)
  t.is(wrapper.vm.showErrorModal, true)
})
그다음은,mount되기전로직테스트=>testdouble활용
import InspectionPage from '~/pages/.../inspectionDetail.vue'
test('검수상태 조회 후 응답데이터가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const EMPTY_RESPONSE = null
  const requestInspectionStateStub =
    sinon.stub(InspectionPage.methods, 'requestInspectionState').callsFake(() => EMPTY_RESPONSE)
  // when
  const option = createOption()
  const wrapper = mount(InspectionPage, option)
  // then
  t.true(requestInspectionStateStub.calledOnce)
  t.is(wrapper.vm.showErrorModal, true)
})
test('검수상태 조회결과가 [심사 대기상태] 일 경우, 페이지 진입시 에러모달을 보여준다.', t => {
  // given
  const MOCK_WAITING_STATE = statesMock[WAITING_INSPECTION]
  const requestInspectionStateStub =
    sinon.stub(InspectionPage.methods, 'requestInspectionState').callsFake(() => MOCK_WAITING_STA
TE)
  // when
  const option = createOption()
  const wrapper = mount(InspectionPage, option)
  // then
  t.true(requestInspectionStateStub.calledOnce)
  t.is(wrapper.vm.showErrorModal, true)
})
mount되기전로직테스트두번째작성
import InspectionPage from '~/pages/.../inspectionDetail.vue'
test('검수상태 조회 후 응답데이터가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const EMPTY_RESPONSE = null
  const requestInspectionStateStub =
    sinon.stub(InspectionPage.methods, 'requestInspectionState').callsFake(() => EMPTY_RESPONSE)
  // when
  const option = createOption()
  const wrapper = mount(InspectionPage, option)
  // then
  t.true(requestInspectionStateStub.calledOnce)
  t.is(wrapper.vm.showErrorModal, true)
})
test('검수상태 조회결과가 [심사 대기상태] 일 경우, 페이지 진입시 에러모달을 보여준다.', t => {
  // given
  const MOCK_WAITING_STATE = statesMock[WAITING_INSPECTION]
  const requestInspectionStateStub =
    sinon.stub(InspectionPage.methods, 'requestInspectionState').callsFake(() => MOCK_WAITING_STA
TE)
  // when
  const option = createOption()
  const wrapper = mount(InspectionPage, option)
  // then
  t.true(requestInspectionStateStub.calledOnce)
  t.is(wrapper.vm.showErrorModal, true)
})
mount되기전로직테스트두번째작성=> 🚨에러발생🚨
앞선테스트에서
(전역)컴포넌트에이미붙여둔테스트더블이겹쳐서…
requestInspectionState
🚨에러해결🚨=>테스트더블복원유틸추가
function restore (...testDoubles) {
  testDoubles.forEach(obj => obj.restore && obj.restore())
}
test('검수상태 조회 후 응답데이터가 없으면, 페이지 진입시 에러 모달을 보여준다.', t => {
  // given
  const EMPTY_RESPONSE = null
  const requestInspectionStateStub =
    sinon.stub(InspectionPage.methods, 'requestInspectionState').callsFake(() => EMPT
Y_RESPONSE)
  // ...
  restore(requestInspectionStateStub)
})
test('검수상태 조회결과가 [심사 대기상태] 일 경우, 페이지 진입시 에러모달을 보여준다.', t => {
  // given
  const MOCK_WAITING_STATE = statesMock[WAITING_INSPECTION]
  const requestInspectionStateStub =
    sinon.stub(InspectionPage.methods, 'requestInspectionState').callsFake(() => MOCK
_WAITING_STATE)
  // ...
  restore(requestInspectionStateStub)
})
Sss
테스트더블을붙이는시기에따라,restore()필요여부가달랐음.
mounted이전의LifeCycleHook으로호출되는로직체크시
전역컴포넌트객체에테스트더블을붙이므로
전역컴포넌트가오염되지않도록
restore()사용이필요했지만,
mounted이후에는
컴포넌트인스턴스(vm)에테스트더블을붙일수있고
각Test의스코프마다인스턴스(vm)를생성하므로
restore호출이필요하지않았음.
테스트더블을붙이는시기에따라,restore()필요여부가달랐음.
mounted이전의LifeCycleHook으로호출되는로직체크시
전역컴포넌트객체에테스트더블을붙이므로
전역컴포넌트가오염되지않도록
restore()사용이필요했지만,
mounted이후에는
컴포넌트인스턴스에테스트더블을붙일수있고
각Test의스코프마다인스턴스를생성하므로
restore()호출이필요하지않았음.
🙄리팩토링+테스트전략 🙄
LifeCycleHook안의코드를
쉽게테스트하려면?
• 추상화된메서드들을호출하는코드만있는게좋지않을까?
• 👉호출시점이프레임워크에서제어되고,
• 👉테스트더블을붙여테스트하기수월
• 컴포넌트곳곳에 hook메서드내에서추상화되지않은코드들이꽤있었는데
• 👉보일때마다리팩토링을해야겠다고생각
컴포넌트 데이터
테스트
{
• props
• data
• computed


데이터를변경시키는…
• (watch)
• (methods)
테스트 해야 할
데이터
종류
{
• <template>표현식결과값
• props
• data
• computed
데이터를변경시키는…
• (watch)
• (methods)
테스트 해야 할
데이터
종류
1)템플릿에표현식이있을때
<!-- 1: 템플릿에 로직이 있음 -->
<div
  ref="headline"
  v-if=“types.includes(INSPECTION_TYPES.FOO)">
  <h4 class="title">타이틀</h4>
  <p class="desc">디스크립션</p>
</div>
<!-- 2: 템플릿에 데이터만 바인딩 -->
<div v-if="hasAType">
  <h4 class="title">타이틀</h4>
  <p class="desc">디스크립션</p>
</div>
<!-- 1: 템플릿에 로직이 있음 -->
<div
  ref="headline"
  v-if=“types.includes(INSPECTION_TYPES.FOO)">
  <h4 class="title">타이틀</h4>
  <p class="desc">디스크립션</p>
</div>
<!-- 2: 템플릿에 데이터만 바인딩 -->
test('선택한 검수타입에 [Foo]가 있으면, Foo 헤드라인을 보여준다.', t => {
  // ...
  // then
  const headline = wrapper.find({ ref: 'headline' })
  const headlineHtml = headline.html()
  t.true(headlineHtml.includes('<h4 class="title">타이틀</h4>'))
})
1)템플릿에표현식이있을때,render결과물을테스트
테스트 할 dom 셀렉터 필요
2)템플릿표현식을제거하면,
<!-- 2: 템플릿에 데이터만 바인딩 -->
<div v-if="hasFooType">
  <h4 class="title">타이틀</h4>
  <p class="desc">디스크립션</p>
</div>
computed 속성 활용
2)템플릿표현식을제거하면,치환된computed결과값을테스트
<!-- 2: 템플릿에 데이터만 바인딩 -->
<div v-if="hasFooType">
  <h4 class="title">타이틀</h4>
  <p class="desc">디스크립션</p>
</div>
test('선택한 검수타입에 [Foo]가 있으면, Foo 헤드라인을 보여준다.', t => {
  // ...
  // then
  t.is(wrapper.vm.hasFooType, true)
})
computed 속성 활용
수다 중에 PO 관련 내용이 나와서 공유를 드려볼께요.



PO (Page Object) 관련 내용입니다.
https://martinfowler.com/bliki/PageObject.html🐨
근래에 자주 이야기하는 mvvm에서 view template 이 로직을 나누어
가지지 말자는 이야기도 이것과 통하는 이야기라서 덧붙이자면.
테스트 코드가 view를 그리는 레벨과
최대한 직접 관계를 맺지 않는 장치에요.
수다 중에 PO 관련 내용이 나와서 공유를 드려볼께요.



PO (Page Object) 관련 내용입니다.
https://martinfowler.com/bliki/PageObject.html
view 의 변화율은 애플리케이션에서 다른 무엇보다 변화율이 높아서.
프레임웍의 refs 를 통해서 view에 접근하듯이 PO를 중간에 두자는 관점이에요
🐨
🐨
🐨
🐨
그래서. 템플릿에 :if="isRight && isNotLeft, ..." 처럼
view 를 관여하는 로직이 포함되어 있다면 어서 제거를...🐨
(찔림)
자세히는 처음 알았어여
감사합니당
그래서. 템플릿에 :if="isRight && isNotLeft, ..." 처럼
view 를 관여하는 로직이 포함되어 있다면 어서 제거를...
바쁘시겠지만 아지트나 위키에 기록해주실수 있나요?
누군가 언젠가 필요할때 찾아볼수 있게요
네. 까먹지 말고 체크해둘께요.
👍👍👍👍👍👍👍👍👍
🐨
🐱
🐸
🐨
🙄리팩토링+테스트전략 🙄
• 리팩토링
• 👉템플릿에서로직(표현식)을분리(computed,methods활용)
• 테스트전략
• 👉DOM에접근해(ex.ref,class,id)렌더링된결과물을확인하기보다는,
• 👉컴포넌트데이터를확인하자.
• 👉컴포넌트데이터의템플릿바인딩여부는snapshot이나e2e에게책임을.
간략하게..snapshot테스팅동작방식
[출처] https://medium.com/@luisvieira_gmr/snapshot-testing-react-components-with-jest-3455d73932a4
간략하게..snapshot테스팅동작방식
test('선택한 검수타입에 [Foo]가 있으면, Foo 헤드라인을 보여준다.', t => {
  // given
  const wrapper = mount(InspectionPage, createOption())
  // ...
  t.snapshot(wrapper.html(), 'Foo가 있을 때 html')
})
│ │ …
├─tests
│ ├─snapshot
│ │ approvedDetail.spec.js.md
│ │ approvedDetail.spec.js.snap
│ │ …
│ ├─specs
│ │ inspectionDetail.spec.vue
│ │ …
# Snapshot report for `tests/…/inspectionDetaill.spec.js`
The actual snapshot is saved in `inspectionDetail.spec.js.snap`.
Generated by [AVA](https://ava.li).
## 선택한 검수타입에 [Foo]가 있으면, Foo 헤드라인을 보여준다.
> Foo가 있을 때 html
    `<div id="article"><div class="title"><h3 class="center">검수 신청하기</
h3><p class=“desc_bizcenter">
…생략…생략
…생략…생략
저장된snapshot
│ │ …
├─tests
│ ├─snapshot
│ │ approvedDetail.spec.js.md
│ │ approvedDetail.spec.js.snap
│ │ …
│ ├─specs
│ │ inspectionDetail.spec.vue
│ │ …
저장된snapshot
│ │ …
├─tests
│ ├─snapshot
│ │ approvedDetail.spec.js.md
│ │ approvedDetail.spec.js.snap
│ │ …
│ ├─specs
│ │ inspectionDetail.spec.vue
│ │ …
{
• <template>표현식결과값
• props
• data
• computed
데이터를변경시키는…
• (watch)
• (method)
const TITLE = '테스트 타이틀'
const DESCRIPTION = '테스트 디스크립션'
// mount시 props 주입
const wrapper = mount(Component, {
  propsData: {
    title: TITLE
  }
})
wrapper.setData({ description: DESCRIPTION })
// props 확인
t.is(wrapper.props('title'), TITLE)
// computed 확인 (data와 동일)
t.is(wrapper.vm.hasTitleAndDescription, true)
뷰인스턴스생성시,주입
{
• <template>표현식결과값
• props
• data
• computed
데이터를변경시키는…
• (watch)
• (method)
뷰인스턴스생성시,주입
뷰인스턴스생성후,조작
const TITLE = '테스트 타이틀'
const DESCRIPTION = '테스트 디스크립션'
// mount시 props 주입
const wrapper = mount(Component, {
  propsData: {
    title: TITLE
  }
})
wrapper.setData({ description: DESCRIPTION })
// props 확인
t.is(wrapper.props('title'), TITLE)
// computed 확인 (data와 동일)
t.is(wrapper.vm.hasTitleAndDescription, true)
{
• <template>표현식결과값
• props
• data
• computed
데이터를변경시키는…
• (watch)
• (methods)
뷰인스턴스생성시,주입
뷰인스턴스생성후,조작
- 데이터변화확인
- 메서드호출,반환값확인
(나는망각의동물…🙈)
컴포넌트 간
테스트
mountvsshallowMount
[출처] https://vuejsdevelopers.com/2019/09/30/stubs-vue-unit-test/
mountvsshallowMount(렌더링결과비교)
<!-- 1. mount의 렌더링 결과값 -->
<div>
  <div class="bg"></div>
  <div class="layer">
    <div class="inner_layer" style="top: 300px;">
      <div class=“head">
<strong class="title">에러모달 타이틀</strong>
</div>
      <div class="body">
        …
</div>
    </div>
  </div>
</div>
<!-- 2. shallowMount의 렌더링 결과값 -->
<inspectionerrormodal-stub></inspectionerrormodal-stub>
특정자식컴포넌트만선택해서테스트하려면?
// 부모가 자식에게
test(‘페이지에서 메시지를 전달하며 에러모달을 열면, 모달컴포넌트에 prop으로 전달된다.', t => {
  // given
  const MODAL_MSG = '테스트용 메세지'
  const wrapper = shallowMount(InspectionPage, createOption())
  // when
  wrapper.vm.showErrorModal(MODAL_MSG)
  const errorModalWrapper = wrapper.find(InspectionErrorModal)
  // then
  t.is(errorModalWrapper.props('msg'), MODAL_MSG)
})
// 자식이 부모에게
test(‘페이지에서 열린 에러모달에서, show이벤트가 오면 값이 페이지에 업데이트 된다.', t => {
  // given
  const MODAL_MSG = '테스트용 메세지'
  const wrapper = shallowMount(InspectionPage, createOption())
  // when
  wrapper.vm.showErrorModal(MODAL_MSG)
  const errorModalWrapper = wrapper.find(InspectionErrorModal)
  errorModalWrapper.vm.$emit('update:show', false)
  // then
  t.is(wrapper.vm.showInspectionErrorModal, false)
})
부모가자식에게props를전달할때
// 부모가 자식에게
test(‘페이지에서 메시지를 전달하며 에러모달을 열면, 모달컴포넌트에 prop으로 전달된다.', t => {
  // given
  const MODAL_MSG = '테스트용 메세지'
  const wrapper = shallowMount(InspectionPage, createOption())
  // when
  wrapper.vm.showErrorModal(MODAL_MSG)
  const errorModalWrapper = wrapper.find(InspectionErrorModal)
  // then
  t.is(errorModalWrapper.props('msg'), MODAL_MSG)
})
// 자식이 부모에게
test(‘페이지에서 열린 에러모달에서, show이벤트가 오면 값이 페이지에 업데이트 된다.', t => {
  // given
  const MODAL_MSG = '테스트용 메세지'
  const wrapper = shallowMount(InspectionPage, createOption())
  // when
  wrapper.vm.showErrorModal(MODAL_MSG)
  const errorModalWrapper = wrapper.find(InspectionErrorModal)
  errorModalWrapper.vm.$emit('update:show', false)
  // then
  t.is(wrapper.vm.showInspectionErrorModal, false)
})
props
ErrorModal
InspectionPage
emit
자식이부모에게emit이벤트를전달할때
// 자식이 부모에게
test(‘페이지에서 열린 에러모달에서, show이벤트가 오면 값이 페이지에 업데이트 된다.', t => {
  // given
  const MODAL_MSG = '테스트용 메세지'
  const wrapper = shallowMount(InspectionPage, createOption())
  // when
  wrapper.vm.showErrorModal(MODAL_MSG)
  const errorModalWrapper = wrapper.find(InspectionErrorModal)
  errorModalWrapper.vm.$emit('update:show', false)
  // then
  t.is(wrapper.vm.showInspectionErrorModal, false)
})
props
ErrorModal
InspectionPage
emit
과정을통해
얻은것과아쉬운점
기술적으로… 💻
1. 테스트역할의이해:리팩토링안정성,가시성
2. 컴포넌트테스트방법과노하우
3. 테스트가쉬운컴포넌트고민->근거있는리팩토링->코드일관성
얻은 것 1
기술적으로… 💻
1. 테스트역할의이해:리팩토링안정성,가시성
2. 컴포넌트테스트방법과노하우
3. 테스트가쉬운컴포넌트고민->근거있는리팩토링->코드일관성
얻은 것 1
기술적으로… 💻
1. 테스트역할의이해:리팩토링안정성,가시성
2. 컴포넌트테스트방법과노하우
3. 테스트가쉬운컴포넌트고민->근거있는리팩토링->코드일관성
얻은 것 1
개인적으로… .
1. 업무가소강(?)상태일때할수있는일
2. 운영외의업무로성장하는느낌
3. 피드백에대한갈증해소
얻은 것 2
개인적으로… .
1. 업무가소강(?)상태일때할수있는일
2. 운영외의업무로성장하는느낌
3. 피드백에대한갈증해소
얻은 것 2
개인적으로… .
1. 업무가소강(?)상태일때할수있는일
2. 운영외의업무로성장하는느낌
3. 피드백에대한갈증해소
얻은 것 2
아쉬웠던 점 (feat. 아주 현실적인) 🙈
- 진행상황에대한업무가시화(목록,일정)를잘하지못함
(무엇을해야하고,어디까지해야하고,얼마나걸릴지감없음)
(감이없어서감으로가시화)
그럼에도 불구하고
다음스텝, 남은고민.. 👽
- 기존코드를망가뜨리지않으면서테스트와리팩토링을잘병행하는방법
- 컴포넌트와협력하는외부객체에대한테스트
- 테스트코드의유지보수경험
- 더복잡한케이스의테스트…
다음스텝, 남은고민.. 👽
- 기존코드를망가뜨리지않으면서테스트와리팩토링을잘병행하는방법
- 컴포넌트와협력하는외부객체에대한테스트
- 테스트코드의유지보수경험
- 더복잡한케이스의테스트…
다음스텝, 남은고민.. 👽
- 기존코드를망가뜨리지않으면서테스트와리팩토링을잘병행하는방법
- 컴포넌트와협력하는외부객체에대한테스트
- 테스트코드의유지보수경험
- 더복잡한케이스의테스트…
감사합니다 😺
lumi.kim@kakaocorp.com
Reference
- vue-test-util 공식가이드: https://vue-test-utils.vuejs.org
- vue-test-util 개발자 블로그: https://eddyerburgh.me
- https://joshua1988.github.io/vue-camp/testing/getting-started.html
- vue testing handbook: https://lmiller1990.github.io/vue-testing-handbook/
- ava: https://github.com/avajs/ava
Q&A

More Related Content

What's hot

TECHTALK 20230131 ビジネスユーザー向け機械学習入門 第1回~機械学習の概要と、ビジネス課題と機械学習問題の定義
TECHTALK 20230131 ビジネスユーザー向け機械学習入門 第1回~機械学習の概要と、ビジネス課題と機械学習問題の定義TECHTALK 20230131 ビジネスユーザー向け機械学習入門 第1回~機械学習の概要と、ビジネス課題と機械学習問題の定義
TECHTALK 20230131 ビジネスユーザー向け機械学習入門 第1回~機械学習の概要と、ビジネス課題と機械学習問題の定義QlikPresalesJapan
 
[NDC18] 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기
[NDC18] 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기[NDC18] 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기
[NDC18] 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기Chanwoong Kim
 
PostgreSQL画像データ収集・格納
PostgreSQL画像データ収集・格納PostgreSQL画像データ収集・格納
PostgreSQL画像データ収集・格納Ayumi Ishii
 
コンテナ導入概要資料2018
コンテナ導入概要資料2018コンテナ導入概要資料2018
コンテナ導入概要資料2018Masahito Zembutsu
 
M19_設計解析業務におけるクラウドエンジニアリングソリューションの活用と効果 [Microsoft Japan Digital Days]
M19_設計解析業務におけるクラウドエンジニアリングソリューションの活用と効果 [Microsoft Japan Digital Days]M19_設計解析業務におけるクラウドエンジニアリングソリューションの活用と効果 [Microsoft Japan Digital Days]
M19_設計解析業務におけるクラウドエンジニアリングソリューションの活用と効果 [Microsoft Japan Digital Days]日本マイクロソフト株式会社
 
Concourseで快適な自動化の旅
Concourseで快適な自動化の旅Concourseで快適な自動化の旅
Concourseで快適な自動化の旅Kazuto Kusama
 
設計品質とアーキテクチャ
設計品質とアーキテクチャ設計品質とアーキテクチャ
設計品質とアーキテクチャToru Koido
 
초심자를 위한 도커 소개 및 입문
초심자를 위한 도커 소개 및 입문초심자를 위한 도커 소개 및 입문
초심자를 위한 도커 소개 및 입문Daniel Seo
 
LOD and Culling Systems That Scale - Unite LA
LOD and Culling Systems That Scale  - Unite LALOD and Culling Systems That Scale  - Unite LA
LOD and Culling Systems That Scale - Unite LAUnity Technologies
 
AWS Black Belt Online Seminar 2017 Amazon Pinpoint で始めるモバイルアプリのグロースハック
AWS Black Belt Online Seminar 2017 Amazon Pinpoint で始めるモバイルアプリのグロースハックAWS Black Belt Online Seminar 2017 Amazon Pinpoint で始めるモバイルアプリのグロースハック
AWS Black Belt Online Seminar 2017 Amazon Pinpoint で始めるモバイルアプリのグロースハックAmazon Web Services Japan
 
Dockerライフサイクルの基礎 地雷を踏み抜けろ!
Dockerライフサイクルの基礎 地雷を踏み抜けろ!Dockerライフサイクルの基礎 地雷を踏み抜けろ!
Dockerライフサイクルの基礎 地雷を踏み抜けろ!Masahito Zembutsu
 
rgpv 7th sem for it & cs Cloud computing lab record
rgpv 7th sem for it & cs Cloud computing lab recordrgpv 7th sem for it & cs Cloud computing lab record
rgpv 7th sem for it & cs Cloud computing lab recordnaaaaz
 
JVMのGCアルゴリズムとチューニング
JVMのGCアルゴリズムとチューニングJVMのGCアルゴリズムとチューニング
JVMのGCアルゴリズムとチューニング佑哉 廣岡
 
AWS Black Belt Online Seminar AWS Direct Connect
AWS Black Belt Online Seminar AWS Direct ConnectAWS Black Belt Online Seminar AWS Direct Connect
AWS Black Belt Online Seminar AWS Direct ConnectAmazon Web Services Japan
 
Cesiumを動かしてみよう
Cesiumを動かしてみようCesiumを動かしてみよう
Cesiumを動かしてみようKazutaka ishizaki
 
凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Public凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Publicbonjin6770 Kurosawa
 
.NET Core 3.0 + Windows 10 で WPF 開発
.NET Core 3.0 + Windows 10 で WPF 開発.NET Core 3.0 + Windows 10 で WPF 開発
.NET Core 3.0 + Windows 10 で WPF 開発一希 大田
 
UI드자이너의 짧은 언리얼 UMG 사용기
UI드자이너의 짧은 언리얼 UMG 사용기UI드자이너의 짧은 언리얼 UMG 사용기
UI드자이너의 짧은 언리얼 UMG 사용기Hong-Gi Joe
 

What's hot (20)

TECHTALK 20230131 ビジネスユーザー向け機械学習入門 第1回~機械学習の概要と、ビジネス課題と機械学習問題の定義
TECHTALK 20230131 ビジネスユーザー向け機械学習入門 第1回~機械学習の概要と、ビジネス課題と機械学習問題の定義TECHTALK 20230131 ビジネスユーザー向け機械学習入門 第1回~機械学習の概要と、ビジネス課題と機械学習問題の定義
TECHTALK 20230131 ビジネスユーザー向け機械学習入門 第1回~機械学習の概要と、ビジネス課題と機械学習問題の定義
 
[NDC18] 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기
[NDC18] 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기[NDC18] 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기
[NDC18] 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기
 
PostgreSQL画像データ収集・格納
PostgreSQL画像データ収集・格納PostgreSQL画像データ収集・格納
PostgreSQL画像データ収集・格納
 
Free bsd jail入門
Free bsd jail入門Free bsd jail入門
Free bsd jail入門
 
コンテナ導入概要資料2018
コンテナ導入概要資料2018コンテナ導入概要資料2018
コンテナ導入概要資料2018
 
M19_設計解析業務におけるクラウドエンジニアリングソリューションの活用と効果 [Microsoft Japan Digital Days]
M19_設計解析業務におけるクラウドエンジニアリングソリューションの活用と効果 [Microsoft Japan Digital Days]M19_設計解析業務におけるクラウドエンジニアリングソリューションの活用と効果 [Microsoft Japan Digital Days]
M19_設計解析業務におけるクラウドエンジニアリングソリューションの活用と効果 [Microsoft Japan Digital Days]
 
Concourseで快適な自動化の旅
Concourseで快適な自動化の旅Concourseで快適な自動化の旅
Concourseで快適な自動化の旅
 
設計品質とアーキテクチャ
設計品質とアーキテクチャ設計品質とアーキテクチャ
設計品質とアーキテクチャ
 
초심자를 위한 도커 소개 및 입문
초심자를 위한 도커 소개 및 입문초심자를 위한 도커 소개 및 입문
초심자를 위한 도커 소개 및 입문
 
LOD and Culling Systems That Scale - Unite LA
LOD and Culling Systems That Scale  - Unite LALOD and Culling Systems That Scale  - Unite LA
LOD and Culling Systems That Scale - Unite LA
 
AWS Black Belt Online Seminar 2017 Amazon Pinpoint で始めるモバイルアプリのグロースハック
AWS Black Belt Online Seminar 2017 Amazon Pinpoint で始めるモバイルアプリのグロースハックAWS Black Belt Online Seminar 2017 Amazon Pinpoint で始めるモバイルアプリのグロースハック
AWS Black Belt Online Seminar 2017 Amazon Pinpoint で始めるモバイルアプリのグロースハック
 
Dockerライフサイクルの基礎 地雷を踏み抜けろ!
Dockerライフサイクルの基礎 地雷を踏み抜けろ!Dockerライフサイクルの基礎 地雷を踏み抜けろ!
Dockerライフサイクルの基礎 地雷を踏み抜けろ!
 
Sum awsloft tko-iotloft-10-lt4-may-2020
Sum awsloft tko-iotloft-10-lt4-may-2020Sum awsloft tko-iotloft-10-lt4-may-2020
Sum awsloft tko-iotloft-10-lt4-may-2020
 
rgpv 7th sem for it & cs Cloud computing lab record
rgpv 7th sem for it & cs Cloud computing lab recordrgpv 7th sem for it & cs Cloud computing lab record
rgpv 7th sem for it & cs Cloud computing lab record
 
JVMのGCアルゴリズムとチューニング
JVMのGCアルゴリズムとチューニングJVMのGCアルゴリズムとチューニング
JVMのGCアルゴリズムとチューニング
 
AWS Black Belt Online Seminar AWS Direct Connect
AWS Black Belt Online Seminar AWS Direct ConnectAWS Black Belt Online Seminar AWS Direct Connect
AWS Black Belt Online Seminar AWS Direct Connect
 
Cesiumを動かしてみよう
Cesiumを動かしてみようCesiumを動かしてみよう
Cesiumを動かしてみよう
 
凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Public凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Public
 
.NET Core 3.0 + Windows 10 で WPF 開発
.NET Core 3.0 + Windows 10 で WPF 開発.NET Core 3.0 + Windows 10 で WPF 開発
.NET Core 3.0 + Windows 10 で WPF 開発
 
UI드자이너의 짧은 언리얼 UMG 사용기
UI드자이너의 짧은 언리얼 UMG 사용기UI드자이너의 짧은 언리얼 UMG 사용기
UI드자이너의 짧은 언리얼 UMG 사용기
 

Similar to 바닥부터 시작하는 Vue 테스트와 리팩토링

위플래닛 발표자료 Meteor_js
위플래닛 발표자료 Meteor_js위플래닛 발표자료 Meteor_js
위플래닛 발표자료 Meteor_jsWebFrameworks
 
[GDG And Kor] 아장아장 테스트 첫걸음
[GDG And Kor] 아장아장 테스트 첫걸음[GDG And Kor] 아장아장 테스트 첫걸음
[GDG And Kor] 아장아장 테스트 첫걸음Wooseop Kim
 
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)SangIn Choung
 
JavaScript로 오픈소스를 해보자. bsJS
JavaScript로 오픈소스를 해보자. bsJSJavaScript로 오픈소스를 해보자. bsJS
JavaScript로 오픈소스를 해보자. bsJSNAVER D2
 
GKAC 2015 Apr. - 테스트 코드에서 코드 커버리지까지
GKAC 2015 Apr. - 테스트 코드에서 코드 커버리지까지GKAC 2015 Apr. - 테스트 코드에서 코드 커버리지까지
GKAC 2015 Apr. - 테스트 코드에서 코드 커버리지까지GDG Korea
 
행복한 개발을 위한_테스트_케이스
행복한 개발을 위한_테스트_케이스행복한 개발을 위한_테스트_케이스
행복한 개발을 위한_테스트_케이스도형 임
 
[H3 2012] 행복한 개발을 위한 테스트 케이스
[H3 2012] 행복한 개발을 위한 테스트 케이스[H3 2012] 행복한 개발을 위한 테스트 케이스
[H3 2012] 행복한 개발을 위한 테스트 케이스KTH, 케이티하이텔
 
발표자료 1인qa로살아남는6가지방법
발표자료 1인qa로살아남는6가지방법발표자료 1인qa로살아남는6가지방법
발표자료 1인qa로살아남는6가지방법SangIn Choung
 
짝 테스트(Pair Testing) 소개와 사례
짝 테스트(Pair Testing) 소개와 사례짝 테스트(Pair Testing) 소개와 사례
짝 테스트(Pair Testing) 소개와 사례SangIn Choung
 
전통적인 개발과 테스트 주도 개발, 그리고 애자일
전통적인 개발과 테스트 주도 개발, 그리고 애자일전통적인 개발과 테스트 주도 개발, 그리고 애자일
전통적인 개발과 테스트 주도 개발, 그리고 애자일Tap ToRestart
 
원모먼트 Vue js 적용기
원모먼트 Vue js 적용기원모먼트 Vue js 적용기
원모먼트 Vue js 적용기우현 김
 
UID DOIT 기말 발표
UID DOIT 기말 발표UID DOIT 기말 발표
UID DOIT 기말 발표한솜 김
 
제로보드과제보고서
제로보드과제보고서제로보드과제보고서
제로보드과제보고서kth919
 
Robot framework 을 이용한 기능 테스트 자동화
Robot framework 을 이용한 기능 테스트 자동화Robot framework 을 이용한 기능 테스트 자동화
Robot framework 을 이용한 기능 테스트 자동화Jaehoon Oh
 
크로스(멀티)브라우저 테스트수행가이드
크로스(멀티)브라우저 테스트수행가이드크로스(멀티)브라우저 테스트수행가이드
크로스(멀티)브라우저 테스트수행가이드SangIn Choung
 
(구로디지털단지역 2분거리)IT실무교육, 프로그래밍, S/W, H/W, 닷넷, 자마린, WPF, ASP.NET, 자바, 스프링, JPA, ...
(구로디지털단지역 2분거리)IT실무교육, 프로그래밍, S/W, H/W, 닷넷, 자마린, WPF, ASP.NET, 자바, 스프링, JPA, ...(구로디지털단지역 2분거리)IT실무교육, 프로그래밍, S/W, H/W, 닷넷, 자마린, WPF, ASP.NET, 자바, 스프링, JPA, ...
(구로디지털단지역 2분거리)IT실무교육, 프로그래밍, S/W, H/W, 닷넷, 자마린, WPF, ASP.NET, 자바, 스프링, JPA, ...탑크리에듀(구로디지털단지역3번출구 2분거리)
 
KGC 2014, 'Software Enginner in Test' in Game Development (Bluehole Studio)
KGC 2014, 'Software Enginner in Test' in Game Development (Bluehole Studio)KGC 2014, 'Software Enginner in Test' in Game Development (Bluehole Studio)
KGC 2014, 'Software Enginner in Test' in Game Development (Bluehole Studio)Sungmin Kim
 
유아이디 두잇 기말 발표
유아이디 두잇 기말 발표유아이디 두잇 기말 발표
유아이디 두잇 기말 발표한솜 김
 
김성훈 - 뛰어난 디버거가 되는 방법
김성훈 - 뛰어난 디버거가 되는 방법김성훈 - 뛰어난 디버거가 되는 방법
김성훈 - 뛰어난 디버거가 되는 방법성훈 김
 

Similar to 바닥부터 시작하는 Vue 테스트와 리팩토링 (20)

위플래닛 발표자료 Meteor_js
위플래닛 발표자료 Meteor_js위플래닛 발표자료 Meteor_js
위플래닛 발표자료 Meteor_js
 
[GDG And Kor] 아장아장 테스트 첫걸음
[GDG And Kor] 아장아장 테스트 첫걸음[GDG And Kor] 아장아장 테스트 첫걸음
[GDG And Kor] 아장아장 테스트 첫걸음
 
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)
 
JavaScript로 오픈소스를 해보자. bsJS
JavaScript로 오픈소스를 해보자. bsJSJavaScript로 오픈소스를 해보자. bsJS
JavaScript로 오픈소스를 해보자. bsJS
 
GKAC 2015 Apr. - 테스트 코드에서 코드 커버리지까지
GKAC 2015 Apr. - 테스트 코드에서 코드 커버리지까지GKAC 2015 Apr. - 테스트 코드에서 코드 커버리지까지
GKAC 2015 Apr. - 테스트 코드에서 코드 커버리지까지
 
행복한 개발을 위한_테스트_케이스
행복한 개발을 위한_테스트_케이스행복한 개발을 위한_테스트_케이스
행복한 개발을 위한_테스트_케이스
 
[H3 2012] 행복한 개발을 위한 테스트 케이스
[H3 2012] 행복한 개발을 위한 테스트 케이스[H3 2012] 행복한 개발을 위한 테스트 케이스
[H3 2012] 행복한 개발을 위한 테스트 케이스
 
발표자료 1인qa로살아남는6가지방법
발표자료 1인qa로살아남는6가지방법발표자료 1인qa로살아남는6가지방법
발표자료 1인qa로살아남는6가지방법
 
짝 테스트(Pair Testing) 소개와 사례
짝 테스트(Pair Testing) 소개와 사례짝 테스트(Pair Testing) 소개와 사례
짝 테스트(Pair Testing) 소개와 사례
 
전통적인 개발과 테스트 주도 개발, 그리고 애자일
전통적인 개발과 테스트 주도 개발, 그리고 애자일전통적인 개발과 테스트 주도 개발, 그리고 애자일
전통적인 개발과 테스트 주도 개발, 그리고 애자일
 
원모먼트 Vue js 적용기
원모먼트 Vue js 적용기원모먼트 Vue js 적용기
원모먼트 Vue js 적용기
 
UID DOIT 기말 발표
UID DOIT 기말 발표UID DOIT 기말 발표
UID DOIT 기말 발표
 
제로보드과제보고서
제로보드과제보고서제로보드과제보고서
제로보드과제보고서
 
애자일의 모든것
애자일의 모든것애자일의 모든것
애자일의 모든것
 
Robot framework 을 이용한 기능 테스트 자동화
Robot framework 을 이용한 기능 테스트 자동화Robot framework 을 이용한 기능 테스트 자동화
Robot framework 을 이용한 기능 테스트 자동화
 
크로스(멀티)브라우저 테스트수행가이드
크로스(멀티)브라우저 테스트수행가이드크로스(멀티)브라우저 테스트수행가이드
크로스(멀티)브라우저 테스트수행가이드
 
(구로디지털단지역 2분거리)IT실무교육, 프로그래밍, S/W, H/W, 닷넷, 자마린, WPF, ASP.NET, 자바, 스프링, JPA, ...
(구로디지털단지역 2분거리)IT실무교육, 프로그래밍, S/W, H/W, 닷넷, 자마린, WPF, ASP.NET, 자바, 스프링, JPA, ...(구로디지털단지역 2분거리)IT실무교육, 프로그래밍, S/W, H/W, 닷넷, 자마린, WPF, ASP.NET, 자바, 스프링, JPA, ...
(구로디지털단지역 2분거리)IT실무교육, 프로그래밍, S/W, H/W, 닷넷, 자마린, WPF, ASP.NET, 자바, 스프링, JPA, ...
 
KGC 2014, 'Software Enginner in Test' in Game Development (Bluehole Studio)
KGC 2014, 'Software Enginner in Test' in Game Development (Bluehole Studio)KGC 2014, 'Software Enginner in Test' in Game Development (Bluehole Studio)
KGC 2014, 'Software Enginner in Test' in Game Development (Bluehole Studio)
 
유아이디 두잇 기말 발표
유아이디 두잇 기말 발표유아이디 두잇 기말 발표
유아이디 두잇 기말 발표
 
김성훈 - 뛰어난 디버거가 되는 방법
김성훈 - 뛰어난 디버거가 되는 방법김성훈 - 뛰어난 디버거가 되는 방법
김성훈 - 뛰어난 디버거가 되는 방법
 

More from if kakao

카카오커머스를 지탱하는 Angular
카카오커머스를 지탱하는 Angular카카오커머스를 지탱하는 Angular
카카오커머스를 지탱하는 Angularif kakao
 
프렌즈타임 웹앱 삽질기
프렌즈타임 웹앱 삽질기프렌즈타임 웹앱 삽질기
프렌즈타임 웹앱 삽질기if kakao
 
카프카 기반의 대규모 모니터링 플랫폼 개발이야기
카프카 기반의 대규모 모니터링 플랫폼 개발이야기카프카 기반의 대규모 모니터링 플랫폼 개발이야기
카프카 기반의 대규모 모니터링 플랫폼 개발이야기if kakao
 
TOROS N2 - lightweight approximate Nearest Neighbor library
TOROS N2 - lightweight approximate Nearest Neighbor libraryTOROS N2 - lightweight approximate Nearest Neighbor library
TOROS N2 - lightweight approximate Nearest Neighbor libraryif kakao
 
딥러닝을 이용한 얼굴 인식
딥러닝을 이용한 얼굴 인식딥러닝을 이용한 얼굴 인식
딥러닝을 이용한 얼굴 인식if kakao
 
딥러닝을 활용한 뉴스 메타 태깅
딥러닝을 활용한 뉴스 메타 태깅딥러닝을 활용한 뉴스 메타 태깅
딥러닝을 활용한 뉴스 메타 태깅if kakao
 
눈으로 듣는 음악 추천 시스템
눈으로 듣는 음악 추천 시스템눈으로 듣는 음악 추천 시스템
눈으로 듣는 음악 추천 시스템if kakao
 
Keynote / 2018
Keynote / 2018Keynote / 2018
Keynote / 2018if kakao
 
카카오 봇 플랫폼 소개
카카오 봇 플랫폼 소개카카오 봇 플랫폼 소개
카카오 봇 플랫폼 소개if kakao
 
다음웹툰의 UX(Animation, Transition, Custom View)
다음웹툰의 UX(Animation, Transition, Custom View)다음웹툰의 UX(Animation, Transition, Custom View)
다음웹툰의 UX(Animation, Transition, Custom View)if kakao
 
모바일 게임플랫폼과 인프라 구축 경험기
모바일 게임플랫폼과 인프라 구축 경험기모바일 게임플랫폼과 인프라 구축 경험기
모바일 게임플랫폼과 인프라 구축 경험기if kakao
 
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개if kakao
 
카카오뱅크 모바일앱 개발 이야기
카카오뱅크 모바일앱 개발 이야기카카오뱅크 모바일앱 개발 이야기
카카오뱅크 모바일앱 개발 이야기if kakao
 
다음 모바일 첫 화면 개선기
다음 모바일 첫 화면 개선기다음 모바일 첫 화면 개선기
다음 모바일 첫 화면 개선기if kakao
 
글로벌 게임 플랫폼에서 무정지, 무점검 서버 개발과 운영 사례
글로벌 게임 플랫폼에서 무정지, 무점검 서버 개발과 운영 사례글로벌 게임 플랫폼에서 무정지, 무점검 서버 개발과 운영 사례
글로벌 게임 플랫폼에서 무정지, 무점검 서버 개발과 운영 사례if kakao
 
액티브X 없는 블록체인 기반 PKI 시스템
액티브X 없는 블록체인 기반 PKI 시스템액티브X 없는 블록체인 기반 PKI 시스템
액티브X 없는 블록체인 기반 PKI 시스템if kakao
 
Klaytn: Service-Oriented Enterprise-Grade Public Blockchain Platform
Klaytn: Service-Oriented Enterprise-Grade Public Blockchain PlatformKlaytn: Service-Oriented Enterprise-Grade Public Blockchain Platform
Klaytn: Service-Oriented Enterprise-Grade Public Blockchain Platformif kakao
 
Kakao Cloud Native Platform, 9rum
Kakao Cloud Native Platform, 9rumKakao Cloud Native Platform, 9rum
Kakao Cloud Native Platform, 9rumif kakao
 
카프카, 산전수전 노하우
카프카, 산전수전 노하우카프카, 산전수전 노하우
카프카, 산전수전 노하우if kakao
 
스프링5 웹플럭스와 테스트 전략
스프링5 웹플럭스와 테스트 전략스프링5 웹플럭스와 테스트 전략
스프링5 웹플럭스와 테스트 전략if kakao
 

More from if kakao (20)

카카오커머스를 지탱하는 Angular
카카오커머스를 지탱하는 Angular카카오커머스를 지탱하는 Angular
카카오커머스를 지탱하는 Angular
 
프렌즈타임 웹앱 삽질기
프렌즈타임 웹앱 삽질기프렌즈타임 웹앱 삽질기
프렌즈타임 웹앱 삽질기
 
카프카 기반의 대규모 모니터링 플랫폼 개발이야기
카프카 기반의 대규모 모니터링 플랫폼 개발이야기카프카 기반의 대규모 모니터링 플랫폼 개발이야기
카프카 기반의 대규모 모니터링 플랫폼 개발이야기
 
TOROS N2 - lightweight approximate Nearest Neighbor library
TOROS N2 - lightweight approximate Nearest Neighbor libraryTOROS N2 - lightweight approximate Nearest Neighbor library
TOROS N2 - lightweight approximate Nearest Neighbor library
 
딥러닝을 이용한 얼굴 인식
딥러닝을 이용한 얼굴 인식딥러닝을 이용한 얼굴 인식
딥러닝을 이용한 얼굴 인식
 
딥러닝을 활용한 뉴스 메타 태깅
딥러닝을 활용한 뉴스 메타 태깅딥러닝을 활용한 뉴스 메타 태깅
딥러닝을 활용한 뉴스 메타 태깅
 
눈으로 듣는 음악 추천 시스템
눈으로 듣는 음악 추천 시스템눈으로 듣는 음악 추천 시스템
눈으로 듣는 음악 추천 시스템
 
Keynote / 2018
Keynote / 2018Keynote / 2018
Keynote / 2018
 
카카오 봇 플랫폼 소개
카카오 봇 플랫폼 소개카카오 봇 플랫폼 소개
카카오 봇 플랫폼 소개
 
다음웹툰의 UX(Animation, Transition, Custom View)
다음웹툰의 UX(Animation, Transition, Custom View)다음웹툰의 UX(Animation, Transition, Custom View)
다음웹툰의 UX(Animation, Transition, Custom View)
 
모바일 게임플랫폼과 인프라 구축 경험기
모바일 게임플랫폼과 인프라 구축 경험기모바일 게임플랫폼과 인프라 구축 경험기
모바일 게임플랫폼과 인프라 구축 경험기
 
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
 
카카오뱅크 모바일앱 개발 이야기
카카오뱅크 모바일앱 개발 이야기카카오뱅크 모바일앱 개발 이야기
카카오뱅크 모바일앱 개발 이야기
 
다음 모바일 첫 화면 개선기
다음 모바일 첫 화면 개선기다음 모바일 첫 화면 개선기
다음 모바일 첫 화면 개선기
 
글로벌 게임 플랫폼에서 무정지, 무점검 서버 개발과 운영 사례
글로벌 게임 플랫폼에서 무정지, 무점검 서버 개발과 운영 사례글로벌 게임 플랫폼에서 무정지, 무점검 서버 개발과 운영 사례
글로벌 게임 플랫폼에서 무정지, 무점검 서버 개발과 운영 사례
 
액티브X 없는 블록체인 기반 PKI 시스템
액티브X 없는 블록체인 기반 PKI 시스템액티브X 없는 블록체인 기반 PKI 시스템
액티브X 없는 블록체인 기반 PKI 시스템
 
Klaytn: Service-Oriented Enterprise-Grade Public Blockchain Platform
Klaytn: Service-Oriented Enterprise-Grade Public Blockchain PlatformKlaytn: Service-Oriented Enterprise-Grade Public Blockchain Platform
Klaytn: Service-Oriented Enterprise-Grade Public Blockchain Platform
 
Kakao Cloud Native Platform, 9rum
Kakao Cloud Native Platform, 9rumKakao Cloud Native Platform, 9rum
Kakao Cloud Native Platform, 9rum
 
카프카, 산전수전 노하우
카프카, 산전수전 노하우카프카, 산전수전 노하우
카프카, 산전수전 노하우
 
스프링5 웹플럭스와 테스트 전략
스프링5 웹플럭스와 테스트 전략스프링5 웹플럭스와 테스트 전략
스프링5 웹플럭스와 테스트 전략
 

바닥부터 시작하는 Vue 테스트와 리팩토링