#
Github์ ์๋ Pull Request๋ฅผ Local๋ก ๊ฐ์ ธ์ค๊ธฐgit fetch origin pull/{ํ ๋ฆฌํ์คํธ ID}/head:{๋ธ๋์น ์ด๋ฆ}git checkout {๋ธ๋์น ์ด๋ฆ}
#
์ ๋ฐ์ดํธ๋ Pull Request ๊ฐ์ ธ์ค๊ธฐgit pull origin pull/{ํ ๋ฆฌํ์คํธ ID}/head
OAuth ๊ธฐ์ ์ ์ฐธ์ฌ์
Our Service ( example.com
), User(์ฌ์ฉ์), Their(Google, Facebook, Twitter ๋ฑ๋ฑ)
User์๊ฒ Their์ id
, pw
๋ฅผ ๊ทธ๋๋ก ์ ๋ฌ๋ฐ๋ ๋ฐฉ๋ฒ
User : ์ฒ์๋ณด๋ ์๋น์ค๋ฅผ ๋ฏฟ์ ์ ์๋ค.
Our Service : ์ ์ ์ id
, pw
๋ฅผ ์์ ํ๊ฒ ๊ด๋ฆฌํด์ผํ ์ฑ
์์ ์ง๋๊ฒ ๋๋ค.
Their Service : ์ 3์์ ์๋น์ค๊ฐ ๋ณธ์ธ๋ค์ id
, pw
๋ฅผ ๊ฐ๊ณ ์๋ค๋ ๊ฒ์ ๋ถ๋ง์ด ์๊ธด๋ค.
Their๊ฐ accessToken
์ ๋ฐ๊ธํ๋ ๋ฐฉ๋ฒ
accessToken
์ id
, pw
๊ฐ ์๋๋ค.
Their Service๋ ์ ๊ณตํ๊ธฐ ์ํ๋ ๊ธฐ๋ฅ๋ง ์ ๊ณตํ ์ ์๋ค.
Client(Our Service)
Resource Owner(User)
Resource Server(Their) + Authorization Server(์ธ์ฆ ์๋ฒ๋ฅผ ๋ฐ๋ก ๊ตฌ๋ถํ๊ธฐ๋ ํจ)
Client ID : ์๋น์ค๋ฅผ ์๋ณํ๋ ์์ด๋
Client Secret : ์๋น์ค์ ๋ํ ๋น๋ฐ๋ฒํธ (์ ๋๋ก ๋ ธ์ถ์ด ๋์ด์ ์๋๋ค.)
Authorized redirect URIs : Resource Server๊ฐ ์ธ์ฆ ์ฝ๋๋ฅผ ์ ๊ณตํ ์๋น์ค์ URL
Scope : Resource Server์์ ์ ๊ณตํ๋ ๊ธฐ๋ฅ (ํ์ํ ๊ธฐ๋ฅ์ ๋ํ ์ธ์ฆ๋ง ์ ๊ณต ๊ฐ๋ฅ )
์์ ๋ก๊ทธ์ธ ๋ฒํผ์ ๋ค์์ ๋งํฌ๊ฐ ๋ด๊ฒจ์๋ค.
http://{๋ฆฌ์์ค ์๋ฒ์ ์ฃผ์}/?{ํด๋ผ์ด์ธํธ ID=id}&{์ค์ฝํ=b,c}&{๋ฆฌ๋ค์ด๋ ํธ URL=url}
๋ฆฌ์์ค ์๋ฒ๋ ์ ์ ์ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ๋ฐ๊ณ , ํด๋ผ์ด์ธํธ ์์ด๋์ ๋ฆฌ๋ค์ด๋ ํธ URL์ด ์ผ์นํ๋์ง ํ์ธํ๊ณ , ๊ถํ ํ์ฉ์ ๋ํ ์๋ด๋ฅผ ์ ์ ์๊ฒ ์ ๋ฌํ๊ณ , ์ ์ ๊ฐ ํ์ฉํ ๊ถํ์ด ๋ฌด์์ธ์ง์ ๋ํด ์ ์ฅํ๋ค.
๋ฆฌ์์ค ์๋ฒ๋ authorization code
๋ฅผ ๋ด์์ ๋ฆฌ๋ค์ด๋ ํธ URL๋ก ์ ์ ์ ๋ธ๋ผ์ฐ์ ๋ฅผ๋ฆฌ๋ค์ด๋ ํธ ์ํจ๋ค.
ํด๋ผ์ด์ธํธ๋ authorization code
๋ฅผ ์ ์ ์๊ฒ ๋ฐ๊ณ , ๋ฆฌ๋ค์ด๋ ํธ URL, ํด๋ผ์ด์ธํธ ID, ํด๋ผ์ด์ธํธ ์ํฌ๋ฆฟ์ ๊ฐ๊ณ ๋ฆฌ์์ค ์๋ฒ์ ์์ฒญํ๋ค.
์ด ๊ณผ์ ์ด ์๋ฃ๋๋ฉด access token์ ๋ฐ๊ธํ๋ค.
access token์ Header: { Authorization : Bearer token }
์ ๋ด์ ๋ณด๋ผ ์ ์๋ค.
Using OAuth 2.0 to Access Google APIs | Google Identity
ํ ์ด๋ธ์ ์ํฐํฐ์ ์ธ์คํด์ค๋ค์ ํํํ๊ธฐ ๋๋ฌธ์ ์๋ฏธ์ ์ผ๋ก ๋ณต์ํ์ด ๋ ๋ง๋ค.
ํ
์ด๋ธ์ ๋จ์ํ์ ์ฌ์ฉํ์ ๋ SQL ๋ฌธ์์ SELECT activity.name
์ผ๋ก ํํํ ์์๋ ์ฅ์ ์ด ์๋ค.
ํ์ง๋ง ๋ณต์ํ์ ์ฌ์ฉํ๋๋ผ๋ SQL alias๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ผ๋ก ๊ทน๋ณต ๊ฐ๋ฅํ๋ค.
SELECT id, name, description FROM products product WHERE product.name = โfooโ AND product.description = โbarโ
REST API์์ ์์์ ๋ํ ์์ฒญ ์ญ์ ํ
์ด๋ธ์ ๋ฐ๋ผ ๋ณต์ํ์ผ๋ก ํ๋ ๊ฒ์ด ์ข๋ค. ex) GET /users/1
The table naming dilemma: singular vs. plural
MySQL ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๋ฐ์ดํฐ ๋๋ ํ ๋ฆฌ ๋ด์ ๋๋ ํ ๋ฆฌ์ ํด๋นํ๊ณ , ํ ์ด๋ธ์ํ์ผ์ ํด๋นํ๋ค. ๋ฐ๋ผ์, ์ด์์ฒด์ ์ ๋์๋ฌธ์ ๊ตฌ๋ถ์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค, ํ ์ด๋ธ ๋ฐ ํธ๋ฆฌ๊ฑฐ ์ด๋ฆ์ ๋์๋ฌธ์ ๊ตฌ๋ถ์ ์ํฅ์ ์ค๋ค.
Windows๋ ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ์ง ์์ง๋ง Unix๋ ๊ตฌ๋ถ, macOS๋ ๊ตฌ๋ถํ์ง ์๋ ํ์ผ์์คํ ์ ์ฌ์ฉํ์ง๋ง ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ๋ ๋ณผ๋ฅจ๋ ์ง์ํจ.
๋ฐ๋ผ์ snake_case
๊ฐ ์ด์์ฑ๊ณผ ์ฌ์ฉ ํธ์์ฑ ์ธก๋ฉด์์ ๊ฐ์ฅ ๊ถ์ฅ๋๋ค.
ํ์ง๋ง camelCase
๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ์๊ณ , JS๊ฐ camelCase
๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์์ป์ ์ ์๋ ํธ์์ฑ์ด ์๋ค.
๋ฌด์๋ณด๋ค ์ผ๊ด๋ ๊ท์น์ ์ฑํํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข๋ค.
MySQL :: MySQL 8.0 Reference Manual :: 9.2.3 Identifier Case Sensitivity
์ค๋ฅ ๋ฐ์์ API ์ฌ์ฉ์์ ํผ๋์ ์์ ๊ธฐ ์ํด ์ค๋ฅ๋ฅผ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ๋ฐ์ํ ์ค๋ฅ์ ์ข ๋ฅ๋ฅผ ๋ํ๋ด๋ HTTP ์๋ต ์ฝ๋๋ฅผ ๋ฐํํด์ผํฉ๋๋ค.ย ์ด๋ฅผ ํตํด API ๊ด๋ฆฌ์๋ ๋ฐ์ํ ๋ฌธ์ ๋ฅผ ์ดํดํ ์์๋ ์ถฉ๋ถํ ์ ๋ณด๋ฅผ ์ป์ ์ ์์ต๋๋ค.ย ์ค๋ฅ๋ก ์ธํด ์์คํ ์ด์ค๋จ๋๋ ๊ฒ์ ์ํ์ง ์์ผ๋ฏ๋ก ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ์ง ์์ ์ฑ๋ก ๋ ์ ์์ต๋๋ค. ์ฆ, API ์๋น์๊ฐ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํด์ผํฉ๋๋ค.
์ผ๋ฐ์ ์ธ ์ค๋ฅ HTTP ์ํ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
400 ์๋ชป๋ ์์ฒญ โ ์ด๋ ํด๋ผ์ด์ธํธ ์ธก ์ ๋ ฅ์ด ์ ํจ์ฑ ๊ฒ์ฌ์ ์คํจ ํจ์ ์๋ฏธํฉ๋๋ค .
401 Unauthorized โ ์ฌ์ฉ์๊ฐ ๋ฆฌ์์ค์ ์ก์ธ์ค ํ ์์๋ ๊ถํ์ด ์์์ ์๋ฏธํฉ๋๋ค .ย ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ์๊ฐ ์ธ์ฆ๋์ง ์์ ๊ฒฝ์ฐ ๋ฐํ๋ฉ๋๋ค.
403 ๊ธ์ง๋จ โ ์ฌ์ฉ์๊ฐ ์ธ์ฆ๋์์ง๋ง ๋ฆฌ์์ค์ ์ก์ธ์ค ํ ์ ์์์ ์๋ฏธํฉ๋๋ค.
404 Not Found โ ๋ฆฌ์์ค๋ฅผ ์ฐพ์ ์ ์์์ ๋ํ๋ ๋๋ค.
500 ๋ด๋ถ ์๋ฒ ์ค๋ฅ โ ์ผ๋ฐ์ ์ธ ์๋ฒ ์ค๋ฅ์ ๋๋ค.ย ์๋ง๋ ๋ช ์ ์ ์ผ๋ก ๋์ ธ์๋ ์๋ฉ๋๋ค.
502ย Bad Gateway โ ์ ์คํธ๋ฆผ ์๋ฒ์ ์๋ชป๋ ์๋ต์ ๋ํ๋ ๋๋ค.
503 ์๋น์ค๋ฅผ ์ฌ์ฉํ ์ ์์ โ ์๋ฒ ์ธก์์ ์๊ธฐ์น ์์ ์ผ์ด ๋ฐ์ํ์์ ๋ํ๋ ๋๋ค (์๋ฒ ๊ณผ๋ถํ, ์์คํ ์ ์ผ๋ถ ์ค๋ฅ ๋ฑ์ด ๋ ์ ์์).
Best practices for REST API design - Stack Overflow Blog
Best Practices for your REST API
XSS ๊ณต๊ฒฉ
https://medium.com/@ryanchenkie_40935/react-authentication-how-to-store-jwt-in-a-cookie-346519310e81
https://bezkoder.com/react-express-authentication-jwt/
https://velog.io/@yaytomato/ํ๋ก ํธ์์-์์ ํ๊ฒ-๋ก๊ทธ์ธ-์ฒ๋ฆฌํ๊ธฐ
https://www.zerocho.com/category/NodeJS/post/5e9bf5b18dcb9c001f36b275
CREATE DATABASE mydb;--- ์์ด๋ ๋ฐ ํจ์ค์๋ ์ค์ CREATE USER 'myuserid'@'%' IDENTIFIED BY 'mypassword';GRANT ALL ON mydb.* TO 'myuserid'@'%';FLUSH PRIVILEGES;
mydb
: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๋ฆ
myuserid
: ์ฌ์ฉ์ id
mypassword
: ์ฌ์ฉ์ ํจ์ค์๋
๋ฃจํธ ์ฌ์ฉ์๋ก ๋ก๊ทธ์ธ ํ ์ผ๋ฐ ์ฌ์ฉ์ ํจ์ค์๋๋ ์ฝ๊ฒ ๋ณ๊ฒฝ ๊ฐ๋ฅ
SET PASSWORD FOR 'myuserid'@'%'='new_password';FLUSH PRIVILEGES;
https://medium.com/@Mareks_082/auto-increment-keys-vs-uuid-a74d81f7476a
https://tomharrisonjr.com/uuid-or-guid-as-primary-keys-be-careful-7b2aa3dcb439
https://www.percona.com/blog/2019/11/22/uuids-are-popular-but-bad-for-performance-lets-discuss/
https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
https://medium.com/aha-official/์ํ-rest-api-์๋ฒ-๊ฐ๋ฐ-6-43568d94878a
https://github.com/goldbergyoni/nodebestpractices#2-error-handling-practices
https://github.com/maximegris/typescript-express-sequelize/blob/master/src/sqlz/config/config.json
https://sequelize.org/v5/manual/migrations.html
https://medium.com/aha-official/์ํ-rest-api-์๋ฒ-๊ฐ๋ฐ-7-712e0588579f
https://stackoverflow.com/a/63299022/12983614
https://medium.com/@changjoopark/express-๋ผ์ฐํธ์์-async-await๋ฅผ-์ฌ์ฉํ๋ ค๋ฉด-7e8ffe0fcc84
์๋น์ค ์ ๊ณต์ : ํ์ ํ๋ณด โ ๋ฐ์ดํฐ ๋ถ์ โ ์๋น์ค ๋ฐฉํฅ ์ค์ , ๋ค์ํ ๋ง์ผํ ํ์ฉ
์ฌ์ฉ์ : ์๋น์ค ์ด์ฉ ํธ์์ฑ, ๊ฐ์ธํ ์๋น์ค ์ด์ฉ
์๋น์ค์ ์ผ์์ด ๋๊ธฐ ์ํ ์ ์ฐจ
์ด ๊ณผ์ ์ ๊ฑฐ์น์ง ๋ชปํ๋ค๋ฉด ๊ฒฝ์ฐ์ ๋ฐ๋ผ์ ํด๋น ์๋น์ค์ ๊ฒ์๋์ด ์๋ ๊ธ ์์ฒด๋ฅผ๋ชป ๋ณด๊ฒ ๋๋ ๋ฑ ๊ฐ์ ์๋ค์ ๋นํด ํ๋์ ์ ์ฝ์ด ๋ฐ์
OAuth 2.0์ ๋ค์ํ ํ๋ซํผ ํ๊ฒฝ์์ ๊ถํ ๋ถ์ฌ๋ฅผ ์ํ ํ์ค ํ๋กํ ์ฝ
์ 3์ ์ฑ์ด ์์์ ์์ ์์ธ ์๋น์ค ์ด์ฉ์๋ฅผ ๋์ ํ์ฌ ์๋น์ค๋ฅผ ์์ฒญํ ์ ์๋๋ก์์ ์ ๊ทผ ๊ถํ์ ์์ํ๋ ๋ฐฉ๋ฒ
ํ์ ์ ๋ณด - key
(์ฌ์ฉ์์ ๋ํ ์ ๋ํฌ ๊ฐ), id
, password
? ์ ์ ์์ด๋๋ฅผ key
๋ก ์ฌ์ฉํ์ ๋์ ๋ฌธ์ ์
id
๋ public ํ๋ค.
id
๋ ๋ณ๊ฒฝ๋ ์ ์๋ค. (์ ์ ์ key
๊ฐ์ด ๋ฐ๋ ์ ์๋ค.)
id
๋ unique
ํด์ผ ํ๋ค.
password
๋ ์ํธํ ๋์ด์ผ ํ๋ค.
key
๊ฐ์ผ๋ก ์ซ์๋ฅผ ์ฌ์ฉํ๋ฉด ๊ณต๊ฒฉ์ ์ฌ์ง๋ฅผ ์ค ์ ์๋ค.
๋ฌธ์์ด์ ๋ง์ด ์ฐ๋๋ฐ, ์ ์ถํ๊ธฐ ์ด๋ ค์์ผ ํ๋ค.
user_id
๋ ์ด๋ฉ์ผ์ ๋ง์ด ์ฐ๋๋ฐ, ์ด๋ฉ์ผ์ ์ ๋ํฌ ํ๋ค๋ ์ ์ด ๋ณด์ฅ๋๋ค.
password
๋ ๋ฌธ์์ ์ซ์, ํน์ ๋ฌธ์ ๋ฑ์ ์กฐํฉํ๋ ๊ฒ์ด ์ข๋ค.
์ฟ ํค๊ฐ ๋ณด์์ ์ทจ์ฝํ ์ด์
๋ง์ ๊ฒฝ์ฐ ์ฟ ํค์ token
์ ๋ฃ๋๋ฐ, ๋ณด์ ์ธก๋ฉด์์ ๊ทธ๋ฅ ๋ฃ๋ ๊ฒฝ์ฐ, ์กฐํฉํด์ ๋ฃ๋๊ฒฝ์ฐ ๋ฑ์ด ์๋ค.
ex) 6์๊ฐ
, refresh token์ ๋ช ์ผ ๋จ์ , ex) 1์ฃผ 2์ฃผ ํ ๋ฌ
์ฌ๋ฐ๊ธ ํ์ง ์๊ณ ๊ธฐ์กด ๊ฒ์ ์ฌ์ฉํ๋ฉด, refresh token์ ์ธ์ ๊ฐ ๋ง๋ฃ ๋์ด ์ฌ์ฉ์์๊ฒ ๋ถํธํจ์ ์ค ์ ์๋ค.
ํญ์ ์ ๊ท๋ก ๋ฐ๊ธํ๋ฉด, ์ฌ๋ฌ ํ๋ซํผ ๊ฐ์ ์ฌ๋ฐ๊ธ ๋ ํ ํฐ์ ์ด๋ป๊ฒ ๋๊ธฐํ ํ ๊ฒ์ด๋์ ๋ํ ๊ณ ๋ฏผ์ด ํ์ํ๋ค.
preview.js
import React from 'react';
import {ThemeProvider} from 'styled-components';
import GlobalStyle from 'src/styles/GlobalStyle';import theme from 'src/styles/theme';
export const parameters = { actions: {argTypesRegex: '^on[A-Z].*'},};
export const decorators = [ (Story) => ( <ThemeProvider theme={theme}> <GlobalStyle /> <Story /> </ThemeProvider> ),];
The Advanced Way to Style with Styled Components
์ด์ - styled-components์ craeteGlobalStyle ์ ์ฉ ์ user agent stylesheet๊ฐ override ํ๋ ์ด์
yarn create react-app my-app-name --template typescript
Create-React-App with TypeScript, ESLint, Prettier, and Github Actions
reportWebVitals
- web-vitals๋ฅผ ํตํ ์ฑ๋ฅ ์ธก์ ์ ์ฌ์ฉ๋จ
yarn remove web-vitals
setupTests.ts
- browser API๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ Test ์คํ ์ global setup์ด ํ์ํ๊ฒฝ์ฐ์ ์ฌ์ฉ
jest์ setupFilesAfterEnv
์ ๊ด๋ จ - https://jestjs.io/docs/en/configuration#setupfilesafterenv-array
https://create-react-app.dev/docs/running-tests/#initializing-test-environment
react-app-env.d.ts
- yarn start ๋ง๋ค ์์ฑ๋จ. ๋ง์ ์ ์ ๊ฐ ์์ฑ์ ๋ฉ์ถฐ๋ฌ๋ผ๊ณ react team์ ์์ฒญํจ
.gitignore
์ /src/react-app-env.d.ts
์ถ๊ฐ
.vscode/settings.json
์ ์ถ๊ฐ
"files.exclude": { "src/*.d.ts": true}
.gitignore
๋ ํ๋ก์ ํธ์์ ์ค์ํ ๊ฒ์ผ๋ก ์์ฉ ํ๋ก๊ทธ๋จ ์์ฒด์์ ์์ฑํ ํ์ผ์ด๋ ๋๋ ํ ๋ฆฌ๋ฅผ ๋์ดํด์ผ ํ๋ค. ๊ฐ์ฅ ์ข์ ์๋ ์บ์ ํ์ผ, ๋ก๊ทธ, ๋ก์ปฌ ๊ตฌ์ฑ ๋ฑ์ด๋ค.
.vscode
, .idea
, .DS_Store
๋ฑ์ ํ๋ก์ ํธ์ .gitignore
๋ณด๋ค ์ปดํจํฐ์ ๊ธ๋ก๋ฒ .gitignore
์ ์ถ๊ฐํ๋ ๊ฒ์ด ์ข๋ค.
๊ธ๋ก๋ฒ .gitignore
๋ home ๋๋ ํ ๋ฆฌ์ .gitconfig
์ ๋ณด๋ฉด ์ ์ ์๋ค.
[core] excludesfile = /Users/{home}/.gitignore_global
Don't put .idea and .vscode directories to project's .gitignore
โโโ src โโโ components โโโ src โโโ pages โโโ App.tsx โโโ index.tsx
npx sb init
lint error ๋ฐ์ (์์)
.eslintignore
**/*.stories.tsx**/stories/*.tsx
.eslintrc.js
rules: { // ...
'import/no-extraneous-dependencies': [ 'error', { devDependencies: [ '**/*.stories.tsx', ], }, ],
// ...}
storybook babel-loader crash
create-react-app babel-loader clash ยท Issue #4764 ยท storybookjs/storybook
https://storybook.js.org/docs/react/get-started/install
unDraw - Open source illustrations for any idea
The Advanced Way to Style with Styled Components
Thoughts around design systems: implementationโ-โReact, Styled-Components, and more
How does the ampersand work in styled-components?
The Sass Ampersand | CSS-Tricks
https://www.typescriptlang.org/docs/handbook/advanced-types.html
๋ฏ์ ํ๊ฒฝ์์ ์๋ก์ด ํ ํ๋ก์ ํธ๋ฅผ ์งํํ๊ฒ ๋์๋ค. ํ๋์ ์ง(์ปดํฌํธ ์กด)์๋ง์์ผ๋ฉด์, ๋๊ธฐ ๋ถ์ฌ๋ ์์ง๋ ฅ ๋ฑ๋ฑ์ด ์ฝํด์ก์๋๋ฐ, ์๋ก์ด ํ๊ฒฝ์์ ๋์ ๊ณผ์ ๋ฅผ๋ถ์ฌ๋ฐ๊ฒ ๋๊ณ , ํจ๊ป ํ์ ํด์ผ ํด์ ๋๊ธฐ ๋ถ์ฌ๊ฐ ๋์๋ค.
์ฐ์ํํ ํฌ์บ ํ๊ฐ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์์ ํ์ ํ ์ ์๋ ์บ ํ์๋ค๋ฉด, ์ด๋ฒ ์บ ํ๋ ๋ค๋ฅธ ๋ถ์ผ ๊ฐ๋ฐ์๋ค๊ณผ ํ์ ํ ์ ์๋ ๊ธฐํ์๋ค. ํนํ ์ด๋ฒ ํ ๋น๋ฉ์ ํตํด์ ์๋ฐ ๋ฐฑ์๋ ํ๊ณผ ํ์ ํ๊ฒ ๋์๋ค.
์บ ํ ์ ์ฒด์ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๊ฐ ๊ฑฐ์ ์๋๊ฒ ์กฐ๊ธ ์ถฉ๊ฒฉ์ด์๊ณ , ์บ ํ์์ ํผ์ ํ๋ก ํธ์๋๋ฅผ ํ๋ ๊ฒ์ด ๋ถ์ํ๊ณ ๋๋ ค์ด ๋ง์์ด ๋ ๋ค.
์ด๋ฒ์ ์งํํ ํ๋ก์ ํธ ์ฃผ์ ์ ๋ํด ๊ฐ์ด ์ ์ค์ง ์๋๋ค. ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํด์ ๋ณด์ฌ์ฃผ๋ ์ฌ์ดํธ ๋ ํผ๋ฐ์ค๋ค์ ์ฐพ์๋ด์ผ๊ฒ ๋ค.
๋งค์ผ์ ๊พธ์คํ ๋ฃจํด
์ง๋ ์บ ํ์์๋ ํ์ผ์ ๋๋ฌด ๋ฌด๋ฆฌํด์ ๊ฐ๋ฐํ๋๋ผ ์ฃผ๋ง์ ํผ๋ก๊ฐ ๋์ ๋์ด ์ฐ๋ฌ์ ธ๋ฒ๋ฆฌ๋ ์ผ์ด ๋ง์๋ค.
ํ์ด์ค๋ฅผ ์ ์กฐ์ ํ์.
๋งค์ผ ๊ฐ์ธ ํ๊ณ ๋ฅผ ์ต๊ดํํ์.
์ด์ ๊ด๋ฆฌ, ํ์คํฌ ๊ด๋ฆฌ
์ค๋ ์ธ์ ์์ "์ ์ ์์ด ๋ฐ์ ์บ ํ์ด๊ธฐ ๋๋ฌธ์, ํ ๋ฌธ์ ์ ๋๋ฌด ๊น์ด ์ฌ๋ก์กํ๋๊ฒ์ด ๋ณ๋ชฉ์ ๋ฐ์์ํฌ ์ ์์ผ๋ฏ๋ก ์ฃผ์ํ๋ผ"๋ ๋ด์ฉ์ ๋ค์๋ค.
ํ์ ์ด ํ์ํ ์ผ์ ๋จผ์ ํด๊ฒฐํ์.
๋ด๊ฐ ํ ์ ์๋ ์ผ์ ๋จผ์ ํด๊ฒฐํ์.
์ด๋ ค์ด ๋ฌธ์ ๋ ์๊ฐ์ ํ๋ณดํ๊ณ ๊ฐ์ธ ์๊ฐ์ ๊น์ด ํ๊ณ ๋ ๋ค.
์๋กญ๊ฒ ์๊ฒ๋ ๊ธฐ์ , ๋๋ ์ ์ ๋ํ ๋ฌธ์ํ
๊ธฐ์ ์ ๋ํ ์ดํด
๋ฉ์ธ ๊ธฐ์ ์คํ์ธ React, TypeScript์ ๋ํ ์ดํด๊ฐ ๋ถ์กฑํ๋ค
์ด๋ฒ ํ๋ก์ ํธ์์ ์ ๋๋ก ๊ณต๋ถํ๊ณ ์ ์ฉํด๋ณด์
ํ ์คํธ ์ฝ๋
ํ ์คํธ ์ฝ๋๋ฅผ 1๋ ์์ฑํ์ง ์์๋ค
์ด๋ฒ ๋ธ๋์ปคํผ ์คํฐ๋์์ ๋ฐฐ์ด ๋ด์ฉ์ ์ ์ฉํด๋ณด์
์์ ๊ฐ ๊ฐ์ง๊ธฐ
์์ ๊ฐ์ ํ์ ๊ณผ ๊ฐ๋ค
๋ถ์ด๋ฃ๊ธฐ๋ ํ๋ค์ง๋ง ๋น ์ง๋๊ฑด ๊ธ๋ฐฉ ๋น ์ง๋ค
HTML์ ๋ฌธ์์ด๋ก ์ง์ ๋น๊ต
diff๊ฐ ์ฉ์ดํ๋๋ก HTML ๊ตฌ์กฐ๋ฅผ ๋ง๋ค์ด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํจ
์ค์ UI ๊ฒฐ๊ณผ๋ฌผ ํ๋ฉด๊ณผ HTML์ด ์ ํํ ์ผ์นํ๋ ๊ฒ์ ์๋๋ค.
HTML์ด ๋ฌ๋ผ๋ ๊ฒฐ๊ณผ๋ฌผ์ด ๋์ผํ ์๋ ์๊ณ , ๊ทธ ๋ฐ๋๋ ๊ฐ๋ฅ
ํ ์คํธ ์ฝ๋๋ง ๋ณด๊ณ ์๋๋ฅผ ํ์ ํ๊ธฐ ์ด๋ ต๋ค
ํ์ฌ์ HTML ๊ตฌ์กฐ๋ฅผ ๊ทธ๋๋ก ํ์ผ๋ก ์ ์ฅ, ๋ณ๊ฒฝ๋ ๋๋ง๋ค ํ ์คํธ ์คํจ
์ค์ ๊ฒฐ๊ณผ๋ ํ์ผ ๋ด๋ถ๋ฅผ ํ์ธํด์ผ ํ๋ค
HTML์ ์ง์ ๋น๊ตํ๋ ๊ฒ๋ณด๋ค ๊ฐํธํ๋ค
ํ๊ท ํ ์คํธ์ ์ญํ
ํ ์คํธ์ ์๋๊ฐ ์๋ค
์ค๋ ์ท ๋ฐ์ดํฐ์ Diff๋ฅผ ๋ด๋ ์๋๋ฅผ ํ์ ํ๊ธฐ ์ด๋ ต๋ค
์ต๊ด์ ์ผ๋ก ์ ๋ฐ์ดํธ ๋๋ฉด์ ํ ์คํธ์ ์ ๋ขฐ์ฑ์ด ๊ฐ์
์๋ํ๊ฐ ์ด๋ ต๋ค
์คํฌ๋ฆฐ์ท์ ์ฐ์ด๋ด๋ ๊ฒ์ด ๊ฐ์ฅ ํ ์คํธ์ ๋ชฉ์ ์ ๋ถํฉ
์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ๋ถ๋ฆฌ๋ ํ๊ฒฝ์์ UI๋ง ๊ฐ๋ฐํ ์ ์๋๋ก ๋์์ค๋ค
์ปดํฌ๋ํธ ๋ฐฉ์์ ๊ฐ๋ฐ์ ์ด์ธ๋ฆฐ๋ค
AI ๊ธฐ์ ํ์ฉ
์คํ ๋ฆฌ๋ถ / Cypress / ์ ๋ ๋์๊ณผ ์ฐ๋ํด์ ์ฌ์ฉ ๊ฐ๋ฅ
Percy, applitools, Chromatic
Introduction | Testing Library
CSS ์ ๋ ํฐ๋ฅผ ์ง์ํ๊ณ ์ฌ์ฉ์๊ฐ ๋ณผ ์ ์๋ ํ ์คํธ ์์ฃผ์ ์ ๋ ํฐ๋ฅผ ์ฌ์ฉ
getByText
, getByLabelText
, getByAltText
, getByTitle
, getByValue
ํ
์คํธ๋ฅผ ์ฌ์ฉํ ์ ์๋ ๊ฒฝ์ฐ์๋ data-test-id
์์ฑ์ ์ฌ์ฉ
getByTestId
์ด๋ฒคํธ ์๋ฎฌ๋ ์ด์
fireEvent
DOM์ด ๋ณ๊ฒฝ๋๊ฑฐ๋ ํน์ ๋จ์ธ(Assertion)์ด ์ฑ๊ณตํ ๋๊น์ง ๊ธฐ๋ค๋ฆด ์ ์๋ ํจ์ - ๋น๋๊ธฐ ๋ก์ง ํ ์คํธ
wait
, waitForElement
, waitForDomChange
DOM๊ณผ ๊ด๋ จ๋ ๋ค์ํ ํ์์ ๋งค์ณ๋ฅผ ์ ๊ณต
toBeDisable
toBeVisible
toHaveTextContent
toHaveStyle
toHaveClass
...