๐Ÿš€
Incheol's TECH BLOG
  • Intro
  • Question & Answer
    • JAVA
      • JVM
      • String, StringBuffer, StringBuilder
      • JDK 17์ผ ์‚ฌ์šฉํ•œ ์ด์œ (feat. JDK 8 ์ดํ›„ ํ›‘์–ด๋ณด๊ธฐ)
      • ์Šคํƒ ์˜ค๋ฒ„ ํ”Œ๋กœ์šฐ(SOF)
      • ๋ธ”๋Ÿญํ‚น | ๋…ผ๋ธ”๋Ÿญํ‚น | ๋™๊ธฐ | ๋น„๋™๊ธฐ
      • ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ๋ฅผ ์ด์šฉํ•œ ์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ฆˆ ๊ฐœ์„ 
      • heap dump ๋ถ„์„ํ•˜๊ธฐ (feat. OOM)
      • G1 GC vs Z GC
      • JIT COMPILER
      • ENUM
      • STATIC
      • Thread(์“ฐ๋ ˆ๋“œ)
      • hashCode()์™€ equals()
      • JDK 8 ํŠน์ง•
      • break ์™€ continue ์‚ฌ์šฉ
      • STREAM
      • Optional
      • ๋žŒ๋‹ค์™€ ํด๋กœ์ €
      • Exception(์˜ˆ์™ธ)
      • Garbage Collector
      • Collection
      • Call by Value & Call by Reference
      • ์ œ๋„ค๋ฆญ(Generic)
    • SPRING
      • Spring ํŠน์ง•
      • N+1 ๋ฌธ์ œ
      • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์–ด๋””๊นŒ์ง€ ์•Œ์•„๋ณด๊ณ  ์˜ค์…จ์–ด์š”?
      • แ„แ…ฆแ„‰แ…ณแ„แ…ณ แ„แ…ฉแ„ƒแ…ณ แ„‰แ…ฅแ†ผแ„‚แ…ณแ†ผ แ„€แ…ขแ„‰แ…ฅแ†ซแ„€แ…ต
      • RestTemplate ์‚ฌ์šฉ์‹œ ์ฃผ์˜์‚ฌํ•ญ
      • ๋™์‹œ์„ฑ ํ•ด๊ฒฐํ•˜๊ธฐ(feat. TMI ์ฃผ์˜)
      • redisson trylock ๋‚ด๋ถ€๋กœ์ง ์‚ดํŽด๋ณด๊ธฐ
      • DB ํŠธ๋ž˜ํ”ฝ ๋ถ„์‚ฐ์‹œํ‚ค๊ธฐ(feat. Routing Datasource)
      • OSIV
      • @Valid ๋™์ž‘ ์›๋ฆฌ
      • mybatis @Builder ์ฃผ์˜์‚ฌํ•ญ
      • ์Šคํ”„๋ง ํด๋ผ์šฐ๋“œ ์ปจํ”ผ๊ทธ ๊ฐฑ์‹  ๋˜์ง€ ์•Š๋Š” ์ด์Šˆ(feat. ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ)
      • ImageIO.read ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ
      • ์นดํ”„์นด transaction ์ฒ˜๋ฆฌ๋Š” ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ?
      • Spring Boot ํŠน์ง•
      • Spring 5 ํŠน์ง•
      • JPA vs MyBatis
      • Filter์™€ Interceptor
      • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ(Persistence Context)
      • @Transactional
      • @Controlleradvice, @ExceptionHandler
      • Spring Security
      • Dispatcher Servlet
      • @EnableWebMvc
      • Stereo Type(์Šคํ…Œ๋ ˆ์˜ค ํƒ€์ž…)
      • AOP
      • JPA Repository ๊ทœ์น™
    • DATABASE
      • Database Index
      • SQL vs NoSQL
      • DB ๊ต์ฐฉ์ƒํƒœ
      • Isolation level
      • [MySQL] ์ด๋ชจ์ง€ ์ €์žฅ์€ ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ์ข‹์„๊นŒ?
      • SQL Hint
      • JOIN
    • INFRA
      • CLOUD COMPUTING
      • GIT
      • DOCKER
      • ์นดํ”„์นด ์ฐ๋จนํ•˜๊ธฐ 1๋ถ€
      • ์นดํ”„์นด ์ฐ๋จนํ•˜๊ธฐ 2๋ถ€ (feat. ํ”„๋กœ๋“€์„œ)
      • ์นดํ”„์นด ์ฐ๋จนํ•˜๊ธฐ 3๋ถ€ (feat. ์ปจ์Šˆ๋จธ)
      • JENKINS
      • POSTMAN
      • DNS ๋™์ž‘ ์›๋ฆฌ
      • ALB, NLB,ELB ์ฐจ์ด๋Š”?
      • ์นดํ”„์นด ํŒŒํ‹ฐ์…˜ ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•˜์ž
      • DEVOPS
      • JWT
      • OSI 7 Layer
      • MSA
      • ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ๋Š” ์–ด๋–ป๊ฒŒ ์„œ๋น„์Šค ๋“ฑ๋ก/ํ•ด์ œ ํ•˜๋Š”๊ฑธ๊นŒ?
      • แ„‘แ…ตแ†ซแ„‘แ…ฉแ„‹แ…ตแ†ซแ„แ…ณ แ„‰แ…กแ„‹แ…ญแ†ผแ„‰แ…ต แ„Œแ…ฎแ„‹แ…ดแ„‰แ…กแ„’แ…กแ†ผ!! (feat แ„…แ…ฉแ„€แ…ณ แ„‘แ…กแ„‹แ…ตแ†ฏ ์‚ฌ์ด์ฆˆ)
      • AWS EC2 แ„ƒแ…ฉแ„†แ…ฆแ„‹แ…ตแ†ซ แ„‰แ…ฅแ†ฏแ„Œแ…ฅแ†ผ (with ALB)
      • ALBแ„‹แ…ฆ SSL แ„‰แ…ฅแ†ฏแ„Œแ…ฅแ†ผแ„’แ…กแ„€แ…ต(feat. ACM)
      • แ„…แ…กแ†ทแ„ƒแ…กแ„…แ…ณแ†ฏ แ„’แ…ชแ†ฏแ„‹แ…ญแ†ผแ„’แ…กแ†ซ แ„แ…ณแ†ฏแ„…แ…กแ„‹แ…ฎแ„ƒแ…ณ แ„‹แ…ชแ„Žแ…ต แ„‹แ…กแ†ฏแ„…แ…ตแ†ท แ„‡แ…กแ†ฎแ„€แ…ต
      • AWS Personalize ์ ์šฉ ํ›„๊ธฐโ€ฆ ๐Ÿ˜ฐ
      • CloudFront๋ฅผ ํ™œ์šฉํ•œ S3 ์„ฑ๋Šฅ ๋ฐ ๋น„์šฉ ๊ฐœ์„ 
    • ARCHITECTURE
      • ๊ฐ์ฒด์ง€ํ–ฅ๊ณผ ์ ˆ์ฐจ์ง€ํ–ฅ
      • ์ƒ์†๋ณด๋‹จ ํ•ฉ์„ฑ
      • SOLID ์›์น™
      • ์บก์Аํ™”
      • DDD(Domain Driven Design)
    • COMPUTER SCIENCE
      • ๋ฎคํ…์Šค์™€ ์„ธ๋งˆํฌ์–ด
      • Context Switch
      • REST API
      • HTTP HEADER
      • HTTP METHOD
      • HTTP STATUS
    • CULTURE
      • AGILE(Feat. ์Šคํฌ๋Ÿผ)
      • ์šฐ๋ฆฌ๋Š” ์„ฑ์žฅ ํ• ์ˆ˜ ์žˆ์„๊นŒ? (w. ํ•จ๊ป˜ ์ž๋ผ๊ธฐ)
      • Expert Beginner
    • SEMINAR
      • 2022 INFCON ํ›„๊ธฐ
        • [104ํ˜ธ] ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ ๋งŒ์„ธ! - ๊ธฐ์ˆ ๋งŒํผ ์ค‘์š”ํ–ˆ๋˜ ์ œํ’ˆ๊ณผ ํŒ€ ์„ฑ์žฅ๊ธฐ
        • [102ํ˜ธ] ํŒ€์„ ๋„˜์–ด์„œ ์ „์‚ฌ์  ํ˜‘์—… ํ™˜๊ฒฝ ๊ตฌ์ถ•ํ•˜๊ธฐ
        • [103ํ˜ธ] ์ฝ”๋“œ ๋ฆฌ๋ทฐ์˜ ๋˜ ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ๋ฒ•: Pull Requests vs. Stacked Changes
        • [105ํ˜ธ]ย ์‹ค์ „! ๋ฉ€ํ‹ฐ ๋ชจ๋“ˆ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ์™€ ์„ค๊ณ„
        • [105ํ˜ธ] ์ง€๊ธˆ ๋‹น์žฅ DevOps๋ฅผ ํ•ด์•ผ ํ•˜๋Š” ์ด์œ 
        • [102ํ˜ธ] (๋ ˆ๊ฑฐ์‹œ ์‹œ์Šคํ…œ) ๊ฐœํŽธ์˜ ๊ธฐ์ˆ  - ๋ฐฐ๋‹ฌ ํ”Œ๋žซํผ์—์„œ ๊ฒช์€ N๋ฒˆ์˜ ๊ฐœํŽธ ๊ฒฝํ—˜๊ธฐ
        • [102ํ˜ธ] ์„œ๋ฒ„๋น„ 0์›, ํด๋ผ์šฐ๋“œ ํ ๋„์ž…์œผ๋กœ ํ•ด๋ƒˆ์Šต๋‹ˆ๋‹ค!
  • STUDY
    • ์˜ค๋ธŒ์ ํŠธ
      • 1์žฅ ๊ฐ์ฒด, ์„ค๊ณ„
      • 2์žฅ ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ
      • 3์žฅ ์—ญํ• , ์ฑ…์ž„, ํ˜‘๋ ฅ
      • 4์žฅ ์„ค๊ณ„ ํ’ˆ์งˆ๊ณผ ํŠธ๋ ˆ์ด๋“œ ์˜คํ”„
      • 5์žฅ ์ฑ…์ž„ ํ• ๋‹นํ•˜๊ธฐ
      • 6์žฅ ๋ฉ”์‹œ์ง€์™€ ์ธํ„ฐํŽ˜์ด์Šค
      • 7์ง• ๊ฐ์ฒด ๋ถ„ํ•ด
      • 8์žฅ ์˜์กด์„ฑ ๊ด€๋ฆฌํ•˜๊ธฐ
      • 9์žฅ ์œ ์—ฐํ•œ ์„ค๊ณ„
      • 10์žฅ ์ƒ์†๊ณผ ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ
      • 11์žฅ ํ•ฉ์„ฑ๊ณผ ์œ ์—ฐํ•œ ์„ค๊ณ„
      • 12์žฅ ๋‹คํ˜•์„ฑ
      • 13์žฅ ์„œ๋ธŒํด๋ž˜์‹ฑ๊ณผ ์„œ๋ธŒํƒ€์ดํ•‘
      • 14์žฅ ์ผ๊ด€์„ฑ ์žˆ๋Š” ํ˜‘๋ ฅ
      • 15์žฅ ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ”„๋ ˆ์ž„์›Œํฌ
      • ๋งˆ๋ฌด๋ฆฌ
    • ๊ฐ์ฒด์ง€ํ–ฅ์˜ ์‚ฌ์‹ค๊ณผ ์˜คํ•ด
      • 1์žฅ ํ˜‘๋ ฅํ•˜๋Š” ๊ฐ์ฒด๋“ค์˜ ๊ณต๋™์ฒด
      • 2์žฅ ์ด์ƒํ•œ ๋‚˜๋ผ์˜ ๊ฐ์ฒด
      • 3์žฅ ํƒ€์ž…๊ณผ ์ถ”์ƒํ™”
      • 4์žฅ ์—ญํ• , ์ฑ…์ž„, ํ˜‘๋ ฅ
    • JAVA ORM JPA
      • 1์žฅ JPA ์†Œ๊ฐœ
      • 2์žฅ JPA ์‹œ์ž‘
      • 3์žฅ ์˜์†์„ฑ ๊ด€๋ฆฌ
      • 4์žฅ ์—”ํ‹ฐํ‹ฐ ๋งคํ•‘
      • 5์žฅ ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘ ๊ธฐ์ดˆ
      • 6์žฅ ๋‹ค์–‘ํ•œ ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘
      • 7์žฅ ๊ณ ๊ธ‰ ๋งคํ•‘
      • 8์žฅ ํ”„๋ก์‹œ์™€ ์—ฐ๊ด€๊ด€๊ณ„ ๊ด€๋ฆฌ
      • 9์žฅ ๊ฐ’ ํƒ€์ž…
      • 10์žฅ ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ ์–ธ์–ด
      • 11์žฅ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ œ์ž‘
      • 12์žฅ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA
      • 13์žฅ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์˜์†์„ฑ ๊ด€๋ฆฌ
      • 14์žฅ ์ปฌ๋ ‰์…˜๊ณผ ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ
      • 15์žฅ ๊ณ ๊ธ‰ ์ฃผ์ œ์™€ ์„ฑ๋Šฅ ์ตœ์ ํ™”
      • 16์žฅ ํŠธ๋žœ์žญ์…˜๊ณผ ๋ฝ, 2์ฐจ ์บ์‹œ
    • ํ† ๋น„์˜ ์Šคํ”„๋ง (3.1)
      • ์Šคํ”„๋ง์˜ ์ดํ•ด์™€ ์›๋ฆฌ
        • 1์žฅ ์˜ค๋ธŒ์ ํŠธ์™€ ์˜์กด๊ด€๊ณ„
        • 2์žฅ ํ…Œ์ŠคํŠธ
        • 3์žฅ ํ…œํ”Œ๋ฆฟ
        • 4์žฅ ์˜ˆ์™ธ
        • 5์žฅ ์„œ๋น„์Šค ์ถ”์ƒํ™”
        • 6์žฅ AOP
        • 8์žฅ ์Šคํ”„๋ง์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?
      • ์Šคํ”„๋ง์˜ ๊ธฐ์ˆ ๊ณผ ์„ ํƒ
        • 5์žฅ AOP์™€ LTW
        • 6์žฅ ํ…Œ์ŠคํŠธ ์ปจํ…์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ
    • ํด๋ฆฐ์ฝ”๋“œ
      • 1์žฅ ๊นจ๋—ํ•œ ์ฝ”๋“œ
      • 2์žฅ ์˜๋ฏธ ์žˆ๋Š” ์ด๋ฆ„
      • 3์žฅ ํ•จ์ˆ˜
      • 4์žฅ ์ฃผ์„
      • 5์žฅ ํ˜•์‹ ๋งž์ถ”๊ธฐ
      • 6์žฅ ๊ฐ์ฒด์™€ ์ž๋ฃŒ ๊ตฌ์กฐ
      • 9์žฅ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
    • ์ž๋ฐ” ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…(with scouter)
      • CHAP 01. ์ž๋ฐ” ๊ธฐ๋ฐ˜์˜ ์‹œ์Šคํ…œ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋“ค
      • CHAP 02. scouter ์‚ดํŽด๋ณด๊ธฐ
      • CHAP 03. scouter ์„ค์ •ํ•˜๊ธฐ(์„œ๋ฒ„ ๋ฐ ์—์ด์ „ํŠธ)
      • CHAP 04. scouter ํด๋ผ์ด์–ธํŠธ์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ๋“ค
      • CHAP 05. scouter XLog
      • CHAP 06. scouter ์„œ๋ฒ„/์—์ด์ „ํŠธ ํ”Œ๋Ÿฌ๊ทธ์ธ
      • CHAP 07. scouter ์‚ฌ์šฉ ์‹œ ์œ ์šฉํ•œ ํŒ
      • CHAP 08. ์Šค๋ ˆ๋“œ ๋•Œ๋ฌธ์—(์Šค๋ ˆ๋“œ์—์„œ) ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค
      • CHAP 09. ์Šค๋ ˆ๋“œ ๋‹จ๋ฉด ์ž˜๋ผ ๋†“๊ธฐ
      • CHAP 10. ์ž˜๋ผ ๋†“์€ ์Šค๋ ˆ๋“œ ๋‹จ๋ฉด ๋ถ„์„ํ•˜๊ธฐ
      • CHAP 11. ์Šค๋ ˆ๋“œ ๋ฌธ์ œ
      • CHAP 12. ๋ฉ”๋ชจ๋ฆฌ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋“ค
      • CHAP 13. ๋ฉ”๋ชจ๋ฆฌ ๋‹จ๋ฉด ์ž˜๋ผ ๋†“๊ธฐ
      • CHAP 14. ์ž˜๋ผ ๋†“์€ ๋ฉ”๋ชจ๋ฆฌ ๋‹จ๋ฉด ๋ถ„์„ํ•˜๊ธฐ
      • CHAP 15. ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ(Case Study)
      • CHAP 24. scouter๋กœ ๋ฆฌ์†Œ์Šค ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ธฐ
      • CHAP 25. ์žฅ์•  ์ง„๋‹จ์€ ์ด๋ ‡๊ฒŒ ํ•œ๋‹ค
      • ๋ถ€๋ก A. Fatal error log ๋ถ„์„
      • ๋ถ€๋ก B. ์ž๋ฐ” ์ธ์ŠคํŠธ๋Ÿญ์…˜
    • ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ ์‹œ์ž‘ํ•˜๊ธฐ
      • CHAP 02. TDD ์‹œ์ž‘
      • CHAP 03. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ ์ˆœ์„œ
      • CHAP 04. TDD/๊ธฐ๋Šฅ ๋ช…์„ธ/์„ค๊ณ„
      • CHAP 05. JUnit 5 ๊ธฐ์ดˆ
      • CHAP 06. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ๊ตฌ์„ฑ
      • CHAP 07. ๋Œ€์—ญ
      • CHAP 08. ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•œ ์„ค๊ณ„
      • CHAP 09. ํ…Œ์ŠคํŠธ ๋ฒ”์œ„์™€ ์ข…๋ฅ˜
      • CHAP 10. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์™€ ์œ ์ง€๋ณด์ˆ˜
      • ๋ถ€๋ก A. Junit 5 ์ถ”๊ฐ€ ๋‚ด์šฉ
      • ๋ถ€๋ก C. Mockito ๊ธฐ์ดˆ ์‚ฌ์šฉ๋ฒ•
      • ๋ถ€๋ก D. AssertJ ์†Œ๊ฐœ
    • KOTLIN IN ACTION
      • 1์žฅ ์ฝ”ํ‹€๋ฆฐ์ด๋ž€ ๋ฌด์—‡์ด๋ฉฐ, ์™œ ํ•„์š”ํ•œ๊ฐ€?
      • 2์žฅ ์ฝ”ํ‹€๋ฆฐ ๊ธฐ์ดˆ
      • 3์žฅ ํ•จ์ˆ˜ ์ •์˜์™€ ํ˜ธ์ถœ
      • 4์žฅ ํด๋ž˜์Šค, ๊ฐ์ฒด, ์ธํ„ฐํŽ˜์ด์Šค
      • 5์žฅ ๋žŒ๋‹ค๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐ
      • 6์žฅ ์ฝ”ํ‹€๋ฆฐ ํƒ€์ž… ์‹œ์Šคํ…œ
      • 7์žฅ ์—ฐ์‚ฐ์ž ์˜ค๋ฒ„๋กœ๋”ฉ๊ณผ ๊ธฐํƒ€ ๊ด€๋ก€
      • 8์žฅ ๊ณ ์ฐจ ํ•จ์ˆ˜: ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋ฐ˜ํ™˜ ๊ฐ’์œผ๋กœ ๋žŒ๋‹ค ์‚ฌ์šฉ
      • 9์žฅ ์ œ๋„ค๋ฆญ์Šค
      • 10์žฅ ์• ๋…ธํ…Œ์ด์…˜๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜
      • ๋ถ€๋ก A. ์ฝ”ํ‹€๋ฆฐ ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ
      • ๋ถ€๋ก B. ์ฝ”ํ‹€๋ฆฐ ์ฝ”๋“œ ๋ฌธ์„œํ™”
      • ๋ถ€๋ก D. ์ฝ”ํ‹€๋ฆฐ 1.1๊ณผ 1.2, 1.3 ์†Œ๊ฐœ
    • KOTLIN ๊ณต์‹ ๋ ˆํผ๋Ÿฐ์Šค
      • BASIC
      • Classes and Objects
        • Classes and Inheritance
        • Properties and Fields
    • ์ฝ”ํ‹€๋ฆฐ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ
      • 1์žฅ Hello, Concurrent World!
      • 2์žฅ ์ฝ”๋ฃจํ‹ด ์ธ ์•ก์…˜
      • 3์žฅ ๋ผ์ดํ”„ ์‚ฌ์ดํด๊ณผ ์—๋Ÿฌ ํ•ธ๋“ค๋ง
      • 4์žฅ ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜์™€ ์ฝ”๋ฃจํ‹ด ์ปจํ…์ŠคํŠธ
      • 5์žฅ ์ดํ„ฐ๋ ˆ์ดํ„ฐ, ์‹œํ€€์Šค ๊ทธ๋ฆฌ๊ณ  ํ”„๋กœ๋“€์„œ
      • 7์žฅ ์Šค๋ ˆ๋“œ ํ•œ์ •, ์•กํ„ฐ ๊ทธ๋ฆฌ๊ณ  ๋ฎคํ…์Šค
    • EFFECTIVE JAVA 3/e
      • ๊ฐ์ฒด ์ƒ์„ฑ๊ณผ ํŒŒ๊ดด
        • ์•„์ดํ…œ1 ์ƒ์„ฑ์ž ๋Œ€์‹  ์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ๊ณ ๋ คํ•˜๋ผ
        • ์•„์ดํ…œ2 ์ƒ์„ฑ์ž์— ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋งŽ๋‹ค๋ฉด ๋นŒ๋”๋ฅผ ๊ณ ๋ คํ•˜๋ผ
        • ์•„์ดํ…œ3 private ์ƒ์„ฑ์ž๋‚˜ ์—ด๊ฑฐ ํƒ€์ž…์œผ๋กœ ์‹ฑ๊ธ€ํ„ด์ž„์„ ๋ณด์ฆํ•˜๋ผ
        • ์•„์ดํ…œ4 ์ธ์Šคํ„ด์Šคํ™”๋ฅผ ๋ง‰์œผ๋ ค๊ฑฐ๋“  private ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ5 ์ž์›์„ ์ง์ ‘ ๋ช…์‹œํ•˜์ง€ ๋ง๊ณ  ์˜์กด ๊ฐ์ฒด ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ6 ๋ถˆํ•„์š”ํ•œ ๊ฐ์ฒด ์ƒ์„ฑ์„ ํ”ผํ•˜๋ผ
        • ์•„์ดํ…œ7 ๋‹ค ์“ด ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ํ•ด์ œํ•˜๋ผ
        • ์•„์ดํ…œ8 finalizer์™€ cleaner ์‚ฌ์šฉ์„ ํ”ผํ•˜๋ผ
        • ์•„์ดํ…œ9 try-finally๋ณด๋‹ค๋Š” try-with-resources๋ฅผ ์‚ฌ์šฉํ•˜๋ผ
      • ๋ชจ๋“  ๊ฐ์ฒด์˜ ๊ณตํ†ต ๋ฉ”์„œ๋“œ
        • ์•„์ดํ…œ10 equals๋Š” ์ผ๋ฐ˜ ๊ทœ์•ฝ์„ ์ง€์ผœ ์žฌ์ •์˜ํ•˜๋ผ
        • ์•„์ดํ…œ11 equals๋ฅผ ์žฌ์ •์˜ ํ•˜๋ ค๊ฑฐ๋“  hashCode๋„ ์žฌ์ •์˜ ํ•˜๋ผ
        • ์•„์ดํ…œ12 toString์„ ํ•ญ์ƒ ์žฌ์ •์˜ํ•˜๋ผ
        • ์•„์ดํ…œ13 clone ์žฌ์ •์˜๋Š” ์ฃผ์˜ํ•ด์„œ ์ง„ํ–‰ํ•ด๋ผ
        • ์•„์ดํ…œ14 Comparable์„ ๊ตฌํ˜„ํ• ์ง€ ๊ณ ๋ คํ•˜๋ผ
      • ํด๋ž˜์Šค์™€ ์ธํ„ฐํŽ˜์ด์Šค
        • ์•„์ดํ…œ15 ํด๋ž˜์Šค์™€ ๋ฉค๋ฒ„์˜ ์ ‘๊ทผ ๊ถŒํ•œ์„ ์ตœ์†Œํ™”ํ•˜๋ผ
        • ์•„์ดํ…œ16 public ํด๋ž˜์Šค์—์„œ๋Š” public ํ•„๋“œ๊ฐ€ ์•„๋‹Œ ์ ‘๊ทผ์ž ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ17 ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ์„ฑ์„ ์ตœ์†Œํ™”ํ•˜๋ผ
        • ์•„์ดํ…œ18 ์ƒ์†๋ณด๋‹ค๋Š” ์ปดํฌ์ง€์…˜์„ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ19 ์ƒ์†์„ ๊ณ ๋ คํ•ด ์„ค๊ณ„ํ•˜๊ณ  ๋ฌธ์„œํ™”ํ•˜๋ผ. ๊ทธ๋Ÿฌ์ง€ ์•Š์•˜๋‹ค๋ฉด ์ƒ์†์„ ๊ธˆ์ง€ํ•˜๋ผ
        • ์•„์ดํ…œ20 ์ถ”์ƒ ํด๋ž˜์Šค๋ณด๋‹ค๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์šฐ์„ ํ•˜๋ผ
        • ์•„์ดํ…œ21 ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๊ตฌํ˜„ํ•˜๋Š” ์ชฝ์„ ์ƒ๊ฐํ•ด ์„ค๊ณ„ํ•˜๋ผ
        • ์•„์ดํ…œ22 ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…์„ ์ •์˜ํ•˜๋Š” ์šฉ๋„๋กœ๋งŒ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ23 ํƒœ๊ทธ ๋‹ฌ๋ฆฐ ํด๋ž˜์Šค๋ณด๋‹ค๋Š” ํด๋ž˜์Šค ๊ณ„์ธต๊ตฌ์กฐ๋ฅผ ํ™œ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ24 ๋ฉค๋ฒ„ ํด๋ž˜์Šค๋Š” ๋˜๋„๋ก static์œผ๋กœ ๋งŒ๋“ค๋ผ
        • ์•„์ดํ…œ25 ํ†ฑ๋ ˆ๋ฒจ ํด๋ž˜์Šค๋Š” ํ•œ ํŒŒ์ผ์— ํ•˜๋‚˜๋งŒ ๋‹ด์œผ๋ผ
      • ์ œ๋„ค๋ฆญ
        • ์•„์ดํ…œ26 ๋กœ ํƒ€์ž…์€ ์‚ฌ์šฉํ•˜์ง€ ๋ง๋ผ
        • ์•„์ดํ…œ27 ๋น„๊ฒ€์‚ฌ ๊ฒฝ๊ณ ๋ฅผ ์ œ๊ฑฐํ•˜๋ผ
        • ์•„์ดํ…œ28 ๋ฐฐ์—ด๋ณด๋‹ค๋Š” ๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ29 ์ด์™•์ด๋ฉด ์ œ๋„ค๋ฆญ ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค๋ผ
        • ์•„์ดํ…œ30 ์ด์™•์ด๋ฉด ์ œ๋„ค๋ฆญ ๋ฉ”์„œ๋“œ๋กœ ๋งŒ๋“ค๋ผ
        • ์•„์ดํ…œ31 ํ•œ์ •์  ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•ด API ์œ ์—ฐ์„ฑ์„ ๋†’์ด๋ผ
        • ์•„์ดํ…œ32 ์ œ๋„ค๋ฆญ๊ณผ ๊ฐ€๋ณ€์ธ์ˆ˜๋ฅผ ํ•จ๊ป˜ ์“ธ ๋•Œ๋Š” ์‹ ์ค‘ํ•˜๋ผ
        • ์•„์ดํ…œ33 ํƒ€์ž… ์•ˆ์ „ ์ด์ข… ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๊ณ ๋ คํ•˜๋ผ
      • ์—ด๊ฑฐ ํƒ€์ž…๊ณผ ์• ๋„ˆํ…Œ์ด์…˜
        • ์•„์ดํ…œ34 int ์ƒ์ˆ˜ ๋Œ€์‹  ์—ด๊ฑฐ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ35 ordinal ๋ฉ”์„œ๋“œ ๋Œ€์‹  ์ธ์Šคํ„ด์Šค ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ36 ๋น„ํŠธ ํ•„๋“œ ๋Œ€์‹  EnumSet์„ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ37 ordinal ์ธ๋ฑ์‹ฑ ๋Œ€์‹  EnumMap์„ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ38 ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋Š” ์—ด๊ฑฐ ํƒ€์ž…์ด ํ•„์š”ํ•˜๋ฉด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ 39 ๋ช…๋ช… ํŒจํ„ด๋ณด๋‹ค ์• ๋„ˆํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ40 @Override ์• ๋„ˆํ…Œ์ด์…˜์„ ์ผ๊ด€๋˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ41 ์ •์˜ํ•˜๋ ค๋Š” ๊ฒƒ์ด ํƒ€์ž…์ด๋ผ๋ฉด ๋งˆ์ปค ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ผ
      • ๋žŒ๋‹ค์™€ ์ŠคํŠธ๋ฆผ
        • ์•„์ดํ…œ46 ์ŠคํŠธ๋ฆผ์—๋Š” ๋ถ€์ž‘์šฉ ์—†๋Š” ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ47 ๋ฐ˜ํ™˜ ํƒ€์ž…์œผ๋กœ๋Š” ์ŠคํŠธ๋ฆผ๋ณด๋‹ค ์ปฌ๋ ‰์…˜์ด ๋‚ซ๋‹ค
        • ์•„์ดํ…œ48 ์ŠคํŠธ๋ฆผ ๋ณ‘๋ ฌํ™”๋Š” ์ฃผ์˜ํ•ด์„œ ์ ์šฉํ•˜๋ผ
      • ๋ฉ”์„œ๋“œ
        • ์•„์ดํ…œ49 ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์œ ํšจํ•œ์ง€ ๊ฒ€์‚ฌํ•˜๋ผ
        • ์•„์ดํ…œ50 ์ ์‹œ์— ๋ฐฉ์–ด์  ๋ณธ์‚ฌ๋ณธ์„ ๋งŒ๋“ค๋ผ
        • ์•„์ดํ…œ53 ๊ฐ€๋ณ€์ธ์ˆ˜๋Š” ์‹ ์ค‘ํžˆ ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ 54 null์ด ์•„๋‹Œ, ๋นˆ ์ปฌ๋ ‰์…˜์ด๋‚˜ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•˜๋ผ
        • ์•„์ดํ…œ56 ๊ณต๊ฐœ๋œ API ์š”์†Œ์—๋Š” ํ•ญ์ƒ ๋ฌธ์„œํ™” ์ฃผ์„์„ ์ž‘์„ฑํ•˜๋ผ
      • ์ผ๋ฐ˜์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์›์น™
        • ์•„์ดํ…œ56 ๊ณต๊ฐœ๋œ API ์š”์†Œ์—๋Š” ํ•ญ์ƒ ๋ฌธ์„œํ™” ์ฃผ์„์„ ์ž‘์„ฑํ•˜๋ผ
        • ์•„์ดํ…œ57 ์ง€์—ญ๋ณ€์ˆ˜์˜ ๋ฒ”์œ„๋ฅผ ์ตœ์†Œํ™”ํ•˜๋ผ
        • ์•„์ดํ…œ 60 ์ •ํ™•ํ•œ ๋‹ต์ด ํ•„์š”ํ•˜๋‹ค๋ฉด float์™€ double์€ ํ”ผํ•˜๋ผ
      • ์˜ˆ์™ธ
        • ์•„์ดํ…œ 73 ์ถ”์ƒํ™” ์ˆ˜์ค€์— ๋งž๋Š” ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋ผ
        • ์•„์ดํ…œ 74 ๋ฉ”์„œ๋“œ๊ฐ€ ๋˜์ง€๋Š” ๋ชจ๋“  ์˜ˆ์™ธ๋ฅผ ๋ฌธ์„œํ™”ํ•˜๋ผ
      • ๋™์‹œ์„ฑ
        • ์•„์ดํ…œ78 ๊ณต์œ  ์ค‘์ธ ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ๋Š” ๋™๊ธฐํ™”ํ•ด ์‚ฌ์šฉํ•˜๋ผ
        • ์•„์ดํ…œ79 ๊ณผ๋„ํ•œ ๋™๊ธฐํ™”๋Š” ํ”ผํ•˜๋ผ
        • ์•„์ดํ…œ 80 ์Šค๋ ˆ๋“œ๋ณด๋‹ค๋Š” ์‹คํ–‰์ž, ํƒœ์Šคํฌ, ์ŠคํŠธ๋ฆผ์„ ์• ์šฉํ•˜๋ผ
      • ์ง๋ ฌํ™”
        • ์•„์ดํ…œ 87 ์ปค์Šคํ…€ ์ง๋ ฌํ™” ํ˜•ํƒœ๋ฅผ ๊ณ ๋ คํ•ด๋ณด๋ผ
    • Functional Programming in Java
      • Chap 01. ํ—ฌ๋กœ, ๋žŒ๋‹ค ํ‘œํ˜„์‹
      • Chap 02. ์ปฌ๋ ‰์…˜์˜ ์‚ฌ์šฉ
      • Chap 03. String, Comparator, ๊ทธ๋ฆฌ๊ณ  filter
      • Chap 04. ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ์ด์šฉํ•œ ์„ค๊ณ„
      • CHAP 05. ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•œ ์ž‘์—…
      • CHAP 06. ๋ ˆ์ด์ง€
      • CHAP 07. ์žฌ๊ท€ ํ˜ธ์ถœ ์ตœ์ ํ™”
      • CHAP 08. ๋žŒ๋‹ค ํ‘œํ˜„์‹์˜ ์กฐํ•ฉ
      • CHAP 09. ๋ชจ๋“  ๊ฒƒ์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด๋ณด์ž
      • ๋ถ€๋ก 1. ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ง‘ํ•ฉ
      • ๋ถ€๋ก 2. ์‹ ํƒ์Šค ์˜ค๋ฒ„๋ทฐ
    • ์ฝ”ํ‹€๋ฆฐ ์ฟก๋ถ
      • 2์žฅ ์ฝ”ํ‹€๋ฆฐ ๊ธฐ์ดˆ
      • 3์žฅ ์ฝ”ํ‹€๋ฆฐ ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ
      • 4์žฅ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ
      • 5์žฅ ์ปฌ๋ ‰์…˜
      • 6์žฅ ์‹œํ€€์Šค
      • 7์žฅ ์˜์—ญ ํ•จ์ˆ˜
      • 9์žฅ ํ…Œ์ŠคํŠธ
      • 10์žฅ ์ž…๋ ฅ/์ถœ๋ ฅ
      • 11์žฅ ๊ทธ ๋ฐ–์˜ ์ฝ”ํ‹€๋ฆฐ ๊ธฐ๋Šฅ
    • DDD START!
      • 1์žฅ ๋„๋ฉ”์ธ ๋ชจ๋ธ ์‹œ์ž‘
      • 2์žฅ ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์š”
      • 3์žฅ ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ
      • 4์žฅ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์™€ ๋ชจ๋ธ๊ตฌํ˜„(JPA ์ค‘์‹ฌ)
      • 5์žฅ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์˜ ์กฐํšŒ ๊ธฐ๋Šฅ(JPA ์ค‘์‹ฌ)
      • 6์žฅ ์‘์šฉ ์„œ๋น„์Šค์™€ ํ‘œํ˜„ ์˜์—ญ
      • 7์žฅ ๋„๋ฉ”์ธ ์„œ๋น„์Šค
      • 8์žฅ ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ
      • 9์žฅ ๋„๋ฉ”์ธ ๋ชจ๋ธ๊ณผ BOUNDED CONTEXT
      • 10์žฅ ์ด๋ฒคํŠธ
      • 11์žฅ CQRS
    • JAVA 8 IN ACTION
      • 2์žฅ ๋™์ž‘ ํŒŒ๋ผ๋ฏธํ„ฐํ™” ์ฝ”๋“œ ์ „๋‹ฌํ•˜๊ธฐ
      • 3์žฅ ๋žŒ๋‹ค ํ‘œํ˜„์‹
      • 4์žฅ ์ŠคํŠธ๋ฆผ ์†Œ๊ฐœ
      • 5์žฅ ์ŠคํŠธ๋ฆผ ํ™œ์šฉ
      • 6์žฅ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
      • 7์žฅ ๋ณ‘๋ ฌ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ ์„ฑ๋Šฅ
      • 8์žฅ ๋ฆฌํŒฉํ† ๋ง, ํ…Œ์ŠคํŒ…, ๋””๋ฒ„๊น…
      • 9์žฅ ๋””ํดํŠธ ๋ฉ”์„œ๋“œ
      • 10์žฅ null ๋Œ€์‹  Optional
      • 11์žฅ CompletableFuture: ์กฐํ•ฉํ•  ์ˆ˜ ์žˆ๋Š” ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ
      • 12์žฅ ์ƒˆ๋กœ์šด ๋‚ ์งœ์™€ ์‹œ๊ฐ„ API
      • 13์žฅ ํ•จ์ˆ˜ํ˜• ๊ด€์ ์œผ๋กœ ์ƒ๊ฐํ•˜๊ธฐ
      • 14์žฅ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ๋ฒ•
    • ๊ฐ์ฒด์ง€ํ–ฅ๊ณผ ๋””์ž์ธํŒจํ„ด
      • ๊ฐ์ฒด ์ง€ํ–ฅ
      • ๋‹คํ˜•์„ฑ๊ณผ ์ถ”์ƒ ํƒ€์ž…
      • ์žฌ์‚ฌ์šฉ: ์ƒ์†๋ณด๋‹จ ์กฐ๋ฆฝ
      • ์„ค๊ณ„ ์›์น™: SOLID
      • DI์™€ ์„œ๋น„์Šค ๋กœ์ผ€์ดํ„ฐ
      • ์ฃผ์š” ๋””์ž์ธ ํŒจํ„ด
        • ์ „๋žตํŒจํ„ด
        • ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ ํŒจํ„ด
        • ์ƒํƒœ ํŒจํ„ด
        • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด
        • ํ”„๋ก์‹œ ํŒจํ„ด
        • ์–ด๋Œ‘ํ„ฐ ํŒจํ„ด
        • ์˜ต์ €๋ฒ„ ํŒจํ„ด
        • ํŒŒ์‚ฌ๋“œ ํŒจํ„ด
        • ์ถ”์ƒ ํŒฉํ† ๋ฆฌ ํŒจํ„ด
        • ์ปดํฌ์ง€ํŠธ ํŒจํ„ด
    • NODE.JS
      • 1ํšŒ์ฐจ
      • 2ํšŒ์ฐจ
      • 3ํšŒ์ฐจ
      • 4ํšŒ์ฐจ
      • 6ํšŒ์ฐจ
      • 7ํšŒ์ฐจ
      • 8ํšŒ์ฐจ
      • 9ํšŒ์ฐจ
      • 10ํšŒ์ฐจ
      • 11ํšŒ์ฐจ
      • 12ํšŒ์ฐจ
      • mongoose
      • AWS๋ž€?
    • SRPING IN ACTION (5th)
      • Chap1. ์Šคํ”„๋ง ์‹œ์ž‘ํ•˜๊ธฐ
      • Chap 2. ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœํ•˜๊ธฐ
      • Chap 3. ๋ฐ์ดํ„ฐ๋กœ ์ž‘์—…ํ•˜๊ธฐ
      • Chap 4. ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ
      • Chap 5. ๊ตฌ์„ฑ ์†์„ฑ ์‚ฌ์šฉํ•˜๊ธฐ
      • Chap 6. REST ์„œ๋น„์Šค ์ƒ์„ฑํ•˜๊ธฐ
      • Chap 7. REST ์„œ๋น„์Šค ์‚ฌ์šฉํ•˜๊ธฐ
      • CHAP 8 ๋น„๋™๊ธฐ ๋ฉ”์‹œ์ง€ ์ „์†กํ•˜๊ธฐ
      • Chap 9. ์Šคํ”„๋ง ํ†ตํ•ฉํ•˜๊ธฐ
      • CHAP 10. ๋ฆฌ์•กํ„ฐ ๊ฐœ์š”
      • CHAP 13. ์„œ๋น„์Šค ํƒ๊ตฌํ•˜๊ธฐ
      • CHAP 15. ์‹คํŒจ์™€ ์ง€์—ฐ ์ฒ˜๋ฆฌํ•˜๊ธฐ
      • CHAP 16. ์Šคํ”„๋ง ๋ถ€ํŠธ ์•ก์ถ”์—์ดํ„ฐ ์‚ฌ์šฉํ•˜๊ธฐ
    • ์Šคํ”„๋ง๋ถ€ํŠธ ์ฝ”๋”ฉ ๊ณต์ž‘์†Œ
      • ์Šคํ”„๋ง ๋ถ€ํŠธ๋ฅผ ์™œ ์‚ฌ์šฉ ํ•ด์•ผ ํ• ๊นŒ?
      • ์ฒซ ๋ฒˆ์งธ ์Šคํ”„๋ง ๋ถ€ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœํ•˜๊ธฐ
      • ๊ตฌ์„ฑ์„ ์‚ฌ์šฉ์žํ™” ํ•˜๊ธฐ
      • ์Šคํ”„๋ง๋ถ€ํŠธ ํ…Œ์ŠคํŠธํ•˜๊ธฐ
      • ์•ก์ถ”์—์ดํ„ฐ๋กœ ๋‚ด๋ถ€ ๋“ค์—ฌ๋‹ค๋ณด๊ธฐ
    • ANGULAR 4
      • CHAPTER 1. A gentle introduction to ECMASCRIPT 6
      • CHAPTER 2. Diving into TypeScript
      • CHAPTER 3. The wonderful land of Web Components
      • CHAPTER 4. From zero to something
      • CHAPTER 5. The templating syntax
      • CHAPTER 6. Dependency injection
      • CHAPTER 7. Pipes
      • CHAPTER 8. Reactive Programming
      • CHAPTER 9. Building components and directives
      • CHAPTER 10. Styling components and encapsulation
      • CHAPTER 11. Services
      • CHAPTER 12. Testing your app
      • CHAPTER 13. Forms
      • CHAPTER 14. Send and receive data with Http
      • CHAPTER 15. Router
      • CHAPTER 16. Zones and the Angular magic
      • CHAPTER 17. This is the end
    • HTTP ์™„๋ฒฝ ๊ฐ€์ด๋“œ
      • ๊ฒŒ์ดํŠธ์›จ์ด vs ํ”„๋ก์‹œ
      • HTTP Header
      • REST API
      • HTTP Method ์ข…๋ฅ˜
        • HTTP Status Code
      • HTTP 2.x
  • REFERENCE
    • TECH BLOGS
      • ์–ด์ธ๋ฐ๋ธŒ๋ธ”๋กœ๊ทธ
      • NAVER D2
      • ์šฐ์•„ํ•œ ํ˜•์ œ๋“ค
      • ์นด์นด์˜ค
      • LINE
      • ์Šคํฌ์นด
      • ํ‹ฐ๋ชฌ
      • NHN
      • ๋งˆ์ผ“์ปฌ๋ฆฌ
      • ์ฟ ํŒก
      • ๋ ˆ์ง„
      • ๋ฐ์ผ๋ฆฌ ํ˜ธํ…”
      • ์ง€๊ทธ์žฌ๊ทธ
      • ์Šคํƒ€์ผ์‰์–ด
      • ๊ตฌ๊ธ€
      • ์•ผ๋†€์ž
    • ALGORITHM
      • ์ƒํ™œ์ฝ”๋”ฉ
      • ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค
      • ๋ฐฑ์ค€
      • ์•Œ๊ณ ์ŠคํŒŸ
      • ์ฝ”๋”œ๋ฆฌํ‹ฐ
      • ๊ตฌ๋ฆ„
      • ๋ฆฟ์ฝ”๋“œ
Powered by GitBook
On this page
  • Forms, dear forms
  • Template-driven
  • Two-way data-binding
  • Fake dependencies
  • Code-driven
  • Adding some validation
  • In a code-friven form
  • In a template-driven form
  • Errors and submission
  • Errors and submission in a code-driven form
  • Errors and submission in a template-driven form
  • Add some style
  • Creating a custom validator
  • Using a validator in a code-driven form
  • Using a validator in a template-driven form
  • Combining template-based and code-based approaches for validation
  • Grouping fields
  • Reacting on changes
  • Summary
  • Reference URL

Was this helpful?

  1. STUDY
  2. ANGULAR 4

CHAPTER 13. Forms

Forms, dear forms

ํผ์€ ํ•ญ์ƒ Angular์—์„œ ๋” ๊ฐ€๊ณต ๋˜์–ด์™”๋‹ค. ์ด๊ฒƒ์€ 1.x์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ ๋œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ด๋ฉฐ ๊ฑฐ์˜ ๋ชจ๋“  ์•ฑ์— ํ˜•ํƒœ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์˜ ๋งˆ์Œ์„ ์‚ฌ๋กœ ์žก์•˜๋‹ค.

ํผ์€ ์–ด๋ ค์› ๋‹ค : ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์„ ๊ฒ€์ฆํ•˜๊ณ , ์˜ค๋ฅ˜๋ฅผ ํ‘œ์‹œํ•˜๊ณ , ํ•„๋“œ๋ฅผ ํ•„์š”๋กœํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๊ฑฐ๋‚˜, ๋‹ค๋ฅธ ํ•„๋“œ์— ๋”ฐ๋ผ ์ผ๋ถ€ ํ•„๋“œ ๋ณ€๊ฒฝ ๋“ฑ์— ๋Œ€์‘ํ•ด์•ผํ•œ๋‹ค. ๋˜ํ•œ ์ด๋Ÿฌํ•œ ์–‘์‹์„ ํ…Œ์ŠคํŠธ ํ•ด์•ผ ํ•œ๋‹ค. AngularJS 1.x์˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋กœ๋Š” ๋ถˆ๊ฐ€๋Šฅ ํ–ˆ๋‹ค. ์—”๋“œ - ํˆฌ - ์—”๋“œ (end-to-end) ํ…Œ์ŠคํŠธ๋งŒ์œผ๋กœ๋Š” ์‹คํ˜„ ๊ฐ€๋Šฅ์„ฑ์ด ๋‚ฎ์•˜๋‹ค.

Angular 2์—์„œ๋Š” ์–‘์‹์— ๋™์ผํ•œ์ฃผ์˜๊ฐ€ ์ ์šฉ๋˜์—ˆ์œผ๋ฉฐ ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” ์–‘์‹์„ ์ž‘์„ฑํ•˜๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค. ์‚ฌ์‹ค ๊ทธ๊ฒƒ์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค.

ํ…œํ”Œ๋ฆฟ์˜ ์ง€์‹œ๋ฌธ๋งŒ ์‚ฌ์šฉํ•˜์—ฌ ์–‘์‹์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” "ํ…œํ”Œ๋ฆฟ ์ค‘์‹ฌ" ๋ฐฉ์‹์ด๋‹ค. ์šฐ๋ฆฌ์˜ ๊ฒฝํ—˜์— ๋น„์ถ”์–ด ๋ณผ ๋•Œ, ๋‹น์‹ ์ด ๋‹จ์ˆœํ•œ ํผ์„ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋•Œ, ๋งŽ์€ ๊ฒ€์ฆ์ด ์—†์ด ๋น›๋‚œ๋‹ค.

๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ ๊ตฌ์„ฑ ์š”์†Œ์— ํผ์— ๋Œ€ํ•œ ์„ค๋ช…์„ ์ž‘์„ฑํ•œ ๋‹ค์Œ ์ง€์‹œ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ํผ์„ ํ…œํ”Œ๋ฆฟ์˜ inputs / textareas / choose์— ๋ฐ”์ธ๋“œํ•˜๋Š” "์ฝ”๋“œ ์ค‘์‹ฌ"๋ฐฉ์‹์ด๋‹ค. ์ข€ ๋” ์ž์„ธํ•œ ์ •๋ณด ๋ฟ ์•„๋‹ˆ๋ผ ํŠนํžˆ ์‚ฌ์šฉ์ž ์ •์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์ถ”๊ฐ€ ๋˜๋Š” ๋™์  ์–‘์‹ ์ƒ์„ฑ์„ ์›ํ•  ๊ฒฝ์šฐ ๋”์šฑ ๊ฐ•๋ ฅํ•˜๋‹ค.

๋™์ผํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ๋‘ ๋ฒˆ ๋ฐ˜๋ณตํ•˜๊ณ  ๊ฐ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฐจ์ด์ ์„ ์‚ดํŽด ๋ณด๋„๋ก ํ•˜์ž.

์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ์˜ ๋ฉ‹์ง„ PonyRacer ์•ฑ์— ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž๋ฅผ ๋“ฑ๋ก ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฐ„๋‹จํ•œ ์–‘์‹์„ ์ž‘์„ฑํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ๊ฐ ์œ ์Šค ์ผ€์ด์Šค๋งˆ๋‹ค ๊ธฐ๋ณธ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹œ์ž‘ํ•ด ๋ณด์ž.

import { Component } from '@angular/core';
@Component({
  selector: 'ns-register',
  template: `
  <h2>Sign up</h2>
  <form></form>
  `
})
export class RegisterFormComponent {
}

๋ฉ‹์ง„ ๊ฒƒ์€ ์—†๋‹ค : ํผ์„ ํฌํ•จํ•˜๋Š” ๊ฐ„๋‹จํ•œ ํ…œํ”Œ๋ฆฟ์„ ๊ฐ€์ง„ ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค์Œ ๋ช‡ ๋ถ„ ์•ˆ์— ์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ์•”ํ˜ธ๋ฅผ ๋“ฑ๋ก ํ•  ์ˆ˜์žˆ๋Š” ์–‘์‹์„ ์ž‘์„ฑํ•œ๋‹ค.

๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ๋ชจ๋‘ Angular๋Š” ์–‘์‹์˜ ํ‘œํ˜„์„ ๋งŒ๋“ ๋‹ค.

"ํ…œํ”Œ๋ฆฟ ์ค‘์‹ฌ" ๋ฐฉ์‹์—์„œ๋Š” ๊ฝค ์ž๋™์ ์ด๋‹ค. ํ…œํ”Œ๋ฆฟ์— ์ ์ ˆํ•œ ์ง€์‹œ๋ฌธ์„ ์ถ”๊ฐ€ ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ํ”„๋ ˆ์ž„ ์›Œํฌ๊ฐ€ ์–‘์‹ ํ‘œํ˜„ ์ž‘์„ฑ์„ ๋‹ด๋‹นํ•œ๋‹ค. "์ฝ”๋“œ ์ค‘์‹ฌ" ๋ฐฉ์‹์—์„œ๋Š” ์ด ํผ ํ‘œํ˜„์„ ์ˆ˜๋™์œผ๋กœ ์ž‘์„ฑํ•œ ๋‹ค์Œ ์ง€์‹œ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์–‘์‹ ํ‘œํ˜„์„ ์ž…๋ ฅ์— ๋ฐ”์ธ๋“œ ํ•œ๋‹ค.

ํผ ๋’ค์—์„œ ์ž…๋ ฅ ํ•„๋“œ ๋‚˜ ์„ ํƒ ํ•„๋“œ์™€ ๊ฐ™์€ ํผ ํ•„๋“œ๋Š” FormControlin Angular 2๋กœ ํ‘œํ˜„๋œ๋‹ค. ์ด๊ฒƒ์€ ํผ์˜ ๊ฐ€์žฅ ์ž‘์€ ๋ถ€๋ถ„์ด๋ฉฐ ํ•„๋“œ์˜ ์ƒํƒœ์™€ ๊ทธ ๊ฐ’์„ ์บก์Аํ™” ํ•œ๋‹ค.

FormControl์—๋Š”, ๋‹ค์Œ์˜ ๋ช‡๊ฐœ์˜ ์†์„ฑ์ด ์žˆ๋‹ค.

โ€ข valid : ํ•ด๋‹น ํ•„๋“œ๊ฐ€ ์œ ํšจํ•˜๊ณ  ํ•ด๋‹น ํ•„๋“œ์— ์ ์šฉ๋˜๋Š” ์š”๊ตฌ ์‚ฌํ•ญ ๋ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ. โ€ข errors : ํ•„๋“œ ์˜ค๋ฅ˜๊ฐ€์žˆ๋Š” ๊ฐ์ฒด. โ€ข dirty : ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ’์„ ์ˆ˜์ •ํ•  ๋•Œ๊นŒ์ง€ false. โ€ข pristine : ์ˆ˜์ •ํ•˜๊ธฐ ์ด์ „. โ€ข touched : ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅ ํ•  ๋•Œ๊นŒ์ง€ false. โ€ข untouched : ์ž…๋ ฅํ•˜๊ธฐ ์ „๊นŒ์ง€ ์ƒํƒœ. โ€ข value : ํ•„๋“œ์˜ ๊ฐ’. โ€ข valueChanges : ํ•„๋“œ์— ๋ณ€ํ™”๊ฐ€ ์žˆ์„ ๋•Œ๋งˆ๋‹ค ๊ด€์ฐฐ ๋˜๋Š” ๊ด€์ฐฐ ๊ฐ€๋Šฅ ํ•จ์ˆ˜

๋˜ํ•œ ์ปจํŠธ๋กค์— ํŠน์ • ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” hasError()์™€ ๊ฐ™์€ ๋ช‡ ๊ฐ€์ง€ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

const password = new FormControl();
console.log(password.dirty); // false until the user enters a value
console.log(password.value); // null until the user enters a value
console.log(password.hasError('required')); // false

์ƒ์„ฑ์ž์— ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ด ์ธ์ˆ˜๋Š” ๊ฐ’์ด ๋œ๋‹ค.

const password = new FormControl('Cรฉdric');
console.log(password.value); // logs "Cรฉdric"

์ด๋Ÿฌํ•œ ์ปจํŠธ๋กค์€ FormGroup์— ๊ทธ๋ฃนํ™”ํ•˜์—ฌ ์–‘์‹์˜ ์ผ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๊ณ  ์ „์šฉ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ทœ์น™์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค. ์–‘์‹ ์ž์ฒด๊ฐ€ ๊ทธ๋ฃน์ด๋‹ค.

FormGroup์€ FormControl๊ณผ ๋™์ผํ•œ ์†์„ฑ์„ ๊ฐ–์ง€๋งŒ ๋ช‡ ๊ฐ€์ง€ ์ฐจ์ด์ ์ด ์žˆ๋‹ค.

โ€ข valid : ๋ชจ๋“  ํ•„๋“œ๊ฐ€ ์œ ํšจํ•˜๋ฉด ๊ทธ๋ฃน์˜ ์œ ํšจ์„ฑ. โ€ข errors : ๊ทธ๋ฃน ์˜ค๋ฅ˜๊ฐ€์žˆ๋Š” ๊ฐ์ฒด์ด๊ฑฐ๋‚˜ ๊ทธ๋ฃน์ด ์œ ํšจํ•œ ๊ฒฝ์šฐ null์ด๋‹ค. ๊ฐ ์˜ค๋ฅ˜๋Š” ํ‚ค์ด๊ณ , value๋Š” ์ด ์˜ค๋ฅ˜์˜ ์˜ํ–ฅ์„ ๋ฐ›๋Š” ๋ชจ๋“  ์ปจํŠธ๋กค์ด ๋“ค์–ด์žˆ๋Š” ๋ฐฐ์—ด์ด๋‹ค. โ€ข dirty : ์ปจํŠธ๋กค ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋”๋Ÿฌ์›Œ ์งˆ ๋•Œ๊นŒ์ง€ false. โ€ข pristine : dirty์˜ ๋ฐ˜๋Œ€. โ€ข touched : ์ปจํŠธ๋กค ์ค‘ ํ•˜๋‚˜๊ฐ€ ํ„ฐ์น˜ ๋  ๋•Œ๊นŒ์ง€ false. โ€ข untouched : touched์˜ ๋ฐ˜๋Œ€. โ€ข value : ๊ทธ๋ฃน์˜ ๊ฐ’. ๋” ์ •ํ™•ํ•˜๊ฒŒ ๋งํ•˜์ž๋ฉด, ํ‚ค / ๊ฐ’์ด ์ปจํŠธ๋กค๊ณผ ๊ทธ ๊ฐ’. โ€ข valueChanges : ๊ทธ๋ฃน์— ๋ณ€ํ™”๊ฐ€์žˆ์„ ๋•Œ๋งˆ๋‹ค ๊ด€์ฐฐ๋˜๋Š” ๊ด€์ฐฐ ๊ฐ€๋Šฅ

hasError()์™€ ๊ฐ™์€ FormControl๊ณผ ๋™์ผํ•œ ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๋˜ํ•œ get() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ทธ๋ฃน์—์„œ ์ปจํŠธ๋กค์„ ๊ฒ€์ƒ‰ํ•œ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

const form = new FormGroup({
  username: new FormControl('Cรฉdric'),
  password: new FormControl()
});
console.log(form.dirty); // logs false until the user enters a value
console.log(form.value); // logs Object {username: "Cรฉdric", password: null}
console.log(form.get('username')); // logs the Control

Template-driven

์ด ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํผ ์—์„œ ๋งŽ์€ ์ง€์‹œ๋ฌธ์„ ์‚ฌ์šฉํ•˜๊ณ  ํ”„๋ ˆ์ž„ ์›Œํฌ์—์„œ ํ•„์š”ํ•œ FormControl ๋ฐ FormGroup ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๋„๋ก ํ•˜์ž. ์˜ˆ๋ฅผ ๋“ค์–ด, NgForm ์ง€์‹œ๋ฌธ์€ ์–‘์‹ ์š”์†Œ๋ฅผ ๊ฐ•๋ ฅํ•œ Angular 2 ๋ฒ„์ „์œผ๋กœ ๋ณ€ํ˜•ํ•œ๋‹ค. Bruce Wayne๊ณผ Batman์˜ ์ฐจ์ด์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

ํ•„์š”ํ•œ ์ง€์‹œ๋ฌธ์€ ๋ชจ๋‘ FormsModule ๋ชจ๋“ˆ์— ํฌํ•จ ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๋ฃจํŠธ ๋ชจ๋“ˆ์—์„œ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
@NgModule({
  imports: [BrowserModule, FormsModule],
  declarations: [PonyRacerAppComponent],
  bootstrap: [PonyRacerAppComponent]
})
export class AppModule {
}

FormsModule์—๋Š” "ํ…œํ”Œ๋ฆฟ ์ค‘์‹ฌ"๋ฐฉ์‹์— ๋Œ€ํ•œ ์ง€์‹œ๋ฌธ์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋‚˜์ค‘์— "์ฝ”๋“œ ์ค‘์‹ฌ"๋ฐฉ์‹์— ํ•„์š”ํ•œ ๋™์ผํ•œ ๋ชจ๋“ˆ @ Angular / Forms์— ReactiveFormsModule์ด๋ผ๋Š” ๋˜ ๋‹ค๋ฅธ ๋ชจ๋“ˆ์ด ์žˆ์Œ์„ ๋ณด๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

import { Component } from '@angular/core';
@Component({
  selector: 'ns-register',
  template: `
  <h2>Sign up</h2>
  <form (ngSubmit)="register()">
   <button type="submit">Register</button>
  </form>
  `
})
export class RegisterFormComponent {
  register() {
   // we will have to handle the submission
  }
}

๋‹จ์ถ”๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์–‘์‹ ํƒœ๊ทธ์— ngSubmit์— ๋Œ€ํ•œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ •์˜ ํ•˜์˜€๋‹ค. ngSubmit ์ด๋ฒคํŠธ๋Š” ์ œ์ถœ์ด ํŠธ๋ฆฌ๊ฑฐ ๋  ๋•Œ NgForm ์ง€์‹œ๋ฌธ์— ์˜ํ•ด ์ƒ์„ฑ๋œ๋‹ค. ๋‚˜์ค‘์— ๊ตฌํ˜„ ๋  ์ปจํŠธ๋กค๋Ÿฌ์˜ register () ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ํ…œํ”Œ๋ฆฟ์ด ๋น ๋ฅด๊ฒŒ ์ปค์งˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ templateUrl์„ ์‚ฌ์šฉํ•˜์—ฌ ์ „์šฉ ํŒŒ์ผ์— ํ…œํ”Œ๋ฆฟ์„ ์ถ”์ถœํ•ด ๋ณด๋„๋ก ํ•˜์ž.

import { Component } from '@angular/core';
@Component({
  selector: 'ns-register',
  templateUrl: 'register-form.component.html'
})
export class RegisterFormComponent {
  register() {
   // we will have to handle the submission
  }
}

"ํ…œํ”Œ๋ฆฟ ์ค‘์‹ฌ"๋ฐฉ์‹์—์„œ๋Š” AngularJS 1.x์™€ ๋งค์šฐ ํก์‚ฌํ•˜๋‹ค. ํ…œํ”Œ๋ฆฟ์— ๋งŽ์€ ์š”์†Œ๊ฐ€ ์žˆ๊ณ  ๊ตฌ์„ฑ ์š”์†Œ์—๋Š” ๋งŽ์ง€ ์•Š๋‹ค.

๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ํ˜•์‹์—์„œ๋Š” ngModel ์ง€์ • ๋ฌธ์„ ์–‘์‹ ํ…œํ”Œ๋ฆฌํŠธ์— ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค. NgModel ์ง€์‹œ๋ฌธ์€ ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•ด FormControl์„ ๋งŒ๋“ค๊ณ  ์–‘์‹์€ ์ž๋™์œผ๋กœ FormGroup์„ ๋งŒ๋“ ๋‹ค. ์ž…๋ ฅ์— ์ด๋ฆ„์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค. ์ด ์ด๋ฆ„์€ ํ”„๋ ˆ์ž„ ์›Œํฌ์—์„œ FormGroup์„ ๋งŒ๋“œ๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.

<h2>Sign up</h2>
<form (ngSubmit)="register()">
  <div>
   <label>Username</label><input name="username" ngModel>
  </div>
  <div>
   <label>Password</label><input type="password" name="password" ngModel>
  </div>
  <button type="submit">Register</button>
</form>

์ด์ œ ์šฐ๋ฆฌ๋Š” ์ œ์ถœ์„ ์œ„ํ•ด ๋ฌด์–ธ๊ฐ€๋ฅผ ํ•˜๊ณ  ์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ์•”ํ˜ธ๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค. ์ด๋ฅผ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๋กœ์ปฌ ๋ณ€์ˆ˜๋ฅผ ์ •์˜ํ•˜๊ณ  ์ด ๋ณ€์ˆ˜์— ์˜ํ•ด ์ƒ์„ฑ ๋œ NgForm ๊ฐ์ฒด๋ฅผ ํ• ๋‹น ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ…œํ”Œ๋ฆฟ ์žฅ์—์„œ ์ด๋“ค์„ ๊ธฐ์–ตํ•˜๋Š”๊ฐ€? ์—ฌ๊ธฐ์„œ๋Š” ํผ์„ ์ฐธ์กฐํ•˜๋Š” ๋ณ€์ˆ˜ userForm์„ ์ •์˜ ํ•  ๊ฒƒ์ด๋‹ค. form ๋””๋ ‰ํ‹ฐ๋ธŒ๋Š” FormGroup ํด๋ž˜์Šค์™€ ๋™์ผํ•œ ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ๋Š” NgForm ์ง€์‹œ๋ฌธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋‚ด๋ณด๋‚ด๋ฏ€๋กœ ๊ทธ๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ณ ๊ธ‰ ์ง€์‹œ๋ฌธ์„ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์—ฐ๊ตฌ ํ•  ๋•Œ ๋‚ด๋ณด๋‚ด๊ธฐ ๋ถ€๋ถ„์ด ๋” ์ž์„ธํžˆ ํ‘œ์‹œ๋œ๋‹ค.

<h2>Sign up</h2>
<!-- we use a local variable #userForm -->
<!-- and give its value to the register method -->
<form (ngSubmit)="register(userForm.value)" #userForm="ngForm">
  <div>
   <label>Username</label><input name="username" ngModel>
  </div>
  <div>
   <label>Password</label><input type="password" name="password" ngModel>
  </div>
  <button type="submit">Register</button>
</form>

์šฐ๋ฆฌ์˜ register ๋ฉ”์†Œ๋“œ๋Š” ์ด์ œ Form ๊ฐ’์„ ์ธ์ˆ˜๋กœ ์‚ฌ์šฉํ•˜์—ฌ ํ˜ธ์ถœํ•œ๋‹ค.

import { Component } from '@angular/core';
@Component({
  selector: 'ns-register',
  templateUrl: 'register.component.html'
})
export class RegisterFormComponent {
  register(user) {
   console.log(user);
  }
}

์ด๊ฒƒ์€ ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ ์ผ๋ฟ ์ด๋‹ค. ํ•„๋“œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ๋ชจ๋ธ์ด ์—…๋ฐ์ดํŠธ ๋˜์ง€๋งŒ ๋ชจ๋ธ์„ ์—…๋ฐ์ดํŠธํ•ด๋„ ํ•„๋“œ ๊ฐ’์€ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š๋Š”๋‹ค. ํ•˜์ง€๋งŒ ngModel์€ ์ƒ๊ฐ๋ณด๋‹ค ๊ฐ•๋ ฅํ•˜๋‹ค.

Two-way data-binding

AngularJS 1.x๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๊ธฐ์‚ฌ์— ๋Œ€ํ•œ ๊ธฐ์‚ฌ๋ฅผ ์ฝ์—ˆ๋‹ค๋ฉด ์ž…๋ ฅ ๊ฐ’์„ ํ‘œ์‹œํ•˜๋Š” ์œ ๋ช…ํ•œ ์˜ˆ์ œ์™€ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅ์„ ์ˆ˜์ •ํ•  ๋•Œ๋งˆ๋‹ค ์—…๋ฐ์ดํŠธ๋˜๋Š” ํ‘œํ˜„์‹ ๋ฐ ํ•„๋“œ๋ฅผ ์ž๋™์œผ๋กœ ํ‘œ์‹œํ•ด์•ผ ํ•œ๋‹ค. ๋ชจ๋ธ์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค.

<!-- AngularJS 1.x code example -->
<input type="text" ng-model="username">
<p>{{username}}</p>

Angular 2์™€ ๋น„์Šทํ•œ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์–‘์‹์—์„œ ์ฑ„์šธ ๋‚ด์šฉ์˜ ๋ชจ๋ธ์„ ์ •์˜ํ•˜์—ฌ ์‹œ์ž‘ํ•œ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ์„ Userclass์—์„œ ์ •์˜ํ•  ๊ฒƒ์ด๋‹ค.

class User {
  username: string;
  password: string;
}

RegisterFormComponent์—๋Š” User ์œ ํ˜•์˜ ํ•„๋“œ ์‚ฌ์šฉ์ž๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

import { Component } from '@angular/core';
class User {
  username: string;
  password: string;
}
@Component({
  selector: 'ns-register',
  templateUrl: 'register-form.component.html',
})
export class RegisterFormComponent {
  user = new User();
  register() {
   console.log(this.user);
  }
}

์˜ˆ์ œ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด register () ๋ฉ”์„œ๋“œ๋Š” ์ด์ œ ์‚ฌ์šฉ์ž ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๋กœ๊น… ํ•œ๋‹ค. ์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ ์–‘์‹์˜ ์ž…๋ ฅ์„ ์ถ”๊ฐ€ ํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ž…๋ ฅ ํ•œ ๋ชจ๋ธ์„ ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•œ ๋ชจ๋ธ์— ์—ฐ๊ฒฐ ํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ngModel ์ง€์‹œ๋ฌธ์„ ์‚ฌ์šฉํ•œ๋‹ค.

<h2>Sign up</h2>
<form (ngSubmit)="register()">
  <div>
   <label>Username</label><input name="username" [(ngModel)]="user.username">
  </div>
  <div>
   <label>Password</label><input type="password" name="password" [(ngModel)]= "user.password">
  </div>
  <button type="submit">Register</button>
</form>
<input name="username" [ngModel]="user.username" (ngModelChange)="user.username = $event ">

NgModel ์ง€์‹œ๋ฌธ์€ ์ž…๋ ฅ์ด ๋ณ€๊ฒฝ ๋  ๋•Œ๋งˆ๋‹ค ๊ด€๋ จ ๋ชจ๋ธ user.username์„ ์—…๋ฐ์ดํŠธํ•˜๋ฏ€๋กœ [ngModel] = "user.username" ๋ถ€๋ถ„์ด ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ngModel์ด๋ผ๋Š” ์ถœ๋ ฅ์—์„œ ์ด๋ฒคํŠธ๋ฅผ ๋‚ด ๋ณด๋‚ด๊ณ  ๋ชจ๋ธ์ด ์—…๋ฐ์ดํŠธ ๋  ๋•Œ๋งˆ๋‹ค ๋ณ€๊ฒฝ ๋œ๋‹ค.

๊ธด ํ˜•์‹์„ ์ž‘์„ฑํ•˜๋Š” ๋Œ€์‹  ์ƒˆ ๊ตฌ๋ฌธ [()]์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‚˜์ฒ˜๋Ÿผ, ๊ทธ๊ฒƒ์ด [()]์ธ์ง€ ์•„๋‹ˆ๋ฉด []์ธ์ง€ ๊ธฐ์–ตํ•˜๊ธฐ๊ฐ€ ์–ด๋ ต๋‹ค๋ฉด ๊ฐ„๋‹จํ•œ ํŒ์ด ์žˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋ฐ”๋‚˜๋‚˜ ์ƒ์ž์ด๋‹ค! : [] , ๋‚ด๋ถ€์—๋Š” ์„œ๋กœ ๋งˆ์ฃผํ•˜๋Š” ๋‘ ๊ฐœ์˜ ๋ฐ”๋‚˜๋‚˜๊ฐ€ ์žˆ๋‹ค ().

์ด์ œ๋Š” ์ž…๋ ฅ ํ•  ๋•Œ๋งˆ๋‹ค ๋ชจ๋ธ์ด ์—…๋ฐ์ดํŠธ ๋ ๊ฒƒ์ด๋‹ค. ๋ชจ๋ธ์—์„œ ๋ชจ๋ธ์ด ์—…๋ฐ์ดํŠธ ๋˜๋ฉด ํ•„๋“œ์—์„œ ์ž๋™์œผ๋กœ ์˜ฌ๋ฐ”๋ฅธ ๊ฐ’์„ ํ‘œ์‹œ ๋œ๋‹ค.

<h2>Sign up</h2>
<form (ngSubmit)="register()">
  <div>
   <label>Username</label><input name="username" [(ngModel)]="user.username">
   <small>{{ user.username }} is an awesome username!</small>
  </div>
  <div>
   <label>Password</label><input type="password" name="password" [(ngModel)]=
"user.password">
  </div>
  <button type="submit">Register</button>
</form>

์œ„์˜ ์˜ˆ๋ฅผ ์‹œ๋„ํ•˜๋ฉด ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์ด ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ์˜ ํ˜•์‹๋„ ๋งˆ์ฐฌ๊ฐ€์ง€ ์ด๋‹ค : ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์„ ์ œ์ถœํ•  ์ˆ˜ ์žˆ๊ณ , ์ปดํฌ๋„ŒํŠธ๋Š” ์‚ฌ์šฉ์ž ๊ฐ์ฒด๋ฅผ ๊ธฐ๋ก ํ•  ๊ฒƒ์ด๋‹ค!

Fake dependencies

ํ…Œ์ŠคํŠธ ๋ชจ๋“ˆ์— ๋Œ€ํ•œ ์ข…์†์„ฑ์„ ์„ ์–ธ ํ•  ์ˆ˜์žˆ๋Š” ๋‹ค๋ฅธ ์šฉ๋„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ์‹ค์ œ ์„œ๋น„์Šค ๋Œ€์‹  ๊ฐ€์งœ ์„œ๋น„์Šค๋ฅผ ์ข…์† ์„œ๋น„์Šค๋กœ ์„ ์–ธํ•˜๋Š” ๊ฒƒ์€ ๋„ˆ๋ฌด ๋งŽ์€ ์ˆ˜๊ณ ๋ฅผ ๋“ค์ด์ง€ ์•Š๊ณ ๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” ๋‚ด RaceService๊ฐ€ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ ˆ์ด์Šค๋ฅผ ์ €์žฅํ•˜๊ณ  ํ‚ค 'race'๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ํ•˜์ž. ๋‹น์‹ ์˜ ๋™๋ฃŒ๋Š” ์šฐ๋ฆฌ์˜ RaceService๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” JSON ์ง๋ ฌํ™” ๋“ฑ์„ ์ทจ๊ธ‰ LocalStorageService๋ผ๋Š” ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜์˜€๋‹ค. list ()ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

@Injectable()
export class RaceService {
  constructor(private localStorage: LocalStorageService) {
  }
  list() {
   return this.localStorage.get('races');
  }
}

์ด์ œ ์šฐ๋ฆฌ๋Š” LocalStorageService ์„œ๋น„์Šค๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ถ์ง€ ์•Š๋Š”๋‹ค. RaceService๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ถ๋‹ค. ์˜์กด์„ฑ ์ฃผ์ž… ์‹œ์Šคํ…œ์„ ํ™œ์šฉํ•˜์—ฌ ์œ„์กฐ ๋œ LocalStorageService๋ฅผ ์ œ๊ณตํ•จ์œผ๋กœ์จ ์‰ฝ๊ฒŒ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ๋‹ค.

class FakeLocalStorage {
  get(key) {
   return [{ name: 'Lyon' }, { name: 'London' }];
  }
}

provide๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ RaceService๋ฅผ ํ…Œ์ŠคํŠธ๋กœ ํ•  ์ˆ˜ ์žˆ๋‹ค.

import { TestBed } from '@angular/core/testing';
describe('RaceService', () => {
  beforeEach(() => TestBed.configureTestingModule({
   providers: [
   { provide: LocalStorageService, useClass: FakeLocalStorage },
   RaceService
   ]
  }));
  it('should return 2 races from localStorage', () => {
   const service = TestBed.get(RaceService);
   const races = service.list();
   expect(races.length).toBe(2);
  });
});

ํ•˜์ง€๋งŒ ์ด ํ…Œ์ŠคํŠธ์— ์™„์ „ํžˆ ๋งŒ์กฑํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. ๊ฐ€์งœ ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ์ง€๋ฃจํ•˜๊ณ  ์žฌ์Šค๋ฏผ์€ ์„œ๋น„์Šค๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  ๊ตฌํ˜„์„ ๊ฐ€์งœ๋กœ ๋Œ€์ฒดํ•˜๋Š” ๊ฒƒ์„ ๋„์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ get () ๋ฉ”์„œ๋“œ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ํ‚ค์ธ 'race'์™€ ํ•จ๊ป˜ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

import { TestBed } from '@angular/core/testing';
describe('RaceService', () => {
  const localStorage = jasmine.createSpyObj('LocalStorageService', ['get']);
  beforeEach(() => TestBed.configureTestingModule({
   providers: [
   { provide: LocalStorageService, useValue: localStorage },
   RaceService
   ]
  }));
  it('should return 2 races from localStorage', () => {
   localStorage.get.and.returnValue([{ name: 'Lyon' }, { name: 'London' }]);
   const service = TestBed.get(RaceService);
   const races = service.list();
   expect(races.length).toBe(2);
   expect(localStorage.get).toHaveBeenCalledWith('races');
  });
});

Code-driven

AngularJS 1.x์—์„œ๋Š” ์ฃผ๋กœ ํ…œํ”Œ๋ฆฟ์— ์–‘์‹์„ ์ž‘์„ฑํ•ด์•ผ ํ–ˆ๋‹ค. Angular 2๋Š” ๋ช…๋ นํ˜•์ด ์•„๋‹ˆ๋ผ ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ์–‘์‹์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค. ์ด์ œ ์ฝ”๋“œ์—์„œ ์ง์ ‘ ์–‘์‹์„ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋” ์žฅํ™ฉํ•˜์ง€๋งŒ ๋” ๊ฐ•๋ ฅํ•˜๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ ์ฝ”๋“œ์—์„œ ์–‘์‹์„ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด FormControl ๋ฐ FormGroup์— ๋Œ€ํ•ด ์„ค๋ช…ํ•œ ์ถ”์ƒํ™”๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

์ด๋Ÿฌํ•œ ๊ธฐ๋ณธ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑ ์š”์†Œ์— ์–‘์‹์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ƒˆ๋กœ์šด FormControl() ๋˜๋Š” ์ƒˆ FormGroup()์„ ์ž‘์„ฑํ•˜๋Š” ๋Œ€์‹  ์šฐ๋ฆฌ๊ฐ€ ์‚ฝ์ž… ํ•  ์ˆ˜์žˆ๋Š” ๋„์šฐ๋ฏธ ํด๋ž˜์Šค ์ธ FormBuilder๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค.

import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';
@Component({
  selector: 'ns-register',
  templateUrl: 'register-form.component.html'
})
export class RegisterFormComponent {
  constructor(fb: FormBuilder) {
   // we will have to build the form
  }
  register() {
   // we will have to handle the submission
  }
}

FormBuilder๋Š” ํ—ฌํผ ํด๋ž˜์Šค์ด๋ฉฐ ์ปจํŠธ๋กค๊ณผ ๊ทธ๋ฃน์„ ๋งŒ๋“œ๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ๋‹ค. ๊ฐ„๋‹จํžˆ ์‹œ์ž‘ํ•˜๊ณ  ๋‘ ๊ฐœ์˜ ์ปจํŠธ๋กค, ์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ์•”ํ˜ธ๋กœ ์ž‘์€ ํผ์„ ๋งŒ๋“ ๋‹ค.

import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
  selector: 'ns-register',
  templateUrl: 'register-form.component.html'
})
export class RegisterFormComponent {
  userForm: FormGroup;
  constructor(fb: FormBuilder) {
   this.userForm = fb.group({
   username: '',
   password: ''
   });
  }
  register() {
   // we will have to handle the submission
  }
}

์šฐ๋ฆฌ๋Š” ๋‘ ๊ฐœ์˜ ์ปจํŠธ๋กค์ด์žˆ๋Š” form์„ ๋งŒ๋“ค์—ˆ๋‹ค. ๊ฐ ์ปจํŠธ๋กค์€ ๊ฐ’ ''์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. FormBuilder ํ—ฌํผ ๋ฉ”์„œ๋“œ control()๋ฅผ ์ด ๋ฌธ์ž์—ด์„ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•˜๋‹ค. ์ƒˆ FormControl('') ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค. ๋ฌธ์ž์—ด์€ ์–‘์‹์— ํ‘œ์‹œ ํ•  ์ดˆ๊ธฐ ๊ฐ’์„ ๋‚˜ํƒ€ ๋‚ธ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋น„์–ด์žˆ์–ด ์ž…๋ ฅ์ด ๋น„์–ด์žˆ๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์˜ˆ๋ฅผ ๋“ค์–ด ๊ธฐ์กด ์—”ํ‹ฐํ‹ฐ๋ฅผ ํŽธ์ง‘ํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์—ฌ๊ธฐ์„œ๋„ ๊ฐ€์น˜๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค. ๋„์šฐ๋ฏธ ๋ฉ”์„œ๋“œ๋Š” ๋‹ค๋ฅธ ํŠน์ • ํŠน์„ฑ์„ ๊ฐ€์งˆ ์ˆ˜๋„ ์žˆ๋‹ค.

์šฐ๋ฆฌ๋Š” register ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. ์•ž์„œ ๋ณด์•˜ ๋“ฏ์ด FormGroup ๊ฐ์ฒด์—๋Š” value ์†์„ฑ์ด ์žˆ์œผ๋ฏ€๋กœ ๋‹จ์ˆœํžˆ

import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
  selector: 'ns-register',
  templateUrl: 'register-form.component.html'
})
export class RegisterFormComponent {
  userForm: FormGroup;
  constructor(fb: FormBuilder) {
   this.userForm = fb.group({
   username: fb.control(''),
   password: fb.control('')
   });
  }
  register() {
   console.log(this.userForm.value);
  }
}

์ด์ œ ํ…œํ”Œ๋ฆฟ์—์„œ ์ผ๋ถ€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•œ๋‹ค. ์šฐ๋ฆฌ๋Š” "ํ…œํ”Œ๋ฆฟ ์ค‘์‹ฌ" ์–‘์‹์—์„œ ๋ณธ ์ง€์‹œ๋ฌธ ์ด์™ธ์˜ ์ง€์‹œ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ์ง€์‹œ๋ฌธ์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฃจํŠธ ๋ชจ๋“ˆ์—์„œ ๊ฐ€์ ธ์™€์•ผ ํ•˜๋Š” ReactiveFormsModule์— ์žˆ๋‹ค. ๊ทธ๊ฒƒ๋“ค์˜ ์ด๋ฆ„์€ "template-driven"ํผ์˜ ๊ฒฝ์šฐ์ฒ˜๋Ÿผ form ๋Œ€์‹  ์‹œ์ž‘๋œ๋‹ค.

formGroup ์ง€์‹œ๋ฌธ ๋•๋ถ„์— ์–‘์‹์„ userForm ๊ฐ์ฒด์— ๋ฐ”์ธ๋”ฉ ํ•ด์•ผ ํ•œ๋‹ค. ๊ฐ ์ž…๋ ฅ ํ•„๋“œ๋Š” formControlName ์ง€์‹œ๋ฌธ ๋•๋ถ„์— ์ปจํŠธ๋กค์— ๋ฐ”์ธ๋”ฉ ๋œ๋‹ค.

<h2>Sign up</h2>
<form (ngSubmit)="register()" [formGroup]="userForm">
  <div>
   <label>Username</label><input formControlName="username">
  </div>
  <div>
   <label>Password</label><input type="password" formControlName="password">
  </div>
  <button type="submit">Register</button>
</form>

์šฐ๋ฆฌ๋Š” ๊ตฌ์„ฑ ์š”์†Œ์˜ ์†์„ฑ ์ธ userForm ๊ฐ์ฒด๋ฅผ formGroup์— ๋ฐ”์ธ๋”ฉ ํ•˜๋ ค๊ณ  ํ•˜๋ฏ€๋กœ ๊ด„ํ˜ธ ํ‘œ๊ธฐ๋ฒ• [formGroup] = "userForm"์„ ์‚ฌ์šฉํ•œ๋‹ค. ๊ฐ ์ž…๋ ฅ์€ ๋ฐ”์ธ๋”ฉ ๋œ ์ปจํŠธ๋กค์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์„ ์‚ฌ์šฉํ•˜์—ฌ formControlName ์ง€์‹œ๋ฌธ์„ ์ˆ˜์‹ ํ•œ๋‹ค. ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ด๋ฆ„์„ ์ง€์ •ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒ ํ•œ๋‹ค. ๊ฐ’์„ ์ „๋‹ฌํ•  ๋•Œ (๊ทธ๋ฆฌ๊ณ  ํ‘œํ˜„์‹์ด ์•„๋‹Œ ๊ฒฝ์šฐ) formControlName์„ []๋กœ ๋ฌถ์ง€ ์•Š๋Š”๋‹ค.

์ œ์ถœ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ์„ ํƒํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํฌํ•จ ๋œ ๊ฐ์ฒด๊ฐ€ ๊ธฐ๋ก๋œ๋‹ค! ํ•„์š”ํ•œ ๊ฒฝ์šฐ setValue()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ์—์„œ FormControl์˜ ๊ฐ’์„ ์—…๋ฐ์ดํŠธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormControl } from '@angular/forms';
@Component({
  selector: 'ns-register',
  templateUrl: 'register-form.component.html',
})
export class RegisterFormComponent {
  usernameCtrl: FormControl;
  passwordCtrl: FormControl;
  userForm: FormGroup;
  constructor(fb: FormBuilder) {
   this.usernameCtrl = fb.control('');
   this.passwordCtrl = fb.control('');
   this.userForm = fb.group({
   username: this.usernameCtrl,
   password: this.passwordCtrl
   });
  }
  reset() {
   this.usernameCtrl.setValue('');
   this.passwordCtrl.setValue('');
  }
  register() {
   console.log(this.userForm.value);
  }
}

Adding some validation

์ผ๋ฐ˜์ ์œผ๋กœ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” ์–‘์‹ ์ž‘์„ฑ์˜ ํฐ ๋ถ€๋ถ„์ด๋‹ค. ์ผ๋ถ€ ํ•„๋“œ๋Š” ํ•„์ˆ˜์ด๊ณ  ์ผ๋ถ€๋Š” ์„œ๋กœ ์˜์กดํ•˜๊ณ  ์ผ๋ถ€๋Š” ํŠน์ • ํ˜•์‹์ด์–ด์•ผ ํ•˜๋ฉฐ ์ผ๋ถ€๋Š” X๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜ ์ž‘์€ ๊ฐ’์„ ๊ฐ€์ ธ์„œ๋Š” ์•ˆ๋œ๋‹ค. ๊ธฐ๋ณธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ทœ์น™์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด์ž.

In a code-friven form

๋ชจ๋“  ํ•„๋“œ๊ฐ€ ํ•„์ˆ˜์ž„์„ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด ์šฐ๋ฆฌ๋Š” Validator๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค. validator๋Š” ์—๋Ÿฌ์˜ ๋งต์„ ๋Œ๋ ค ์ฃผ๋Š”์ง€, ์—๋Ÿฌ๋ฅผ ๊ฒ€์ถœํ•˜์ง€ ์•Š์•˜๋˜ ๊ฒฝ์šฐ๋Š” null๋ฅผ ๋Œ๋ ค์ค€๋‹ค.

ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” ๋ช‡ ๊ฐ€์ง€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

โ€ข Validators.required - ๊ฐ’์ด ๋น„์–ด ์žˆ์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค. โ€ข Validators.minLength (n) - ์ž…๋ ฅ ํ•œ ๊ฐ’์˜ ๋ฌธ์ž ์ˆ˜๊ฐ€ n ์ž ์ดํ•˜์ด์–ด์•ผ ํ•œ๋‹ค. โ€ข Validators.maxLength (n) - ์ž…๋ ฅ ๋œ ๊ฐ’์˜ ๋ฌธ์ž ์ˆ˜๋Š” ์ตœ๋Œ€ n ์ž ์ด์ƒ์ด์–ด์•ผ ํ•œ๋‹ค. โ€ข Validators.pattern (p) : ๊ฐ’์ด ์ •๊ทœ ํ‘œํ˜„์‹ p์™€ ์ผ์น˜ํ•ด์•ผ ํ•œ๋‹ค.

์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ๋Š” Validators.compose()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑ ๊ฐ€๋Šฅํ•˜๋ฉฐ FormControl ๋˜๋Š” FormGroup์— ์ ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋ชจ๋“  ํ•„๋“œ๋ฅผ ํ•„์ˆ˜๋กœ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ ์ปจํŠธ๋กค์— ํ•„์š”ํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‚ฌ์šฉ์ž ์ด๋ฆ„์ด ์ตœ์†Œ 3 ์ž ์ด์ƒ์ด์–ด์•ผ ํ•œ๋‹ค.

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
  selector: 'ns-register',
  templateUrl: 'register-form.component.html',
})
export class RegisterFormComponent {
  userForm: FormGroup;
  constructor(fb: FormBuilder) {
   this.userForm = fb.group({
   username: fb.control('', Validators.compose([Validators.required, Validators
.minLength(3)])),
   password: fb.control('', Validators.required)
   });
  }
  register() {
   console.log(this.userForm.value);
  }
}

In a template-driven form

ํ…œํ”Œ๋ฆฟ ์ค‘์‹ฌ ์–‘์‹์— ํ•„์ˆ˜ ์ž…๋ ฅ๋ž€์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ๋„ ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋‹ค. ํ•„์š”ํ•œ ์†์„ฑ์„ ์ž…๋ ฅ์— ์ถ”๊ฐ€ํ•˜๊ธฐ ๋งŒํ•˜๋ฉด ๋œ๋‹ค. required๋Š” ์ œ๊ณต๋œ ์ง€์‹œ๋ฌธ์ด๋ฉฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ์ด ํ•„๋“œ์— ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค. minlength์™€ maxlength์™€ ๊ฐ™์€ ์˜๋ฏธ์ด๋‹ค.

<h2>Sign up</h2>
<form (ngSubmit)="register(userForm.value)" #userForm="ngForm">
  <div>
   <label>Username</label><input name="username" ngModel required minlength="3">
  </div>
  <div>
   <label>Password</label><input type="password" name="password" ngModel required>
  </div>
  <button type="submit">Register</button>
</form>

Errors and submission

๋ฌผ๋ก  ์‚ฌ์šฉ์ž๊ฐ€ ์˜ค๋ฅ˜๊ฐ€ ๋‚จ์•„์žˆ๋Š” ๋™์•ˆ ์–‘์‹์„ ์ œ์ถœํ•  ์ˆ˜ ์—†์–ด์•ผํ•˜๋ฉฐ ์˜ค๋ฅ˜๊ฐ€ ์™„๋ฒฝํ•˜๊ฒŒ ํ‘œ์‹œ ๋˜์–ด์•ผ ํ•œ๋‹ค. ์˜ˆ์ œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋„ ์–‘์‹์„ ์ œ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค. ์•„๋งˆ๋„ ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๋ญ”๊ฐ€ ํ•  ์ˆ˜ ์žˆ์„๊นŒ?

disabled ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฒ„ํŠผ์„ ์‰ฝ๊ฒŒ ๋น„ํ™œ์„ฑํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ ํ˜„์žฌ ์–‘์‹์˜ ์ƒํƒœ๋ฅผ ๋ฐ˜์˜ํ•˜๋Š” ํ‘œํ˜„์‹์„ ์ œ๊ณตํ•ด์•ผ ํ•œ๋‹ค.

Errors and submission in a code-driven form

FormGroup ์œ ํ˜•์˜ userForm ํ•„๋“œ๋ฅผ ์šฐ๋ฆฌ ๊ตฌ์„ฑ ์š”์†Œ์— ์ถ”๊ฐ€ํ–ˆ๋‹ค. ์ด ํ•„๋“œ๋Š” ์–‘์‹ ๋ฐ ํ•„๋“œ ์ƒํƒœ ๋ฐ ์˜ค๋ฅ˜์— ๋Œ€ํ•œ ์™„์ „ํ•œ ๋ทฐ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์–‘์‹์ด ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์–‘์‹ ์ œ์ถœ์„ ์‚ฌ์šฉ ์ค‘์ง€ ํ•  ์ˆ˜ ์žˆ๋‹ค.

<h2>Sign up</h2>
<form (ngSubmit)="register()" [formGroup]="userForm">
  <div>
   <label>Username</label><input formControlName="username">
  </div>
  <div>
   <label>Password</label><input type="password" formControlName="password">
  </div>
  <button type="submit" [disabled]="!userForm.valid">Register</button>
</form>

๋งˆ์ง€๋ง‰ ์ค„์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด disabled๋ฅผ userForm์˜ ์œ ํšจํ•œ ์†์„ฑ์— ์—ฐ๊ฒฐํ•˜๊ธฐ ๋งŒํ•˜๋ฉด ๋œ๋‹ค. ์ด์ œ ๋ชจ๋“  ์ปจํŠธ๋กค์ด ์œ ํšจ ํ•  ๋•Œ๋งŒ ์ œ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์–‘์‹์„ ์ œ์ถœํ•  ์ˆ˜ ์—†๋Š” ์ด์œ ๋ฅผ ์ดํ•ดํ•˜๋„๋ก ๋•๊ธฐ ์œ„ํ•ด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•ด์•ผ ํ•œ๋‹ค.

<h2>Sign up</h2>
<form (ngSubmit)="register()" [formGroup]="userForm">
  <div>
   <label>Username</label><input formControlName="username">
   <div *ngIf="userForm.get('username').hasError('required')">Username is required</div>
   <div *ngIf="userForm.get('username').hasError('minlength')">Username should be 3
characters min</div>
  </div>
  <div>
   <label>Password</label><input type="password" formControlName="password">
   <div *ngIf="userForm.get('password').hasError('required')">Password is required</div>
  </div>
  <button type="submit" [disabled]="!userForm.valid">Register</button>
</form>

ํ•„๋“œ๊ฐ€ ๋น„์–ด ์žˆ์œผ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ํ‘œ์‹œ๋˜๊ณ  ๊ฐ’์ด ์žˆ์œผ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์–‘์‹์ด ํ‘œ์‹œ๋˜๋ฉด ๋ฐ”๋กœ ํ‘œ์‹œ๋œ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ๋•Œ๊นŒ์ง€ ์ˆจ๊ธธ ์ˆ˜ ์žˆ์„๊นŒ?

<h2>Sign up</h2>
<form (ngSubmit)="register()" [formGroup]="userForm">
  <div>
   <label>Username</label><input formControlName="username">
   <div *ngIf="userForm.get('username').dirty &&
userForm.get('username').hasError('required')">
   Username is required
   </div>
   <div *ngIf="userForm.get('username').dirty &&
userForm.get('username').hasError('minlength')">
   Username should be 3 characters min
   </div>
  </div>
  <div>
   <label>Password</label><input type="password" formControlName="password">
   <div *ngIf="userForm.get('password').dirty &&
userForm.get('password').hasError('required')">
   Password is required
   </div>
  </div>
  <button type="submit" [disabled]="!userForm.valid">Register</button>
</form>

๊ทธ๋Ÿฌ๋‚˜ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๊ฐ ์ปจํŠธ๋กค์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

@Component({
  selector: 'ns-register',
  templateUrl: 'register-form.component.html'
})
export class RegisterFormComponent {
  userForm: FormGroup;
  usernameCtrl: FormControl;
  passwordCtrl: FormControl;
  constructor(fb: FormBuilder) {
   this.usernameCtrl = fb.control('', Validators.required);
   this.passwordCtrl = fb.control('', Validators.required);
   this.userForm = fb.group({
   username: this.usernameCtrl,
   password: this.passwordCtrl
   });
  }
  register() {
   console.log(this.userForm.value);
  }
}
<h2>Sign up</h2>
<form (ngSubmit)="register()" [formGroup]="userForm">
  <div>
   <label>Username</label><input formControlName="username">
   <div *ngIf="usernameCtrl.dirty && usernameCtrl.hasError('required')">Username is
required</div>
   <div *ngIf="usernameCtrl.dirty && usernameCtrl.hasError('minlength')">Username should
be 3 characters min</div>
  </div>
  <div>
   <label>Password</label><input type="password" formControlName="password">
   <div *ngIf="passwordCtrl.dirty && passwordCtrl.hasError('required')">Password is
required</div>
  </div>
  <button type="submit" [disabled]="!userForm.valid">Register</button>
</form>

Errors and submission in a template-driven form

ํ…œํ”Œ๋ฆฟ ์ค‘์‹ฌ ์–‘์‹์—์„œ๋Š” FormGroup์„ ์ฐธ์กฐํ•˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ์— ํ•„๋“œ๊ฐ€ ์—†์ง€๋งŒ ์„œ์‹ ์ง€์‹œ๋ฌธ์—์„œ ๋‚ด ๋ณด๋‚ธ NgForm ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ํ…œํ”Œ๋ฆฟ์˜ ๋กœ์ปฌ ๋ณ€์ˆ˜๋ฅผ ์ด๋ฏธ ์„ ์–ธ ํ•˜์˜€๋‹ค. ๋‹ค์‹œ ํ•œ๋ฒˆ,์ด ๋ณ€์ˆ˜๋Š” ํผ์˜ ์ƒํƒœ๋ฅผ ์•Œ ์ˆ˜ ์žˆ๊ณ  ์ปจํŠธ๋กค์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

<h2>Sign up</h2>
<form (ngSubmit)="register(userForm.value)" #userForm="ngForm">
  <div>
   <label>Username</label><input name="username" ngModel required>
  </div>
  <div>
   <label>Password</label><input type="password" name="password" ngModel required>
  </div>
  <button type="submit" [disabled]="!userForm.valid">Register</button>
</form>

์ด์ œ ๊ฐ ํ•„๋“œ์˜ ์˜ค๋ฅ˜๋ฅผ ํ‘œ์‹œํ•ด์•ผ ํ•œ๋‹ค. form ์ง€์‹œ๋ฌธ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฐ ์ปจํŠธ๋กค์€ FormControl ๊ฐ์ฒด๋ฅผ ๋‚ด ๋ณด๋‚ธ๋‹ค. ๋”ฐ๋ผ์„œ ์˜ค๋ฅ˜์— ์•ก์„ธ์Šคํ•˜๋Š” ๋กœ์ปฌ ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

<h2>Sign up</h2>
<form (ngSubmit)="register(userForm.value)" #userForm="ngForm">
  <div>
   <label>Username</label><input name="username" ngModel required #username="ngModel">
   <div *ngIf="username.control.dirty && username.control.hasError('required')">Username
is required</div>
  </div>
  <div>
   <label>Password</label><input type="password" name="password" ngModel required
#password="ngModel">
   <div *ngIf="password.control.dirty && password.control.hasError('required')">Password
is required</div>
  </div>
  <button type="submit" [disabled]="!userForm.valid">Register</button>
</form>

Add some style

์–‘์‹์„ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์— ๊ด€๊ณ„์—†์ด Angular 2๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฉ‹์ง„ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๊ฐ ํ•„๋“œ (๋ฐ ์–‘์‹)์— CSS ํด๋ž˜์Šค๋ฅผ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ ๋ฐ ์ œ๊ฑฐํ•˜์—ฌ ์‹œ๊ฐ์  ์Šคํƒ€์ผ์„ ์ถ”๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ํ•„๋“œ๋Š” ๋ฐœ๋ฆฌ ๋ฐ์ดํ„ฐ ์ค‘ ํ•˜๋‚˜๊ฐ€ ์‹คํŒจํ•˜๋ฉด ng-invalid ํด๋ž˜์Šค๋ฅผ ๊ฐ€์ง€๋ฉฐ, ๋ชจ๋“  valid ๋ฐ์ดํ„ฐ๊ฐ€ ์„ฑ๊ณตํ•˜๋ฉด ng-valid ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง„๋‹ค. ์ฆ‰, ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์— ์‹คํŒจํ•œ ํ•„๋“œ ์ฃผ์œ„์— ๋ฉ‹์ง„ ๋นจ๊ฐ• ํ…Œ๋‘๋ฆฌ์™€ ๊ฐ™์€ ์Šคํƒ€์ผ์„ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋‹ค.

<style>
  input.ng-invalid {
   border: 3px red solid;
  }
</style>
<h2>Sign up</h2>
<form (ngSubmit)="register(userForm.value)" #userForm="ngForm">
  <div>
   <label>Username</label><input name="username" ngModel required>
  </div>
  <div>
   <label>Password</label><input type="password" name="password" ngModel required>
  </div>
  <button type="submit" [disabled]="!userForm.valid">Register</button>
</form>

๋˜ ๋‹ค๋ฅธ ์œ ์šฉํ•œ CSS ํด๋ž˜์Šค๋Š” ng-dirty์ด๋ฉฐ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ’์„ ๋ณ€๊ฒฝ ํ•œ ๊ฒฝ์šฐ ํ‘œ์‹œ๋œ๋‹ค. ๊ทธ ๋ฐ˜๋Œ€๋Š” ng-pristine์ด๋ฉฐ, ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒฐ์ฝ” ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์œผ๋ฉด ๋‚˜ํƒ€๋‚œ๋‹ค. ๋‚˜๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ’์„ ํ•œ ๋ฒˆ ์ด์ƒ ๋ณ€๊ฒฝ ํ•œ ๊ฒฝ์šฐ์—๋งŒ ๋นจ๊ฐ„์ƒ‰ ํ…Œ๋‘๋ฆฌ๋ฅผ ํ‘œ์‹œํ•œ๋‹ค.

<style>
  input.ng-invalid.ng-dirty {
   border: 3px red solid;
  }
</style>
<h2>Sign up</h2>
<form (ngSubmit)="register(userForm.value)" #userForm="ngForm">
  <div>
   <label>Username</label><input name="username" ngModel required>
  </div>
  <div>
   <label>Password</label><input type="password" name="password" ngModel required>
  </div>
  <button type="submit" [disabled]="!userForm.valid">Register</button>
</form>

๋งˆ์ง€๋ง‰์œผ๋กœ ๋งˆ์ง€๋ง‰ CSS ํด๋ž˜์Šค ์ธ ng-touch๊ฐ€ ์žˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ํ•„๋“œ์— ์ ์–ด๋„ ํ•œ ๋ฒˆ ์ด์ƒ ๋“ค์–ด๊ฐ€๊ณ  ๋‚˜๊ฐ€๋ฉด (์‹ฌ์ง€์–ด ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์•˜๋”๋ผ๋„) ๋‚˜ํƒ€๋‚œ๋‹ค. ๊ทธ ๋ฐ˜๋Œ€๋Š” ์•„๋ฌด๋Ÿฐ ๋ณ€ํ™”๊ฐ€ ์—†๋‹ค. ์–‘์‹์„ ์ฒ˜์Œ์œผ๋กœ ํ‘œ์‹œ ํ•  ๋•Œ ํ•„๋“œ์—๋Š” ๋Œ€๊ฐœ CSS ํด๋ž˜์Šค ng-pristine ng-untouched ng-invalid๊ฐ€ ์žˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•˜๊ณ  ํ•„๋“œ๋ฅผ ๋– ๋‚  ๋•Œ ng-pristine ng-touched ng-invalid๋กœ ์ „ํ™˜๋œ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์—ฌ์ „ํžˆ ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฐ’์œผ๋กœ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๊ณ  ng-dirty๊ฐ€ ng-invalid๋กœ ์„ค์ • ๋œ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ๊ฐ’์ด ์œ ํšจํ•œ ๊ฒฝ์šฐ๋Š” ng-dirty ng-touch ng-valid๋กœ ๋ณ€ํ™˜ ๋ ๊ฒƒ์ด๋‹ค.

Creating a custom validator

ํฌ๋‹ˆ ๋ ˆ์ด์Šค๋Š” ์ค‘๋…์„ฑ ๊ฒŒ์ž„์ด๋ฏ€๋กœ 18 ์„ธ ์ด์ƒ์ธ ๊ฒฝ์šฐ์—๋งŒ ๋“ฑ๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ˆ˜๋กœ ์‹ค์ˆ˜ํ•˜์ง€ ์•Š์•˜์Œ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‘ ๋ฒˆ ์ž…๋ ฅํ•ด์•ผ ํ•œ๋‹ค. ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ? ์šฐ๋ฆฌ๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ๋งŒ๋“ ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด FormControl์„ ์‚ฌ์šฉํ•˜๊ณ  ๊ฐ’์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ํ†ต๊ณผ๋˜๋ฉด null์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

const isOldEnough = (control: FormControl) => {
  // control is a date input, so we can build the Date from the value
  const birthDatePlus18 = new Date(control.value);
  birthDatePlus18.setFullYear(birthDatePlus18.getFullYear() + 18);
  return birthDatePlus18 < new Date() ? null : { tooYoung: true };
};

๊ฒ€์ฆ ๋ฐฉ๋ฒ•์€ ๋งค์šฐ ์‰ฝ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ปจํŠธ๋กค์˜ ๊ฐ€์น˜๋ฅผ ์ทจํ•˜๊ณ , ๋‚ ์งœ๋ฅผ ๋งŒ๋“ค๊ณ , 18 ๋ฒˆ์งธ ์ƒ์ผ์ด ์ „์— ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ 'tooYoung'ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ ํ•œ๋‹ค. ์ด์ œ ์ด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ํฌํ•จํ•ด์•ผ ํ•œ๋‹ค.

Using a validator in a code-driven form

FormBuilder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ์ด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํผ์— ์ƒˆ ์ปจํŠธ๋กค์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

import { Component } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
  selector: 'ns-register',
  templateUrl: 'register-form.component.html'
})
export class RegisterFormComponent {
  usernameCtrl: FormControl;
  passwordCtrl: FormControl;
  birthdateCtrl: FormControl;
  userForm: FormGroup;
  static isOldEnough(control: FormControl) {
   // control is a date input, so we can build the Date from the value
   const birthDatePlus18 = new Date(control.value);
   birthDatePlus18.setFullYear(birthDatePlus18.getFullYear() + 18);
   return birthDatePlus18 < new Date() ? null : { tooYoung: true };
  }
  constructor(fb: FormBuilder) {
   this.usernameCtrl = fb.control('', Validators.required);
   this.passwordCtrl = fb.control('', Validators.required);
   this.birthdateCtrl = fb.control('', Validators.compose([Validators.required,
RegisterFormComponent.isOldEnough]));
   this.userForm = fb.group({
   username: this.usernameCtrl,
   password: this.passwordCtrl,
   birthdate: this.birthdateCtrl
   });
  }
  register() {
   console.log(this.userForm.value);
  }
}

๋ณด์‹œ๋‹ค์‹œํ”ผ, ์šฐ๋ฆฌ๋Š” ๋‘ ๊ฐœ์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด ์ œ์–ด ์ƒ๋…„์›”์ผ์„ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค. ์ฒซ ๋ฒˆ์งธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ ๋‹ค๋ฅธ ํด๋ž˜์Šค๋Š” isOldEnough ํด๋ž˜์Šค์˜ ์ •์  ๋ฉ”์„œ๋“œ ์ด๋‹ค. ๋ฌผ๋ก  ์ด ๋ฉ”์†Œ๋“œ๋Š” ์›ํ•  ๊ฒฝ์šฐ ๋‹ค๋ฅธ ํด๋ž˜์Šค์— ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค. (์˜ˆ : ์ •์  ๋ฉ”์†Œ๋“œ)

ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์–‘์‹์— ์˜ค๋ฅ˜๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋ง ๊ฒƒ!!!

<h2>Sign up</h2>
<form (ngSubmit)="register()" [formGroup]="userForm">
  <div>
   <label>Username</label><input formControlName="username">
   <div *ngIf="usernameCtrl.dirty && usernameCtrl.hasError('required')">Username is
required</div>
  </div>
  <div>
   <label>Password</label><input type="password" formControlName="password">
   <div *ngIf="passwordCtrl.dirty && passwordCtrl.hasError('required')">Password is
required</div>
  </div>
  <div>
   <label>Birth date</label><input type="date" formControlName="birthdate">
   <div *ngIf="birthdateCtrl.dirty">
   <div *ngIf="birthdateCtrl.hasError('required')">Birth date is required</div>
   <div *ngIf="birthdateCtrl.hasError('tooYoung')">You're way too young to be betting
on pony races</div>
   </div>
  </div>
  <button type="submit" [disabled]="!userForm.valid">Register</button>
</form>

Using a validator in a template-driven form

ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜ ํผ์— ์‚ฌ์šฉ์ž ์ •์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ... ํ…œํ”Œ๋ฆฟ์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ์ž…๋ ฅ์— ์ ์šฉ ํ•  ์‚ฌ์šฉ์ž ์ง€์ • ์ง€์‹œ๋ฌธ์„ ์ž‘์„ฑํ•ด์•ผ ํ•˜์ง€๋งŒ ์ •์งํ•˜๊ฒŒ๋Š” "์ฝ”๋“œ ๊ธฐ๋ฐ˜" ์–‘์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ๋” ์‰ฝ๋‹ค. ๋˜๋Š” ๋‘ ์„ธ๊ณ„์˜ ์žฅ์ ์„ ๊ฒฐํ•ฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.

Combining template-based and code-based approaches for validation

์‚ฌ์šฉ์ž ์ •์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ œ์™ธํ•˜๊ณ  "ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜"์œผ๋กœ ๋ชจ๋“  ์ž‘์—…์„ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! ์šฐ๋ฆฌ๋Š” ๋ณด๊ธฐ์— ์ด๋Ÿฐ ์‹์œผ๋กœ ๋๋‚  ๊ฒƒ์ด๋‹ค.

<label>Username</label><input name="username" [formControl]="usernameCtrl" [(ngModel)]=
"user.username" required>
<div class="error" *ngIf="usernameCtrl.dirty && usernameCtrl.hasError('notAllowed')"
>Username is not allowed</div>
usernameCtrl = new FormControl('', RegisterFormComponent.usernameValidator);
static usernameValidator(control: FormControl) {
  return control.value !== 'admin' ? null : { notAllowed: true };
}

์ด๊ฒƒ์€ ์ •๋ง ์ข‹์€ ์ ˆ์ถฉ์•ˆ ์ด๋‹ค.

โ€ข ๋ชจ๋“  ์–‘์‹์„ ์ฝ”๋“œ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค ํ•„์š”๋Š” ์—†๋‹ค. โ€ข ํ•„์ˆ˜ ๋ฐ usernameAllowed ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ์ง์ ‘ ๊ตฌ์„ฑ ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. Angular๊ฐ€ ์‚ฌ์šฉ์ž๋ฅผ ๋Œ€์‹  ํ•œ๋‹ค. โ€ข ์ˆœ์ˆ˜ํ•œ ์ฝ”๋“œ ์ค‘์‹ฌ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์•„๋‹Œ ์–‘๋ฐฉํ–ฅ ๋ฐ”์ธ๋”ฉ์ด ์—ฌ์ „ํžˆ ์žˆ๋‹ค.

์ด๋Š” ๊ฐ„๋‹จํ•œ ์‚ฌ์•ˆ๊ณผ ์–‘๋ฐฉํ–ฅ ๋ฐ”์ธ๋”ฉ์„ ์œ„ํ•œ ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜ ์ ‘๊ทผ ๋ฐฉ์‹๊ณผ ๋งž์ถค ๊ฒ€์ฆ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ฝ”๋“œ ๊ธฐ๋ฐ˜ ์ ‘๊ทผ ๋ฐฉ์‹์„ ๊ฒฐํ•ฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

Grouping fields

์ง€๊ธˆ๊นŒ์ง€ ์šฐ๋ฆฌ๋Š” ํ•˜๋‚˜์˜ ๊ทธ๋ฃน, ์ฆ‰ ์™„๋ฒฝํ•œ ํ˜•ํƒœ๋ฅผ ๊ฐ€์กŒ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๋ฃน ๋‚ด์—์„œ ๊ทธ๋ฃน์„ ์„ ์–ธ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” ์ฃผ์†Œ์™€ ๊ฐ™์€ ํ•„๋“œ ๊ทธ๋ฃน์„ ๊ฒ€์ฆํ•˜๊ฑฐ๋‚˜ ์•ž์˜ ์˜ˆ์˜ ๊ฒฝ์šฐ์™€ ๊ฐ™์ด ์•”ํ˜ธ ๋ฐ ํ™•์ธ์ด ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ๋งค์šฐ ์œ ์šฉํ•˜๋‹ค. ํ•ด๊ฒฐ์ฑ…์€ ์ฝ”๋“œ ์ฃผ๋„ํ˜•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค (์•ž์—์„œ ๋ณด์•˜ ๋“ฏ์ด ์›ํ•  ๊ฒฝ์šฐ ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜ ์–‘์‹๊ณผ ๊ฒฐํ•ฉ ํ•  ์ˆ˜ ์žˆ๋‹ค). ๋จผ์ € ์ƒˆ๋กœ์šด ํ•„๋“œ ์ธ passwordForm์„ ๋งŒ๋“ค๊ณ  ๋‘ ํ•„๋“œ๋ฅผ userForm ๊ทธ๋ฃน์— ์ถ”๊ฐ€ ํ•œ๋‹ค.

import { Component } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
  selector: 'ns-register',
  templateUrl: 'register-form.component.html'
})
export class RegisterFormComponent {
  passwordForm: FormGroup;
  userForm: FormGroup;
  usernameCtrl: FormControl;
  passwordCtrl: FormControl;
  confirmCtrl: FormControl;
  static passwordMatch(control: FormGroup) {
   const password = control.controls['password'].value;
   const confirm = control.controls['confirm'].value;
   return password === confirm ? null : { matchingError: true };
  }
  constructor(fb: FormBuilder) {
   this.usernameCtrl = fb.control('', Validators.required);
   this.passwordCtrl = fb.control('', Validators.required);
   this.confirmCtrl = fb.control('', Validators.required);
   this.passwordForm = fb.group(
   { password: this.passwordCtrl, confirm: this.confirmCtrl },
   { validator: RegisterFormComponent.passwordMatch }
   );
   this.userForm = fb.group({ username: this.usernameCtrl, passwordForm: this
.passwordForm });
  }
  register() {
   console.log(this.userForm.value);
  }
}

๋ณด์‹œ๋‹ค์‹œํ”ผ, ํ•„๋“œ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ณ€๊ฒฝ ๋  ๋•Œ๋งˆ๋‹ค ํ˜ธ์ถœ๋˜๋Š” validModifier ๊ทธ๋ฃน์— passwordMatch๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค. formGroupName ์ง€์‹œ์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ ์–‘์‹์„ ๋ฐ˜์˜ํ•˜๋„๋ก ํ…œํ”Œ๋ฆฟ์„ ์—…๋ฐ์ดํŠธ ํ•˜์ž.

<h2>Sign up</h2>
<form (ngSubmit)="register()" [formGroup]="userForm">
  <div>
   <label>Username</label><input formControlName="username">
   <div *ngIf="usernameCtrl.dirty && usernameCtrl.hasError('required')">Username is
required</div>
  </div>
  <div formGroupName="passwordForm">
   <div>
   <label>Password</label><input type="password" formControlName="password">
   <div *ngIf="passwordCtrl.dirty && passwordCtrl.hasError('required')">Password is
required</div>
   </div>
   <div>
   <label>Confirm password</label><input type="password" formControlName="confirm">
   <div *ngIf="confirmCtrl.dirty && confirmCtrl.hasError('required')">Confirm your
password</div>
   </div>
   <div *ngIf="passwordForm.dirty && passwordForm.hasError('matchingError')">Your
password does not match</div>
  </div>
  <button type="submit" [disabled]="!userForm.valid">Register</button>
</form>

Reacting on changes

์ฝ”๋“œ ์ค‘์‹ฌ ์–‘์‹์„ ์‚ฌ์šฉํ•  ๋•Œ ๋งˆ์ง€๋ง‰์œผ๋กœ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ : ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ valueChanges๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ’ ๋ณ€๊ฒฝ์— ์‰ฝ๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฆฌ ์•กํ‹ฐ๋ธŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ FTW! ์˜ˆ๋ฅผ ๋“ค์–ด ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์›ํ•œ๋‹ค๊ณ  ๊ฐ€์ • ํ•ด๋ณด์ž. ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ•๋„ ํ‘œ์‹œ๊ธฐ๋ฅผ ํ‘œ์‹œํ•œ๋‹ค. ์šฐ๋ฆฌ๋Š” ์•”ํ˜ธ ๊ฐ’์ด ๋ณ€๊ฒฝ ๋  ๋•Œ๋งˆ๋‹ค ๊ธธ์ด๋ฅผ ๊ณ„์‚ฐํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

import { Component } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
@Component({
  selector: 'ns-register',
  templateUrl: 'register-form.component.html'
})
export class RegisterFormComponent {
  userForm: FormGroup;
  usernameCtrl: FormControl;
  passwordCtrl: FormControl;
  passwordStrength: number = 0;
  constructor(fb: FormBuilder) {
   this.usernameCtrl = fb.control('', Validators.required);
   this.passwordCtrl = fb.control('', Validators.required);
   this.userForm = fb.group({
   username: this.usernameCtrl,
   password: this.passwordCtrl
   });
   // we subscribe to every password change
   this.passwordCtrl.valueChanges
   // only recompute when the user stops typing for 400ms
   .debounceTime(400)
   // only recompute if the new value is different than the last
   .distinctUntilChanged()
   .subscribe(newValue => this.passwordStrength = newValue.length);
  }
  register() {
   console.log(this.userForm.value);
  }
}

์ด์ œ ๊ตฌ์„ฑ ์š”์†Œ ์ธ์Šคํ„ด์Šค์— passwordStrength ํ•„๋“œ๊ฐ€์žˆ์–ด์„œ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<h2>Sign up</h2>
<form (ngSubmit)="register()" [formGroup]="userForm">
  <div>
   <label>Username</label><input formControlName="username">
   <div *ngIf="usernameCtrl.dirty && usernameCtrl.hasError('required')">Username is
required</div>
  </div>
  <div>
   <label>Password</label><input type="password" formControlName="password">
   <div>Strength: {{passwordStrength}}</div>
   <div *ngIf="passwordCtrl.dirty && passwordCtrl.hasError('required')">Password is
required</div>
  </div>
  <button type="submit" [disabled]="!userForm.valid">Register</button>
</form>

์šฐ๋ฆฌ๋Š” RxJS ์šด์˜์ž๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ช‡ ๊ฐ€์ง€ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

โ€ข distinctTime (400)ํ•จ์ˆ˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ 400ms ๋™์•ˆ ํƒ€์ดํ•‘์„ ๋ฉˆ ์ถ”๋ฉด ๊ฐ’์„ ๋ฐฉ์ถœ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅ ํ•œ ๋ชจ๋“  ๊ฐ’์— ๋Œ€ํ•ด ์•”ํ˜ธ ๊ธธ์ด๋ฅผ ๊ณ„์‚ฐํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ์ปดํ“จํŒ…์— ์˜ค๋žœ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๊ฑฐ๋‚˜ HTTP ์š”์ฒญ์ด ์‹œ์ž‘๋˜๋ฉด ์ •๋ง ํฅ๋ฏธ๋กœ์šด ์ผ์ด๋‹ค.

โ€ข distinctUntilChanged ()๋Š” ์ž…๋ ฅ ๋œ ์ƒˆ ๊ฐ’์ด ๋งˆ์ง€๋ง‰ ๊ฐ’๊ณผ ๋‹ค๋ฅธ ๊ฒฝ์šฐ์—๋งŒ ๊ฐ’์„ ๋‚ด๋ณด๋‚ธ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ 'password'๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ํƒ€์ดํ•‘์ด ์ค‘์ง€๋œ๋‹ค๊ณ  ์ƒ์ƒํ•ด ๋ณด์•„๋ผ. ์šฐ๋ฆฌ๋Š” ๊ธธ์ด๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ƒˆ๋กœ์šด ๊ธ€์ž๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ๋นจ๋ฆฌ ์ œ๊ฑฐํ•œ๋‹ค (400ms ์ „). distinctTimewill์˜ ๋‹ค์Œ ์ด๋ฒคํŠธ๋Š” ๋‹ค์‹œ '์•”ํ˜ธ'์ž…๋‹ˆ๋‹ค. ์•”ํ˜ธ ๊ธธ์ด๋ฅผ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜๋Š” ๊ฒƒ์€ ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค! ์ด ์—ฐ์‚ฐ์ž๋Š” ๊ฐ’์„ ๋ฐฉ์ถœํ•˜์ง€ ์•Š์œผ๋ฉฐ ์šฐ๋ฆฌ์—๊ฒŒ ๋‹ค์‹œ ๊ณ„์‚ฐ์„ ์ €์žฅํ•œ๋‹ค.

RxJS๋Š” ๋‹น์‹ ์„ ์œ„ํ•ด ์ˆ˜ ๋งŽ์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๋ฐฉ๊ธˆ ํ•œ ๋‘ ์ค„๋กœ ์ฝ”๋”ฉ ํ•œ ๊ฒƒ์„ ์ƒ์ƒํ•ด๋ณด์•„๋ผ. ๋˜ํ•œ Http ์„œ๋น„์Šค๋Š” ๊ด€์ธก ๊ฐ€๋Šฅ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— HTTP ์ž‘์—…๊ณผ ์‰ฝ๊ฒŒ ๊ฒฐํ•ฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.

Summary

Angular 2 ํผ์„ ๋งŒ๋“œ๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค. :

โ€ข ํ•˜๋‚˜๋Š” ํ…œํ”Œ๋ฆฟ์—์„œ ๋ชจ๋“  ์„ค์ •์„ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๋‹น์‹ ์ด ๋ณธ๋Œ€๋กœ, ๊ทธ๊ฒƒ์€ ๊ฒ€์ฆ์„ ์œ„ํ•œ ์‚ฌ์šฉ์ž ์ง€์ • ์ง€์‹œ๋ฌธ์„ ํ•„์š”๋กœ ํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์€ ์–ด๋ ค์šธ ๊ฒƒ์ด๋‹ค. ์–‘์‹์€ ์˜ˆ๋ฅผ ๋“ค์–ด ํ•˜๋‚˜ ๋˜๋Š” ์—ฌ๋Ÿฌ ํ•„๋“œ๊ฐ€ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

โ€ข ํ•˜๋‚˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ๊ฑฐ์˜ ๋ชจ๋“  ์„ค์ •์„ ํ•œ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ํ•„์š”์— ๋”ฐ๋ผ ์—ฌ๋Ÿฌ ์ˆ˜์ค€์˜ ๊ทธ๋ฃน์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒ€์ฆ ๋ฐ ํ…Œ์ŠคํŠธ ์„ค์ •์„ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋ณต์žกํ•œ ์–‘์‹์„ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•œ ์„ ํƒ์˜ ๋ฌด๊ธฐ์ด๋‹ค. ๊ทธ๋ฃน์ด๋‚˜ ํ•„๋“œ์˜ ๋ณ€๊ฒฝ์—๋„ ๋ฐ˜์‘ ํ•  ์ˆ˜ ์žˆ๋‹ค.

โ€ข "ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜"์–‘์‹์˜ ๊ฐ„๊ฒฐํ•จ๊ณผ ํƒ€๋‹น์„ฑ์„ ๊ฒ€์ฆํ•˜๊ธฐ์œ„ํ•œ ใ€Œ์ฝ”๋“œ ์ค‘์‹ฌ 'ํผ์˜ ์ค‘๋ณต์„ ์–‘๋ฆฝ์‹œํ‚ค๊ธฐ

์ด๊ฒƒ์€ ์•„๋งˆ๋„ ๊ฐ€์žฅ ์‹ค์šฉ์ ์ธ ๋ฐฉ๋ฒ•์ด๋‹ค : ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜๊ณผ ์–‘๋ฐฉํ–ฅ ๋ฐ”์ธ๋”ฉ์„ ๋งˆ์Œ์— ๋“ค๋ฉด ํผ ๊ทธ๋ฃน๊ณผ ํผ ์ปจํŠธ๋กค์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์ฆ‰์‹œ (์‚ฌ์šฉ์ž ์ง€์ • ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์™€ ๋ฐ˜์‘์ ์ธ ๋™์ž‘์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋“ฑ) ๊ตฌ์„ฑ ์š”์†Œ ๋‚ด๋ถ€์˜ ํ•„์š”์„ฑ์„ ํŒ๋‹จํ•˜๊ณ  ์ ์ ˆํ•œ ์ง€์นจ์„ ์‚ฌ์šฉํ•˜์—ฌ input๊ณผ div๋ฅผ ๋ฐ”์ธ๋”ฉ ํ•œ๋‹ค.

Reference URL

PreviousCHAPTER 12. Testing your appNextCHAPTER 14. Send and receive data with Http

Last updated 5 years ago

Was this helpful?

Become a NINJA with Angular 2
Learn Angular 2
Angular 2 Component
An Introduction to Angular 2