oh-my-oop 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MCP_STRUCTURE.md +301 -0
- package/README.md +167 -0
- package/SCOPE.md +27 -0
- package/dist/application/discovery.d.ts +133 -0
- package/dist/application/discovery.js +62 -0
- package/dist/application/discovery.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +80 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js +32 -0
- package/dist/config.js.map +1 -0
- package/dist/domain/collaborators.d.ts +2 -0
- package/dist/domain/collaborators.js +26 -0
- package/dist/domain/collaborators.js.map +1 -0
- package/dist/domain/ids.d.ts +4 -0
- package/dist/domain/ids.js +23 -0
- package/dist/domain/ids.js.map +1 -0
- package/dist/domain/model.d.ts +71 -0
- package/dist/domain/model.js +2 -0
- package/dist/domain/model.js.map +1 -0
- package/dist/domain/schemas.d.ts +213 -0
- package/dist/domain/schemas.js +79 -0
- package/dist/domain/schemas.js.map +1 -0
- package/dist/domain/stereotypes.d.ts +7 -0
- package/dist/domain/stereotypes.js +65 -0
- package/dist/domain/stereotypes.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/io/classStore.d.ts +9 -0
- package/dist/io/classStore.js +39 -0
- package/dist/io/classStore.js.map +1 -0
- package/dist/io/collaborationStore.d.ts +7 -0
- package/dist/io/collaborationStore.js +32 -0
- package/dist/io/collaborationStore.js.map +1 -0
- package/dist/io/design.d.ts +3 -0
- package/dist/io/design.js +22 -0
- package/dist/io/design.js.map +1 -0
- package/dist/io/designIndex.d.ts +6 -0
- package/dist/io/designIndex.js +49 -0
- package/dist/io/designIndex.js.map +1 -0
- package/dist/io/frontmatter.d.ts +8 -0
- package/dist/io/frontmatter.js +31 -0
- package/dist/io/frontmatter.js.map +1 -0
- package/dist/io/history.d.ts +10 -0
- package/dist/io/history.js +38 -0
- package/dist/io/history.js.map +1 -0
- package/dist/io/useCaseStore.d.ts +8 -0
- package/dist/io/useCaseStore.js +36 -0
- package/dist/io/useCaseStore.js.map +1 -0
- package/dist/io/workspace.d.ts +14 -0
- package/dist/io/workspace.js +82 -0
- package/dist/io/workspace.js.map +1 -0
- package/dist/oop/commands.d.ts +446 -0
- package/dist/oop/commands.js +319 -0
- package/dist/oop/commands.js.map +1 -0
- package/dist/oop/conformance.d.ts +16 -0
- package/dist/oop/conformance.js +30 -0
- package/dist/oop/conformance.js.map +1 -0
- package/dist/prompts/discovery.d.ts +2 -0
- package/dist/prompts/discovery.js +41 -0
- package/dist/prompts/discovery.js.map +1 -0
- package/dist/prompts/imagineWithoutController.d.ts +2 -0
- package/dist/prompts/imagineWithoutController.js +75 -0
- package/dist/prompts/imagineWithoutController.js.map +1 -0
- package/dist/prompts/index.d.ts +2 -0
- package/dist/prompts/index.js +17 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/phasePressureTest.d.ts +2 -0
- package/dist/prompts/phasePressureTest.js +69 -0
- package/dist/prompts/phasePressureTest.js.map +1 -0
- package/dist/prompts/rddIntro.d.ts +2 -0
- package/dist/prompts/rddIntro.js +53 -0
- package/dist/prompts/rddIntro.js.map +1 -0
- package/dist/prompts/review.d.ts +2 -0
- package/dist/prompts/review.js +45 -0
- package/dist/prompts/review.js.map +1 -0
- package/dist/prompts/stateMachineIntrospect.d.ts +2 -0
- package/dist/prompts/stateMachineIntrospect.js +80 -0
- package/dist/prompts/stateMachineIntrospect.js.map +1 -0
- package/dist/prompts/tellDontAskRephrase.d.ts +2 -0
- package/dist/prompts/tellDontAskRephrase.js +66 -0
- package/dist/prompts/tellDontAskRephrase.js.map +1 -0
- package/dist/render/diff.d.ts +21 -0
- package/dist/render/diff.js +84 -0
- package/dist/render/diff.js.map +1 -0
- package/dist/render/mermaid.d.ts +24 -0
- package/dist/render/mermaid.js +53 -0
- package/dist/render/mermaid.js.map +1 -0
- package/dist/render/sideBySide.d.ts +14 -0
- package/dist/render/sideBySide.js +91 -0
- package/dist/render/sideBySide.js.map +1 -0
- package/dist/resources/index.d.ts +2 -0
- package/dist/resources/index.js +349 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +22 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/alternatives.d.ts +2 -0
- package/dist/tools/alternatives.js +9 -0
- package/dist/tools/alternatives.js.map +1 -0
- package/dist/tools/classes.d.ts +2 -0
- package/dist/tools/classes.js +13 -0
- package/dist/tools/classes.js.map +1 -0
- package/dist/tools/collaborations.d.ts +2 -0
- package/dist/tools/collaborations.js +8 -0
- package/dist/tools/collaborations.js.map +1 -0
- package/dist/tools/compare.d.ts +2 -0
- package/dist/tools/compare.js +9 -0
- package/dist/tools/compare.js.map +1 -0
- package/dist/tools/conformance.d.ts +3 -0
- package/dist/tools/conformance.js +11 -0
- package/dist/tools/conformance.js.map +1 -0
- package/dist/tools/diagram.d.ts +2 -0
- package/dist/tools/diagram.js +9 -0
- package/dist/tools/diagram.js.map +1 -0
- package/dist/tools/discovery.d.ts +2 -0
- package/dist/tools/discovery.js +23 -0
- package/dist/tools/discovery.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +27 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/init.d.ts +2 -0
- package/dist/tools/init.js +9 -0
- package/dist/tools/init.js.map +1 -0
- package/dist/tools/oop.d.ts +9 -0
- package/dist/tools/oop.js +26 -0
- package/dist/tools/oop.js.map +1 -0
- package/dist/tools/responsibilities.d.ts +2 -0
- package/dist/tools/responsibilities.js +13 -0
- package/dist/tools/responsibilities.js.map +1 -0
- package/dist/tools/state.d.ts +2 -0
- package/dist/tools/state.js +8 -0
- package/dist/tools/state.js.map +1 -0
- package/dist/tools/useCases.d.ts +2 -0
- package/dist/tools/useCases.js +18 -0
- package/dist/tools/useCases.js.map +1 -0
- package/dist/tools/validate.d.ts +2 -0
- package/dist/tools/validate.js +9 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/tradeoff/engine.d.ts +61 -0
- package/dist/tradeoff/engine.js +171 -0
- package/dist/tradeoff/engine.js.map +1 -0
- package/dist/tradeoff/heuristics/classSplit.d.ts +3 -0
- package/dist/tradeoff/heuristics/classSplit.js +94 -0
- package/dist/tradeoff/heuristics/classSplit.js.map +1 -0
- package/dist/tradeoff/heuristics/collaborationShape.d.ts +2 -0
- package/dist/tradeoff/heuristics/collaborationShape.js +82 -0
- package/dist/tradeoff/heuristics/collaborationShape.js.map +1 -0
- package/dist/tradeoff/heuristics/collectionShape.d.ts +2 -0
- package/dist/tradeoff/heuristics/collectionShape.js +113 -0
- package/dist/tradeoff/heuristics/collectionShape.js.map +1 -0
- package/dist/tradeoff/heuristics/dualSubjectComparison.d.ts +2 -0
- package/dist/tradeoff/heuristics/dualSubjectComparison.js +114 -0
- package/dist/tradeoff/heuristics/dualSubjectComparison.js.map +1 -0
- package/dist/tradeoff/heuristics/errorHandling.d.ts +2 -0
- package/dist/tradeoff/heuristics/errorHandling.js +110 -0
- package/dist/tradeoff/heuristics/errorHandling.js.map +1 -0
- package/dist/tradeoff/heuristics/responsibilitySplit.d.ts +23 -0
- package/dist/tradeoff/heuristics/responsibilitySplit.js +183 -0
- package/dist/tradeoff/heuristics/responsibilitySplit.js.map +1 -0
- package/dist/tradeoff/heuristics/stereotypeChoice.d.ts +2 -0
- package/dist/tradeoff/heuristics/stereotypeChoice.js +23 -0
- package/dist/tradeoff/heuristics/stereotypeChoice.js.map +1 -0
- package/dist/tradeoff/heuristics/strategyOrPolicy.d.ts +2 -0
- package/dist/tradeoff/heuristics/strategyOrPolicy.js +110 -0
- package/dist/tradeoff/heuristics/strategyOrPolicy.js.map +1 -0
- package/dist/tradeoff/heuristics/valueObjectShape.d.ts +2 -0
- package/dist/tradeoff/heuristics/valueObjectShape.js +111 -0
- package/dist/tradeoff/heuristics/valueObjectShape.js.map +1 -0
- package/dist/tradeoff/templates.d.ts +2 -0
- package/dist/tradeoff/templates.js +33 -0
- package/dist/tradeoff/templates.js.map +1 -0
- package/dist/util/log.d.ts +1 -0
- package/dist/util/log.js +7 -0
- package/dist/util/log.js.map +1 -0
- package/dist/util/mcp.d.ts +4 -0
- package/dist/util/mcp.js +18 -0
- package/dist/util/mcp.js.map +1 -0
- package/dist/validate/cohesion.d.ts +3 -0
- package/dist/validate/cohesion.js +88 -0
- package/dist/validate/cohesion.js.map +1 -0
- package/dist/validate/collectionWrapper.d.ts +3 -0
- package/dist/validate/collectionWrapper.js +131 -0
- package/dist/validate/collectionWrapper.js.map +1 -0
- package/dist/validate/coupling.d.ts +3 -0
- package/dist/validate/coupling.js +47 -0
- package/dist/validate/coupling.js.map +1 -0
- package/dist/validate/cycle.d.ts +3 -0
- package/dist/validate/cycle.js +101 -0
- package/dist/validate/cycle.js.map +1 -0
- package/dist/validate/dataDuplication.d.ts +3 -0
- package/dist/validate/dataDuplication.js +150 -0
- package/dist/validate/dataDuplication.js.map +1 -0
- package/dist/validate/dependencyDirection.d.ts +3 -0
- package/dist/validate/dependencyDirection.js +106 -0
- package/dist/validate/dependencyDirection.js.map +1 -0
- package/dist/validate/dualUseKnowing.d.ts +3 -0
- package/dist/validate/dualUseKnowing.js +137 -0
- package/dist/validate/dualUseKnowing.js.map +1 -0
- package/dist/validate/emptyObjectFill.d.ts +3 -0
- package/dist/validate/emptyObjectFill.js +119 -0
- package/dist/validate/emptyObjectFill.js.map +1 -0
- package/dist/validate/findings.d.ts +22 -0
- package/dist/validate/findings.js +12 -0
- package/dist/validate/findings.js.map +1 -0
- package/dist/validate/functionDecompositionExcess.d.ts +3 -0
- package/dist/validate/functionDecompositionExcess.js +120 -0
- package/dist/validate/functionDecompositionExcess.js.map +1 -0
- package/dist/validate/functionNotObject.d.ts +3 -0
- package/dist/validate/functionNotObject.js +80 -0
- package/dist/validate/functionNotObject.js.map +1 -0
- package/dist/validate/godObject.d.ts +6 -0
- package/dist/validate/godObject.js +76 -0
- package/dist/validate/godObject.js.map +1 -0
- package/dist/validate/presentationInDomain.d.ts +3 -0
- package/dist/validate/presentationInDomain.js +57 -0
- package/dist/validate/presentationInDomain.js.map +1 -0
- package/dist/validate/primitiveWrapper.d.ts +3 -0
- package/dist/validate/primitiveWrapper.js +106 -0
- package/dist/validate/primitiveWrapper.js.map +1 -0
- package/dist/validate/rules.d.ts +24 -0
- package/dist/validate/rules.js +269 -0
- package/dist/validate/rules.js.map +1 -0
- package/dist/validate/singletonStatefulHolder.d.ts +3 -0
- package/dist/validate/singletonStatefulHolder.js +128 -0
- package/dist/validate/singletonStatefulHolder.js.map +1 -0
- package/dist/validate/strategyAsymmetry.d.ts +3 -0
- package/dist/validate/strategyAsymmetry.js +131 -0
- package/dist/validate/strategyAsymmetry.js.map +1 -0
- package/dist/validate/testability.d.ts +5 -0
- package/dist/validate/testability.js +157 -0
- package/dist/validate/testability.js.map +1 -0
- package/dist/validate/vagueName.d.ts +3 -0
- package/dist/validate/vagueName.js +136 -0
- package/dist/validate/vagueName.js.map +1 -0
- package/dist/validate/validationLocation.d.ts +3 -0
- package/dist/validate/validationLocation.js +120 -0
- package/dist/validate/validationLocation.js.map +1 -0
- package/lessons/STYLE_GUIDE.md +304 -0
- package/lessons/woowacourse-kotlin-blackjack/2chang5/33-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +293 -0
- package/lessons/woowacourse-kotlin-blackjack/Choisehyeon/52-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +106 -0
- package/lessons/woowacourse-kotlin-blackjack/Choisehyeon/9-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +282 -0
- package/lessons/woowacourse-kotlin-blackjack/DYGames/14-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +182 -0
- package/lessons/woowacourse-kotlin-blackjack/DYGames/37-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +122 -0
- package/lessons/woowacourse-kotlin-blackjack/EmilyCh0/12-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +117 -0
- package/lessons/woowacourse-kotlin-blackjack/EmilyCh0/38-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +291 -0
- package/lessons/woowacourse-kotlin-blackjack/HANDOVER.md +1475 -0
- package/lessons/woowacourse-kotlin-blackjack/HamBeomJoon/119-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +251 -0
- package/lessons/woowacourse-kotlin-blackjack/HamBeomJoon/137-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +459 -0
- package/lessons/woowacourse-kotlin-blackjack/Hevton/100-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +282 -0
- package/lessons/woowacourse-kotlin-blackjack/Hevton/82-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +303 -0
- package/lessons/woowacourse-kotlin-blackjack/Hogu59/71-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +134 -0
- package/lessons/woowacourse-kotlin-blackjack/Hogu59/96-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +323 -0
- package/lessons/woowacourse-kotlin-blackjack/JoYehyun99/66-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +210 -0
- package/lessons/woowacourse-kotlin-blackjack/Junyoung-WON/101-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +191 -0
- package/lessons/woowacourse-kotlin-blackjack/Junyoung-WON/80-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +253 -0
- package/lessons/woowacourse-kotlin-blackjack/Leeyerin0210/108-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +516 -0
- package/lessons/woowacourse-kotlin-blackjack/Leeyerin0210/133-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +541 -0
- package/lessons/woowacourse-kotlin-blackjack/RightHennessy/22-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +278 -0
- package/lessons/woowacourse-kotlin-blackjack/RightHennessy/48-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +188 -0
- package/lessons/woowacourse-kotlin-blackjack/SeongHoonC/16-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +228 -0
- package/lessons/woowacourse-kotlin-blackjack/SeongHoonC/54-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +227 -0
- package/lessons/woowacourse-kotlin-blackjack/Yunseok-Nam/76-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +279 -0
- package/lessons/woowacourse-kotlin-blackjack/Yunseok-Nam/86-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +203 -0
- package/lessons/woowacourse-kotlin-blackjack/aprilgom/105-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +162 -0
- package/lessons/woowacourse-kotlin-blackjack/aprilgom/64-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +279 -0
- package/lessons/woowacourse-kotlin-blackjack/boogi-woogi/18-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +188 -0
- package/lessons/woowacourse-kotlin-blackjack/boogi-woogi/36-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +769 -0
- package/lessons/woowacourse-kotlin-blackjack/briandr97/19-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +226 -0
- package/lessons/woowacourse-kotlin-blackjack/briandr97/59-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +193 -0
- package/lessons/woowacourse-kotlin-blackjack/chaehyuns/70-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +175 -0
- package/lessons/woowacourse-kotlin-blackjack/chaehyuns/98-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +206 -0
- package/lessons/woowacourse-kotlin-blackjack/chanho0908/123-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +277 -0
- package/lessons/woowacourse-kotlin-blackjack/chanho0908/150-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +219 -0
- package/lessons/woowacourse-kotlin-blackjack/chws0508/26-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +259 -0
- package/lessons/woowacourse-kotlin-blackjack/chws0508/43-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +189 -0
- package/lessons/woowacourse-kotlin-blackjack/cucumber99/109-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +351 -0
- package/lessons/woowacourse-kotlin-blackjack/cucumber99/149-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +147 -0
- package/lessons/woowacourse-kotlin-blackjack/devfeijoa/121-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +168 -0
- package/lessons/woowacourse-kotlin-blackjack/devfeijoa/153-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +116 -0
- package/lessons/woowacourse-kotlin-blackjack/doabletuple/126-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +680 -0
- package/lessons/woowacourse-kotlin-blackjack/doabletuple/142-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +739 -0
- package/lessons/woowacourse-kotlin-blackjack/donghyun81/116-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +297 -0
- package/lessons/woowacourse-kotlin-blackjack/donghyun81/147-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +244 -0
- package/lessons/woowacourse-kotlin-blackjack/dpcks0509/78-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +232 -0
- package/lessons/woowacourse-kotlin-blackjack/dpcks0509/88-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +137 -0
- package/lessons/woowacourse-kotlin-blackjack/etama123/112-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +269 -0
- package/lessons/woowacourse-kotlin-blackjack/etama123/148-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +240 -0
- package/lessons/woowacourse-kotlin-blackjack/gaeun5744/102-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +146 -0
- package/lessons/woowacourse-kotlin-blackjack/gaeun5744/69-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +224 -0
- package/lessons/woowacourse-kotlin-blackjack/gahyunkim/118-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +397 -0
- package/lessons/woowacourse-kotlin-blackjack/gahyunkim/141-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +215 -0
- package/lessons/woowacourse-kotlin-blackjack/giovannijunseokim/106-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +395 -0
- package/lessons/woowacourse-kotlin-blackjack/giovannijunseokim/134-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +219 -0
- package/lessons/woowacourse-kotlin-blackjack/haeum808/62-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +230 -0
- package/lessons/woowacourse-kotlin-blackjack/haeum808/89-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +169 -0
- package/lessons/woowacourse-kotlin-blackjack/hwannow/110-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +221 -0
- package/lessons/woowacourse-kotlin-blackjack/hwannow/132-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +573 -0
- package/lessons/woowacourse-kotlin-blackjack/hxeyexn/103-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +188 -0
- package/lessons/woowacourse-kotlin-blackjack/hxeyexn/81-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +301 -0
- package/lessons/woowacourse-kotlin-blackjack/hyemdooly/21-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +613 -0
- package/lessons/woowacourse-kotlin-blackjack/hyemdooly/35-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +691 -0
- package/lessons/woowacourse-kotlin-blackjack/hyunji1203/13-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +206 -0
- package/lessons/woowacourse-kotlin-blackjack/ii2001/79-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +153 -0
- package/lessons/woowacourse-kotlin-blackjack/ii2001/85-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +318 -0
- package/lessons/woowacourse-kotlin-blackjack/ijh1298/128-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +250 -0
- package/lessons/woowacourse-kotlin-blackjack/ijh1298/135-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +269 -0
- package/lessons/woowacourse-kotlin-blackjack/inseonyun/28-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +346 -0
- package/lessons/woowacourse-kotlin-blackjack/inseonyun/49-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +360 -0
- package/lessons/woowacourse-kotlin-blackjack/ippnsj/30-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +152 -0
- package/lessons/woowacourse-kotlin-blackjack/ippnsj/42-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +213 -0
- package/lessons/woowacourse-kotlin-blackjack/jaeyeongjo/73-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +247 -0
- package/lessons/woowacourse-kotlin-blackjack/jaeyeongjo/87-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +900 -0
- package/lessons/woowacourse-kotlin-blackjack/jerry8282/125-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +248 -0
- package/lessons/woowacourse-kotlin-blackjack/jerry8282/151-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +243 -0
- package/lessons/woowacourse-kotlin-blackjack/jinuemong/63-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +281 -0
- package/lessons/woowacourse-kotlin-blackjack/jinuemong/92-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +283 -0
- package/lessons/woowacourse-kotlin-blackjack/jiyuneel/113-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +348 -0
- package/lessons/woowacourse-kotlin-blackjack/jiyuneel/140-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +265 -0
- package/lessons/woowacourse-kotlin-blackjack/junjange/61-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +270 -0
- package/lessons/woowacourse-kotlin-blackjack/junjange/93-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +465 -0
- package/lessons/woowacourse-kotlin-blackjack/junseo511/107-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +296 -0
- package/lessons/woowacourse-kotlin-blackjack/junseo511/145-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +316 -0
- package/lessons/woowacourse-kotlin-blackjack/ki960213/11-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +175 -0
- package/lessons/woowacourse-kotlin-blackjack/kimhm0728/72-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +458 -0
- package/lessons/woowacourse-kotlin-blackjack/kimhm0728/97-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +225 -0
- package/lessons/woowacourse-kotlin-blackjack/kkosang/68-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +221 -0
- package/lessons/woowacourse-kotlin-blackjack/kkosang/94-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +158 -0
- package/lessons/woowacourse-kotlin-blackjack/kmkim2689/65-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +252 -0
- package/lessons/woowacourse-kotlin-blackjack/kmkim2689/90-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +460 -0
- package/lessons/woowacourse-kotlin-blackjack/krrong/32-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +216 -0
- package/lessons/woowacourse-kotlin-blackjack/krrong/46-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +285 -0
- package/lessons/woowacourse-kotlin-blackjack/m6z1/122-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +177 -0
- package/lessons/woowacourse-kotlin-blackjack/m6z1/144-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +155 -0
- package/lessons/woowacourse-kotlin-blackjack/medAndro/130-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +683 -0
- package/lessons/woowacourse-kotlin-blackjack/medAndro/143-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +678 -0
- package/lessons/woowacourse-kotlin-blackjack/moondev03/114-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +262 -0
- package/lessons/woowacourse-kotlin-blackjack/moondev03/131-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +498 -0
- package/lessons/woowacourse-kotlin-blackjack/murjune/77-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +287 -0
- package/lessons/woowacourse-kotlin-blackjack/murjune/91-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +280 -0
- package/lessons/woowacourse-kotlin-blackjack/no1msh/31-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +250 -0
- package/lessons/woowacourse-kotlin-blackjack/otter66/24-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +302 -0
- package/lessons/woowacourse-kotlin-blackjack/otter66/58-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +391 -0
- package/lessons/woowacourse-kotlin-blackjack/oungsi2000/111-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +253 -0
- package/lessons/woowacourse-kotlin-blackjack/oungsi2000/136-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +540 -0
- package/lessons/woowacourse-kotlin-blackjack/parkjiminnnn/117-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +819 -0
- package/lessons/woowacourse-kotlin-blackjack/parkjiminnnn/139-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +683 -0
- package/lessons/woowacourse-kotlin-blackjack/pingu244/20-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +258 -0
- package/lessons/woowacourse-kotlin-blackjack/pingu244/41-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +171 -0
- package/lessons/woowacourse-kotlin-blackjack/re4rk/34-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +289 -0
- package/lessons/woowacourse-kotlin-blackjack/re4rk/8-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +297 -0
- package/lessons/woowacourse-kotlin-blackjack/rhkrwngud445/17-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +279 -0
- package/lessons/woowacourse-kotlin-blackjack/rhkrwngud445/50-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +145 -0
- package/lessons/woowacourse-kotlin-blackjack/rhthrhrl0/23-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +253 -0
- package/lessons/woowacourse-kotlin-blackjack/rhthrhrl0/39-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +238 -0
- package/lessons/woowacourse-kotlin-blackjack/rosemin928/129-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +726 -0
- package/lessons/woowacourse-kotlin-blackjack/rosemin928/138-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +845 -0
- package/lessons/woowacourse-kotlin-blackjack/s6m1n/67-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +220 -0
- package/lessons/woowacourse-kotlin-blackjack/s6m1n/99-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +229 -0
- package/lessons/woowacourse-kotlin-blackjack/s9hn/10-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +284 -0
- package/lessons/woowacourse-kotlin-blackjack/sh1mj1/75-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +224 -0
- package/lessons/woowacourse-kotlin-blackjack/songpink/74-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +238 -0
- package/lessons/woowacourse-kotlin-blackjack/songpink/95-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +216 -0
- package/lessons/woowacourse-kotlin-blackjack/sujin9/15-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +132 -0
- package/lessons/woowacourse-kotlin-blackjack/sujin9/47-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +265 -0
- package/lessons/woowacourse-kotlin-blackjack/tmdgh1592/29-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +305 -0
- package/lessons/woowacourse-kotlin-blackjack/tmdgh1592/51-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +278 -0
- package/lessons/woowacourse-kotlin-blackjack/tobae-time/115-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +305 -0
- package/lessons/woowacourse-kotlin-blackjack/tobae-time/154-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +298 -0
- package/lessons/woowacourse-kotlin-blackjack/whk06061/25-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +235 -0
- package/lessons/woowacourse-kotlin-blackjack/whk06061/53-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +130 -0
- package/lessons/woowacourse-kotlin-blackjack/wondroid-world/124-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +486 -0
- package/lessons/woowacourse-kotlin-blackjack/wondroid-world/152-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +482 -0
- package/lessons/woowacourse-kotlin-blackjack/yrsel/120-1/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +194 -0
- package/lessons/woowacourse-kotlin-blackjack/yrsel/146-2/353/213/250/352/263/204-/353/270/224/353/236/231/354/236/255.md +444 -0
- package/lessons/woowacourse-kotlin-lotto/2chang5/27-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +155 -0
- package/lessons/woowacourse-kotlin-lotto/2chang5/52-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +187 -0
- package/lessons/woowacourse-kotlin-lotto/Choisehyeon/14-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +206 -0
- package/lessons/woowacourse-kotlin-lotto/Choisehyeon/47-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +119 -0
- package/lessons/woowacourse-kotlin-lotto/DYGames/2-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +83 -0
- package/lessons/woowacourse-kotlin-lotto/DYGames/34-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +88 -0
- package/lessons/woowacourse-kotlin-lotto/EmilyCh0/12-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +107 -0
- package/lessons/woowacourse-kotlin-lotto/EmilyCh0/40-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +135 -0
- package/lessons/woowacourse-kotlin-lotto/HANDOVER.md +217 -0
- package/lessons/woowacourse-kotlin-lotto/HamBeomJoon/121-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +66 -0
- package/lessons/woowacourse-kotlin-lotto/HamBeomJoon/139-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +75 -0
- package/lessons/woowacourse-kotlin-lotto/Hevton/76-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +21 -0
- package/lessons/woowacourse-kotlin-lotto/Hevton/92-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +122 -0
- package/lessons/woowacourse-kotlin-lotto/Hogu59/100-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +110 -0
- package/lessons/woowacourse-kotlin-lotto/Hogu59/68-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +84 -0
- package/lessons/woowacourse-kotlin-lotto/JoYehyun99/60-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +176 -0
- package/lessons/woowacourse-kotlin-lotto/JoYehyun99/96-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +71 -0
- package/lessons/woowacourse-kotlin-lotto/Junyoung-WON/61-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +143 -0
- package/lessons/woowacourse-kotlin-lotto/Junyoung-WON/93-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +16 -0
- package/lessons/woowacourse-kotlin-lotto/Leeyerin0210/109-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +57 -0
- package/lessons/woowacourse-kotlin-lotto/Leeyerin0210/135-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +115 -0
- package/lessons/woowacourse-kotlin-lotto/PROGRESS.md +160 -0
- package/lessons/woowacourse-kotlin-lotto/README.md +77 -0
- package/lessons/woowacourse-kotlin-lotto/RightHennessy/32-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +178 -0
- package/lessons/woowacourse-kotlin-lotto/RightHennessy/5-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +149 -0
- package/lessons/woowacourse-kotlin-lotto/SUMMARY.md +369 -0
- package/lessons/woowacourse-kotlin-lotto/SeongHoonC/25-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +129 -0
- package/lessons/woowacourse-kotlin-lotto/SeongHoonC/37-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +27 -0
- package/lessons/woowacourse-kotlin-lotto/Yunseok-Nam/72-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +137 -0
- package/lessons/woowacourse-kotlin-lotto/Yunseok-Nam/86-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +17 -0
- package/lessons/woowacourse-kotlin-lotto/aprilgom/102-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +62 -0
- package/lessons/woowacourse-kotlin-lotto/aprilgom/64-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +146 -0
- package/lessons/woowacourse-kotlin-lotto/boogi-woogi/11-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +26 -0
- package/lessons/woowacourse-kotlin-lotto/boogi-woogi/29-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +26 -0
- package/lessons/woowacourse-kotlin-lotto/briandr97/26-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +52 -0
- package/lessons/woowacourse-kotlin-lotto/briandr97/51-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +123 -0
- package/lessons/woowacourse-kotlin-lotto/chaehyuns/71-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +115 -0
- package/lessons/woowacourse-kotlin-lotto/chaehyuns/97-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +107 -0
- package/lessons/woowacourse-kotlin-lotto/chanho0908/124-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +51 -0
- package/lessons/woowacourse-kotlin-lotto/chanho0908/129-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +149 -0
- package/lessons/woowacourse-kotlin-lotto/chws0508/24-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +117 -0
- package/lessons/woowacourse-kotlin-lotto/chws0508/50-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +167 -0
- package/lessons/woowacourse-kotlin-lotto/cucumber99/116-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +155 -0
- package/lessons/woowacourse-kotlin-lotto/cucumber99/147-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +223 -0
- package/lessons/woowacourse-kotlin-lotto/devfeijoa/125-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +68 -0
- package/lessons/woowacourse-kotlin-lotto/devfeijoa/153-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +107 -0
- package/lessons/woowacourse-kotlin-lotto/doabletuple/119-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +157 -0
- package/lessons/woowacourse-kotlin-lotto/doabletuple/138-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +77 -0
- package/lessons/woowacourse-kotlin-lotto/donghyun81/111-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +67 -0
- package/lessons/woowacourse-kotlin-lotto/donghyun81/152-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +124 -0
- package/lessons/woowacourse-kotlin-lotto/dpcks0509/80-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +141 -0
- package/lessons/woowacourse-kotlin-lotto/dpcks0509/87-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +134 -0
- package/lessons/woowacourse-kotlin-lotto/etama123/112-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +170 -0
- package/lessons/woowacourse-kotlin-lotto/etama123/148-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +183 -0
- package/lessons/woowacourse-kotlin-lotto/gaeun5744/101-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +21 -0
- package/lessons/woowacourse-kotlin-lotto/gaeun5744/78-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +158 -0
- package/lessons/woowacourse-kotlin-lotto/gahyunkim/107-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +83 -0
- package/lessons/woowacourse-kotlin-lotto/gahyunkim/136-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +51 -0
- package/lessons/woowacourse-kotlin-lotto/giovannijunseokim/106-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +171 -0
- package/lessons/woowacourse-kotlin-lotto/giovannijunseokim/137-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +111 -0
- package/lessons/woowacourse-kotlin-lotto/haeum808/67-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +52 -0
- package/lessons/woowacourse-kotlin-lotto/haeum808/95-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +97 -0
- package/lessons/woowacourse-kotlin-lotto/hwannow/113-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +235 -0
- package/lessons/woowacourse-kotlin-lotto/hwannow/132-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +149 -0
- package/lessons/woowacourse-kotlin-lotto/hxeyexn/77-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +141 -0
- package/lessons/woowacourse-kotlin-lotto/hxeyexn/98-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +162 -0
- package/lessons/woowacourse-kotlin-lotto/hyemdooly/17-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +19 -0
- package/lessons/woowacourse-kotlin-lotto/hyemdooly/28-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +23 -0
- package/lessons/woowacourse-kotlin-lotto/hyunji1203/15-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +171 -0
- package/lessons/woowacourse-kotlin-lotto/hyunji1203/48-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +150 -0
- package/lessons/woowacourse-kotlin-lotto/ii2001/62-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +104 -0
- package/lessons/woowacourse-kotlin-lotto/ii2001/88-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +20 -0
- package/lessons/woowacourse-kotlin-lotto/ijh1298/122-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +179 -0
- package/lessons/woowacourse-kotlin-lotto/ijh1298/133-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +153 -0
- package/lessons/woowacourse-kotlin-lotto/inseonyun/3-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +222 -0
- package/lessons/woowacourse-kotlin-lotto/inseonyun/49-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +166 -0
- package/lessons/woowacourse-kotlin-lotto/ippnsj/16-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +154 -0
- package/lessons/woowacourse-kotlin-lotto/ippnsj/55-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +145 -0
- package/lessons/woowacourse-kotlin-lotto/jaeyeongjo/59-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +68 -0
- package/lessons/woowacourse-kotlin-lotto/jaeyeongjo/83-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +53 -0
- package/lessons/woowacourse-kotlin-lotto/jerry8282/115-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +97 -0
- package/lessons/woowacourse-kotlin-lotto/jerry8282/134-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +145 -0
- package/lessons/woowacourse-kotlin-lotto/jinuemong/63-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +144 -0
- package/lessons/woowacourse-kotlin-lotto/jinuemong/89-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +148 -0
- package/lessons/woowacourse-kotlin-lotto/jiyuneel/128-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +52 -0
- package/lessons/woowacourse-kotlin-lotto/jiyuneel/144-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +149 -0
- package/lessons/woowacourse-kotlin-lotto/junjange/58-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +148 -0
- package/lessons/woowacourse-kotlin-lotto/junjange/81-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +178 -0
- package/lessons/woowacourse-kotlin-lotto/junseo511/120-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +153 -0
- package/lessons/woowacourse-kotlin-lotto/junseo511/131-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +188 -0
- package/lessons/woowacourse-kotlin-lotto/ki960213/43-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +24 -0
- package/lessons/woowacourse-kotlin-lotto/ki960213/8-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +159 -0
- package/lessons/woowacourse-kotlin-lotto/kimhm0728/74-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +50 -0
- package/lessons/woowacourse-kotlin-lotto/kimhm0728/94-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +176 -0
- package/lessons/woowacourse-kotlin-lotto/kkosang/85-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +145 -0
- package/lessons/woowacourse-kotlin-lotto/kmkim2689/70-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +97 -0
- package/lessons/woowacourse-kotlin-lotto/kmkim2689/82-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +157 -0
- package/lessons/woowacourse-kotlin-lotto/krrong/19-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +161 -0
- package/lessons/woowacourse-kotlin-lotto/krrong/41-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +73 -0
- package/lessons/woowacourse-kotlin-lotto/m6z1/105-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +67 -0
- package/lessons/woowacourse-kotlin-lotto/m6z1/151-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +104 -0
- package/lessons/woowacourse-kotlin-lotto/medAndro/127-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +186 -0
- package/lessons/woowacourse-kotlin-lotto/medAndro/130-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +132 -0
- package/lessons/woowacourse-kotlin-lotto/moondev03/110-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +66 -0
- package/lessons/woowacourse-kotlin-lotto/moondev03/141-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +155 -0
- package/lessons/woowacourse-kotlin-lotto/murjune/69-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +143 -0
- package/lessons/woowacourse-kotlin-lotto/murjune/90-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +177 -0
- package/lessons/woowacourse-kotlin-lotto/no1msh/20-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +69 -0
- package/lessons/woowacourse-kotlin-lotto/no1msh/46-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +143 -0
- package/lessons/woowacourse-kotlin-lotto/otter66/13-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +178 -0
- package/lessons/woowacourse-kotlin-lotto/otter66/53-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +160 -0
- package/lessons/woowacourse-kotlin-lotto/oungsi2000/126-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +71 -0
- package/lessons/woowacourse-kotlin-lotto/oungsi2000/149-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +56 -0
- package/lessons/woowacourse-kotlin-lotto/parkjiminnnn/117-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +129 -0
- package/lessons/woowacourse-kotlin-lotto/parkjiminnnn/146-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +173 -0
- package/lessons/woowacourse-kotlin-lotto/pingu244/33-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +21 -0
- package/lessons/woowacourse-kotlin-lotto/pingu244/54-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +30 -0
- package/lessons/woowacourse-kotlin-lotto/pingu244/7-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +19 -0
- package/lessons/woowacourse-kotlin-lotto/re4rk/31-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +143 -0
- package/lessons/woowacourse-kotlin-lotto/re4rk/4-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +66 -0
- package/lessons/woowacourse-kotlin-lotto/rhkrwngud445/23-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +26 -0
- package/lessons/woowacourse-kotlin-lotto/rhkrwngud445/38-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +129 -0
- package/lessons/woowacourse-kotlin-lotto/rhthrhrl0/21-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +146 -0
- package/lessons/woowacourse-kotlin-lotto/rhthrhrl0/36-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +150 -0
- package/lessons/woowacourse-kotlin-lotto/rosemin928/123-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +179 -0
- package/lessons/woowacourse-kotlin-lotto/rosemin928/140-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +126 -0
- package/lessons/woowacourse-kotlin-lotto/s6m1n/103-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +119 -0
- package/lessons/woowacourse-kotlin-lotto/s6m1n/66-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +118 -0
- package/lessons/woowacourse-kotlin-lotto/s9hn/18-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +112 -0
- package/lessons/woowacourse-kotlin-lotto/sh1mj1/75-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +104 -0
- package/lessons/woowacourse-kotlin-lotto/sh1mj1/91-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +140 -0
- package/lessons/woowacourse-kotlin-lotto/songpink/65-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +96 -0
- package/lessons/woowacourse-kotlin-lotto/songpink/99-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +64 -0
- package/lessons/woowacourse-kotlin-lotto/sujin9/10-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +20 -0
- package/lessons/woowacourse-kotlin-lotto/sujin9/35-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +124 -0
- package/lessons/woowacourse-kotlin-lotto/tmdgh1592/22-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +30 -0
- package/lessons/woowacourse-kotlin-lotto/tmdgh1592/30-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +20 -0
- package/lessons/woowacourse-kotlin-lotto/tobae-time/114-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +158 -0
- package/lessons/woowacourse-kotlin-lotto/tobae-time/145-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +120 -0
- package/lessons/woowacourse-kotlin-lotto/whk06061/42-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +21 -0
- package/lessons/woowacourse-kotlin-lotto/whk06061/9-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +89 -0
- package/lessons/woowacourse-kotlin-lotto/wondroid-world/118-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +100 -0
- package/lessons/woowacourse-kotlin-lotto/wondroid-world/150-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +142 -0
- package/lessons/woowacourse-kotlin-lotto/yrsel/108-1/353/213/250/352/263/204-/353/241/234/353/230/220.md +124 -0
- package/lessons/woowacourse-kotlin-lotto/yrsel/143-2/353/213/250/352/263/204-/353/241/234/353/230/220.md +108 -0
- package/lessons/woowacourse-racingcar/2chang5/33-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +56 -0
- package/lessons/woowacourse-racingcar/2chang5/75-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/CFalws/85-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +56 -0
- package/lessons/woowacourse-racingcar/Choisehyeon/46-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +61 -0
- package/lessons/woowacourse-racingcar/DYGames/49-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +62 -0
- package/lessons/woowacourse-racingcar/EmilyCh0/40-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +64 -0
- package/lessons/woowacourse-racingcar/EmilyCh0/57-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/HamBeomJoon/137-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/HamBeomJoon/170-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +50 -0
- package/lessons/woowacourse-racingcar/Hevton/106-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/Hevton/118-/353/246/254/355/214/251/355/204/260/353/247/201.md +51 -0
- package/lessons/woowacourse-racingcar/Hogu59/108-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +56 -0
- package/lessons/woowacourse-racingcar/Hogu59/125-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +59 -0
- package/lessons/woowacourse-racingcar/JoYehyun99/111-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +57 -0
- package/lessons/woowacourse-racingcar/JoYehyun99/88-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +45 -0
- package/lessons/woowacourse-racingcar/Junyoung-WON/127-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +49 -0
- package/lessons/woowacourse-racingcar/Junyoung-WON/90-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +49 -0
- package/lessons/woowacourse-racingcar/Leeyerin0210/133-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +50 -0
- package/lessons/woowacourse-racingcar/Leeyerin0210/159-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/PROGRESS.md +94 -0
- package/lessons/woowacourse-racingcar/README.md +69 -0
- package/lessons/woowacourse-racingcar/RightHennessy/51-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/RightHennessy/58-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +57 -0
- package/lessons/woowacourse-racingcar/RunaNam/81-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +44 -0
- package/lessons/woowacourse-racingcar/SUMMARY.md +339 -0
- package/lessons/woowacourse-racingcar/SeongHoonC/37-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +57 -0
- package/lessons/woowacourse-racingcar/SeongHoonC/66-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +48 -0
- package/lessons/woowacourse-racingcar/Songusika/84-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +60 -0
- package/lessons/woowacourse-racingcar/Yunseok-Nam/114-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +43 -0
- package/lessons/woowacourse-racingcar/Yunseok-Nam/96-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/aprilgom/130-/353/246/254/355/214/251/355/204/260/353/247/201.md +52 -0
- package/lessons/woowacourse-racingcar/aprilgom/97-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +49 -0
- package/lessons/woowacourse-racingcar/awesomeo184/23-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +48 -0
- package/lessons/woowacourse-racingcar/boogi-woogi/35-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +62 -0
- package/lessons/woowacourse-racingcar/boogi-woogi/59-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/briandr97/54-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +57 -0
- package/lessons/woowacourse-racingcar/chaehyuns/131-/353/246/254/355/214/251/355/204/260/353/247/201.md +49 -0
- package/lessons/woowacourse-racingcar/chaehyuns/99-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +52 -0
- package/lessons/woowacourse-racingcar/chanho0908/147-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/chanho0908/160-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +50 -0
- package/lessons/woowacourse-racingcar/chws0508/45-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +61 -0
- package/lessons/woowacourse-racingcar/chws0508/73-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +46 -0
- package/lessons/woowacourse-racingcar/cucumber99/132-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/cucumber99/177-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/devfeijoa/146-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +57 -0
- package/lessons/woowacourse-racingcar/doabletuple/149-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/doabletuple/175-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/donghyun81/154-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +58 -0
- package/lessons/woowacourse-racingcar/dpcks0509/103-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +48 -0
- package/lessons/woowacourse-racingcar/dpcks0509/112-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +48 -0
- package/lessons/woowacourse-racingcar/drunkenhw/82-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +60 -0
- package/lessons/woowacourse-racingcar/etama123/145-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +59 -0
- package/lessons/woowacourse-racingcar/gaeun5744/124-/353/246/254/355/214/251/355/204/260/353/247/201.md +53 -0
- package/lessons/woowacourse-racingcar/gaeun5744/93-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +60 -0
- package/lessons/woowacourse-racingcar/gahyunkim/134-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/gahyunkim/164-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/giovannijunseokim/140-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/haeum808/101-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/haeum808/129-/353/246/254/355/214/251/355/204/260/353/247/201.md +46 -0
- package/lessons/woowacourse-racingcar/hwannow/141-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +56 -0
- package/lessons/woowacourse-racingcar/hwannow/157-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +45 -0
- package/lessons/woowacourse-racingcar/hxeyexn/109-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +61 -0
- package/lessons/woowacourse-racingcar/hxeyexn/128-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +59 -0
- package/lessons/woowacourse-racingcar/hyemdooly/50-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +48 -0
- package/lessons/woowacourse-racingcar/hyemdooly/68-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/hyunji1203/29-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/hyunji1203/60-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +50 -0
- package/lessons/woowacourse-racingcar/ii2001/102-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +42 -0
- package/lessons/woowacourse-racingcar/ii2001/122-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +49 -0
- package/lessons/woowacourse-racingcar/ijh1298/144-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +57 -0
- package/lessons/woowacourse-racingcar/injoon2019/24-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +58 -0
- package/lessons/woowacourse-racingcar/inseonyun/44-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +60 -0
- package/lessons/woowacourse-racingcar/inseonyun/76-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/ippnsj/36-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/ippnsj/67-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +57 -0
- package/lessons/woowacourse-racingcar/jaeyeongjo/110-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +39 -0
- package/lessons/woowacourse-racingcar/jaeyeongjo/89-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +46 -0
- package/lessons/woowacourse-racingcar/jinuemong/104-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +49 -0
- package/lessons/woowacourse-racingcar/jinuemong/117-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +42 -0
- package/lessons/woowacourse-racingcar/jiyuneel/142-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +59 -0
- package/lessons/woowacourse-racingcar/junjange/113-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +41 -0
- package/lessons/woowacourse-racingcar/junjange/87-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +39 -0
- package/lessons/woowacourse-racingcar/junseo511/148-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +52 -0
- package/lessons/woowacourse-racingcar/junseo511/165-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +51 -0
- package/lessons/woowacourse-racingcar/ki960213/32-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/ki960213/63-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +51 -0
- package/lessons/woowacourse-racingcar/kimhm0728/100-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +52 -0
- package/lessons/woowacourse-racingcar/kimhm0728/116-/353/246/254/355/214/251/355/204/260/353/247/201.md +54 -0
- package/lessons/woowacourse-racingcar/kkosang/105-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/kkosang/126-/353/246/254/355/214/251/355/204/260/353/247/201.md +53 -0
- package/lessons/woowacourse-racingcar/kmkim2689/123-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +50 -0
- package/lessons/woowacourse-racingcar/kmkim2689/92-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/krrong/34-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +63 -0
- package/lessons/woowacourse-racingcar/m6z1/150-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/m6z1/156-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +40 -0
- package/lessons/woowacourse-racingcar/medAndro/153-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/moondev03/135-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +57 -0
- package/lessons/woowacourse-racingcar/moondev03/161-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +52 -0
- package/lessons/woowacourse-racingcar/murjune/121-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +50 -0
- package/lessons/woowacourse-racingcar/murjune/95-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/no1msh/43-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/no1msh/69-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +60 -0
- package/lessons/woowacourse-racingcar/otter66/48-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/otter66/77-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +52 -0
- package/lessons/woowacourse-racingcar/oungsi2000/152-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +56 -0
- package/lessons/woowacourse-racingcar/oungsi2000/187-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/parkjiminnnn/143-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +58 -0
- package/lessons/woowacourse-racingcar/parkjiminnnn/158-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/pingu244/38-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +58 -0
- package/lessons/woowacourse-racingcar/pingu244/62-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +59 -0
- package/lessons/woowacourse-racingcar/re4rk/28-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +60 -0
- package/lessons/woowacourse-racingcar/re4rk/55-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/rhkrwngud445/42-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/rhkrwngud445/74-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +47 -0
- package/lessons/woowacourse-racingcar/rhthrhrl0/31-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +43 -0
- package/lessons/woowacourse-racingcar/rosemin928/138-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/rosemin928/171-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +38 -0
- package/lessons/woowacourse-racingcar/s6m1n/120-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +54 -0
- package/lessons/woowacourse-racingcar/s6m1n/91-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +62 -0
- package/lessons/woowacourse-racingcar/s9hn/53-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +44 -0
- package/lessons/woowacourse-racingcar/s9hn/78-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +43 -0
- package/lessons/woowacourse-racingcar/sh1mj1/107-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/sh1mj1/115-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +61 -0
- package/lessons/woowacourse-racingcar/songpink/119-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +51 -0
- package/lessons/woowacourse-racingcar/songpink/98-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +55 -0
- package/lessons/woowacourse-racingcar/sujin9/52-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +48 -0
- package/lessons/woowacourse-racingcar/sujin9/61-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +47 -0
- package/lessons/woowacourse-racingcar/tmdgh1592/47-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +49 -0
- package/lessons/woowacourse-racingcar/tmdgh1592/64-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/tobae-time/151-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +51 -0
- package/lessons/woowacourse-racingcar/tobae-time/167-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +43 -0
- package/lessons/woowacourse-racingcar/whk06061/41-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +58 -0
- package/lessons/woowacourse-racingcar/wondroid-world/136-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +49 -0
- package/lessons/woowacourse-racingcar/wondroid-world/166-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +50 -0
- package/lessons/woowacourse-racingcar/yrsel/139-1/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +53 -0
- package/lessons/woowacourse-racingcar/yujamint/83-2/353/213/250/352/263/204-/354/236/220/353/217/231/354/260/250-/352/262/275/354/243/274.md +59 -0
- package/package.json +46 -0
- package/skills/oh-my-oop/SKILL.md +39 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# PR #80 — [채드] 1단계 로또
|
|
2
|
+
|
|
3
|
+
- **Reviewee:** [@dpcks0509](https://github.com/dpcks0509)
|
|
4
|
+
- **PR:** https://github.com/woowacourse/kotlin-lotto/pull/80
|
|
5
|
+
- **단계:** 1단계
|
|
6
|
+
- **리뷰어:** @ghojeong (파이로)
|
|
7
|
+
- **라인 코멘트:** 15
|
|
8
|
+
|
|
9
|
+
## 잘한 점 (설계 관점)
|
|
10
|
+
|
|
11
|
+
- **`enum Rank(countOfMatch, winningAmount)`** — 등수+상금 응집.
|
|
12
|
+
- **`Lottos(List<Lotto>)`, `Lotto(List<Int>)` 일급 컬렉션 시도** — 비록 책임 없어 폐기 권고 받음.
|
|
13
|
+
- **`InputValidator` 예외 *세세한 테스트*** — 리뷰어 칭찬: "*예외 처리에 대해 세세한 테스트를 남겨 주신 점 좋습니다.* 👍 보통 *성공 시나리오에 대해서만 테스트하지, 실패했을 예외에 대한 테스트는 귀찮아할 수 있는데 꼼꼼*하시네요. 👍".
|
|
14
|
+
|
|
15
|
+
## 못한 점 (설계 관점)
|
|
16
|
+
|
|
17
|
+
### 1. 일급 컬렉션 = *역할 + 책임* 있을 때만
|
|
18
|
+
|
|
19
|
+
```kotlin
|
|
20
|
+
class Lottos(val publishedLottos: List<Lotto>) {
|
|
21
|
+
override fun toString(): String {
|
|
22
|
+
return publishedLottos.joinToString("\n")
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
open class Lotto(val numbers: List<Int>) {
|
|
27
|
+
override fun toString(): String {
|
|
28
|
+
return numbers.sorted().joinToString(", ", "[", "]")
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
학생 의문: "*일급 컬렉션을 어떨 때 사용해야 하는지 기준*과 *어디까지 일급 컬렉션을 사용해야 하는지* 이해가 잘 가지 않습니다. 예를 들어서, *List<Int> numbers를 의미하는 Lotto를 만들고 List<Lotto>를 의미하는 Lottos를 또 다시 일급 컬렉션으로 만드는 게 맞는지* 궁금합니다."
|
|
34
|
+
|
|
35
|
+
리뷰어: "필요하다면 *끊임없이 일급 컬렉션을 만들어 나가야* 합니다. 그리고 *만약 새로 만든 일급 컬렉션이 불편하다면 일급 컬렉션으로 만들지 않으면 됩니다*. 가령 저는 안드로이드 개발을 처음 할 때 *테트리스 게임*을 만들었었는데, *중첩된 일급 컬렉션을 만들기 싫어서, 1차원 리스트(배열)만을 활용*하여, 2차원으로 렌더링 되는 테트리스를 그렸습니다. 그리고 제 맹세컨대 *1차원으로 테트리스를 개발하니 중첩된 2차원의 일급 컬렉션보다 훨씬 어려워서 고생*을 했습니다. 만약 3차원의 물체를 다루는 안드로이드 앱을 만들게 된다면, 저는 *일급 컬렉션의 일급 컬렉션의 일급 컬렉션*을 만들겁니다. ㅋㅋㅋㅋ"
|
|
36
|
+
|
|
37
|
+
리뷰어 (별도 위치, 핵심): "*반면 저는 중첩된 일급 컬렉션이 문제가 아니라, 채드의 일급 컬렉션의 역할과 책임이 없어서 문제*가 되는 것 같습니다. *일급 컬렉션을 만드는 이유는 객체가 하나일 때와, 그 객체가 여러 개일 때, 할 수 있는 역할과 책임이 달라지고, 그에 따라 테스트가 필요한 개성 있는 메서드를, 일급 컬렉션이 가지게 되기 때문*입니다. 하지만 제가 느끼기에 *Lotto와 Lottos 모두, toString만 override가 되어 있을 뿐 테스트가 필요한 비즈니스 로직이 존재하지 않*습니다. 그렇기에 제 입장에서 *Lotto와 Lottos 둘 다 일급 컬렉션으로 분류하지 않*습니다."
|
|
38
|
+
|
|
39
|
+
→ **테스트할 비즈니스 로직 = 일급 컬렉션 자격 기준.**
|
|
40
|
+
|
|
41
|
+
### 2. `Lotto` `open class` + 상속 — 의도 불명
|
|
42
|
+
|
|
43
|
+
```kotlin
|
|
44
|
+
open class Lotto(val numbers: List<Int>) {. }
|
|
45
|
+
|
|
46
|
+
open class WinningLotto(private val winningNumbers: List<Int>, private val bonusNumber: Int) : Lotto(winningNumbers) {
|
|
47
|
+
.
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
리뷰어: "Lotto와 WinningLotto를 *open class로 하신 의도*가 궁금합니다. *kotlin이 class를 상속하려면 open이라는 키워드를 필요하게끔 한 이유*는, *abstract class와 interface가 아닌 일반 클래스는 되도록 상속을 하지 않도록 프로그래밍 언어적으로 제약을 걸으려 하였기 때문*입니다. *어떤 유용함 때문에 제약을 해제하였는지 채드의 의도*가 궁금합니다." 학생: "Lotto에서 numbers를 가지고 있고 WinningLotto에서도 lotto의 numbers를 사용하니 *Lotto를 상속받아서 이용하면 좋을 것이라고 생각*했는데 피드백을 보고 다시 보니 *상속을 할 이유가 없어 보여서 해제*하였습니다." — *데이터 재사용을 위한 상속*은 안티. *합성*이 자연.
|
|
52
|
+
|
|
53
|
+
### 3. `Buyer.lateinit var lottos` — *세터 메서드*가 본질
|
|
54
|
+
|
|
55
|
+
```kotlin
|
|
56
|
+
class Buyer(private val purchaseAmount: Int) {
|
|
57
|
+
val numberOfLotto: Int = calculateNumberOfLotto()
|
|
58
|
+
lateinit var lottos: Lottos // ⚠
|
|
59
|
+
|
|
60
|
+
fun buyLottos(lottos: Lottos) {
|
|
61
|
+
this.lottos = lottos
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
리뷰어: "lottos를 굳이 *lateinit var로 선언한 이유*가 궁금합니다. 저에게 *buyLottos는 단순한 세터 메서드처럼 느껴지기 때문*입니다." 학생: "Buyer 클래스 안에서 lottos를 생성하기 보다는 *바깥에서 생성된 lottos를 받아와서 선언해 주는 게 좋겠다*고 생각해서 *lateinit var을 사용해서 나중에 초기화*하려고 생각했습니다. 코드를 리팩토링하다 보니 *Buyer 클래스는 필요가 없다고 판단되어 제거*하고 calculateNumberOfLotto 메소드를 외부에 선언하였습니다." — *역할 없는 객체* 폐기.
|
|
67
|
+
|
|
68
|
+
### 4. `Rank.countOfMatch`만 *매직넘버* — 일관성 부재
|
|
69
|
+
|
|
70
|
+
```kotlin
|
|
71
|
+
enum class Rank(val countOfMatch: Int, val winningAmount: Int) {
|
|
72
|
+
FIRST(6, Constant.FIRST_RANK_WINNING_MONEY), // ⚠ 6은 매직, FIRST_RANK는 상수
|
|
73
|
+
SECOND(5, Constant.SECOND_RANK_WINNING_MONEY),
|
|
74
|
+
.
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
리뷰어: "winningAmount는 Constant에 상수로 정의했지만, *countOfMatch는 Rank enum 안에 매직넘버로 사용하신 이유*가 있나요? *countOfMatch와 winningAmount가 어떻게 다른지, 왜 다르게 구현*하셨는지 채드의 기준이 궁금해서 여쭈어봅니다." 학생: "*countOfMatch는 Rank 클래스 안에서만 사용*되고, *winningAmount가 다른 클래스에서도 사용*될 줄 알고 상수 선언을 했었습니다. 그런데 Rank 파일에서만 winningAmount가 사용이 되어서 *Rank 파일 내에서 상수 선언을 하려 했는데 enum class 안에서는 상수 선언이 안 된다고 하여 상수 선언하는 것을 해제*하였습니다." — *enum 자체가 상수*. companion object에 const 가능.
|
|
79
|
+
|
|
80
|
+
### 5. `WinningStatistic.toString` — 다국어 + 모델 변경 위험
|
|
81
|
+
|
|
82
|
+
```kotlin
|
|
83
|
+
class WinningStatistic(val result: Pair<Rank, Int>) {
|
|
84
|
+
override fun toString(): String {
|
|
85
|
+
.
|
|
86
|
+
return "${rank.countOfMatch}개 일치${", 보너스 볼 일치".takeIf { result.first.ordinal == 1 } ?: " "}(${rank.winningAmount}원)- ${count}개"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
리뷰어 (1차): "*toString만을 override하는 클래스가 모델에서 굉장히 많이* 보입니다. 안드로이드 앱을 개발하다가 *앱이 잘 나가면 다국어 지원을 해야 하는 경우*가 종종 생깁니다. 만약 *한국어로 만든 앱이 잘 되어서, 영미권에 동일한 앱으로 진출*을 하려 한다면 *어떻게 언어 확장*을 하려 하시나요?"
|
|
92
|
+
|
|
93
|
+
학생 자가 질문: "*toString을 override해서 출력 형식을 커스텀하는 것과 get메소드를 새로 추가하여 String을 반환하는 것으로 하는 것의 차이*가 있는지 궁금합니다. *다국어 지원을 할 때 언어 확장을 할 수 없는 단점 때문에 toString을 지양*하는 것이 좋은 건가요?"
|
|
94
|
+
|
|
95
|
+
리뷰어: "*저는 별로 차이가 없다고 생각*합니다. 아뇨. *근본적으로는 문구 수정을 할 때 model의 소스코드를 까서 수정해야 한다는 점이 단점*입니다. 가령 채드가 *유일한 안드로이드 개발자*인 상황에서, 기획자가 문구를 더 자극적으로 바꾸었다고 가정해 봅시다. (안드로이드는 팀이 굉장히 작은 경우가 종종 있습니다.) *'XXX개를 맞추었고, 보너스 볼! 까지 맞추었습니다!!'* 그 상황에 *채드가 휴가*를 갔다고 가정해 봅시다. *model은 비즈니스 로직을 담고 있기에, 출근한 동료 개발자는 굉장히 신중하게 수정*하려 할 겁니다. *만약 잘못 수정하면 화면에 표시가 잘못되는 것을 넘어, 중대한 버그나 크래시를 일으킬 수 있기 때문*입니다. 동료는 *간단한 문구 수정임에도 불구하고 채드에게 WinningStatistic 모델만 수정하면 될지, 혹시 더 추가로 살펴봐야 하는 모델은 없는지 코드 리뷰 요청*을 보낼 수 있습니다. *휴가 중에 동료의 요청을 채드가 즐겁게 대응할 수 있다면, 문제가 없*습니다. 하지만 저는 *개인적으로 별로 즐겁지 않았기에 제 개인 경험을 공유*해 보았습니다." — *경험 기반 가르침*.
|
|
96
|
+
|
|
97
|
+
### 6. Controller의 `makeRandomNumbers` + `drawSixNumbers` *불필요한 분리*
|
|
98
|
+
|
|
99
|
+
```kotlin
|
|
100
|
+
private fun generateLottoNumbers(): List<Int> {
|
|
101
|
+
val numberGenerator = NumberGenerator()
|
|
102
|
+
val randomNumbers = numberGenerator.makeRandomNumbers()
|
|
103
|
+
return numberGenerator.drawSixNumbers(randomNumbers) // ⚠ 항상 연달아
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
리뷰어: "어차피 *makeRandomNumbers와 drawSixNumbers는 반드시 같이 쓰일 수밖에 없는데, 굳이 메서드를 분리한 이유*가 있나요? *분리함으로 인해 어떤 유연함 혹은 장점이 생겼는지* 궁금합니다." 학생: "한 메소드에서 *한 가지 행동만 하게 하려고 두 가지 메소드로 분리* 하였는데 *굳이 불필요한 행동이었던 것* 같습니다. 따로 *LottoGenerator object를 생성하여 하나의 메소드로 통일*시켜 보았습니다!" — *SRP 오해*: *함수 한 줄 = 한 책임*이 아니라 *변경 이유 한 가지*.
|
|
108
|
+
|
|
109
|
+
## 리뷰어 의견 요지
|
|
110
|
+
|
|
111
|
+
- **일급 컬렉션의 정당화 = *비즈니스 로직 + 테스트 메서드*** (역할/책임).
|
|
112
|
+
- **중첩 일급 컬렉션은 OK** — *각자 책임 있으면*.
|
|
113
|
+
- **상속 사용에 *유용함 근거* 필요** — 데이터 재사용은 상속 X.
|
|
114
|
+
- **`lateinit var` = *세터 메서드 신호*** — *역할 검토*.
|
|
115
|
+
- **`enum`의 `countOfMatch` vs `winningAmount` *일관성*** — 상수 처리 통일.
|
|
116
|
+
- **`toString()` 다국어 문제 + *휴가 시 동료가 model 수정 위험*** = View로.
|
|
117
|
+
- **함수 *항상 같이 쓰임* = SRP 오해**.
|
|
118
|
+
|
|
119
|
+
- **대표 인용:**
|
|
120
|
+
> 반면 저는 중첩된 일급 컬렉션이 문제가 아니라, *채드의 일급 컬렉션의 역할과 책임이 없어서 문제*가 되는 것 같습니다.
|
|
121
|
+
> — @ghojeong on `src/main/kotlin/model/Lotto.kt`
|
|
122
|
+
|
|
123
|
+
> kotlin이 class를 상속하려면 open이라는 키워드를 필요하게끔 한 이유는, *abstract class와 interface가 아닌 일반 클래스는 되도록 상속을 하지 않도록 프로그래밍 언어적으로 제약을 걸으려 하였기 때문*입니다.
|
|
124
|
+
> — @ghojeong on `src/main/kotlin/model/WinningLotto.kt`
|
|
125
|
+
|
|
126
|
+
> model은 비즈니스 로직을 담고 있기에, *출근한 동료 개발자는 굉장히 신중하게 수정*하려 할 겁니다. 만약 잘못 수정하면 *화면에 표시가 잘못되는 것을 넘어, 중대한 버그나 크래시를 일으킬 수 있기 때문*입니다.
|
|
127
|
+
> — @ghojeong on `src/main/kotlin/model/WinningStatistic.kt`
|
|
128
|
+
|
|
129
|
+
> 어차피 makeRandomNumbers와 drawSixNumbers는 *반드시 같이 쓰일 수밖에 없는데, 굳이 메서드를 분리한 이유*가 있나요?
|
|
130
|
+
> — @ghojeong on `src/main/kotlin/controller/LottoController.kt`
|
|
131
|
+
|
|
132
|
+
## 얻은 교훈
|
|
133
|
+
|
|
134
|
+
- **일급 컬렉션 = *행동/책임 있을 때***. 단순 `List` 감싸기 + `toString`은 자격 미달. *테스트할 메서드*가 신호.
|
|
135
|
+
- **중첩 일급 컬렉션 OK** — *테트리스 1차원 vs 2차원* 비유. 자연스러운 차원만큼 감싸기.
|
|
136
|
+
- **상속은 *재사용 도구 아님*** — Kotlin이 `open` 키워드 강제하는 건 *상속 회피*가 기본 방침이라는 언어적 신호.
|
|
137
|
+
- **`lateinit var = 세터 메서드*** — *나중에 초기화*는 *순서 의존*. 책임이 *외부 입력 수용*이라면 *생성자 인자*로.
|
|
138
|
+
- **`enum` 자체가 상수** — 별도 상수로 다시 뽑을 필요 X. *일관성*은 *내부 표현 결정*에 따라.
|
|
139
|
+
- **`toString()` 문구 = *모델 변경 위험*** — *휴가 중 동료가 model 수정*하려면 *부담*. View 매핑이 *안전*.
|
|
140
|
+
- **함수 *항상 같이 쓰임* = 한 함수일 수 있음** — SRP는 *변경 이유 한 가지*이지 *함수 한 줄*이 아님.
|
|
141
|
+
- **예외 테스트도 *경계값 다수*** — `"-1000"`, `"0"`, `"999"`, `" 1000 "`, `" "`, `""`, `"oneThousand"` 같은 *다양한 실패 경로*가 *명세*.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# PR #87 — [채드] 2단계 로또
|
|
2
|
+
|
|
3
|
+
- **Reviewee:** [@dpcks0509](https://github.com/dpcks0509)
|
|
4
|
+
- **PR:** https://github.com/woowacourse/kotlin-lotto/pull/87
|
|
5
|
+
- **단계:** 2단계 (수동 로또)
|
|
6
|
+
- **리뷰어:** @ghojeong (파이로)
|
|
7
|
+
- **라인 코멘트:** 28 (리뷰어 23 + 학생 5)
|
|
8
|
+
|
|
9
|
+
## 잘한 점 (설계 관점)
|
|
10
|
+
|
|
11
|
+
- **매직 넘버 *모두 상수화*** — 칭찬: "*매직 넘버들을 모두 상수로 훌륭하게 선언*해 주셨군요. 👍".
|
|
12
|
+
- **`Strategy 패턴` 자발 도입** — 리뷰어 칭찬: "*전략 패턴이라는 키워드를 알려드리지 않았음에도, 스스로 적용해 주시다니 대단*하시네요. *다른 크루분들께 정보를 공유받으셨을까요? 아니면 직접 생각해 내셨나요*?"
|
|
13
|
+
- **수동 로또 *테스트 작성*** — 칭찬: "*수동 로또 발행에 대해서도 꼼꼼히 테스트를 작성해 주신 점 좋습니다*. 👍".
|
|
14
|
+
|
|
15
|
+
## 못한 점 (설계 관점)
|
|
16
|
+
|
|
17
|
+
### 1. *도메인 init vs 입력 검증* — 이중일?
|
|
18
|
+
|
|
19
|
+
학생: "*입력을 받을 때 입력값 검증을 통해 잘못된 값이 들어오면 오류를 발생하도록 구현을 해 놓아도 model 안에서도 init, require를 통해서 값을 검증하는 것이 좋은 방법인 건지, 이중일을 하는 불필요한 행동인지* 궁금합니다."
|
|
20
|
+
|
|
21
|
+
리뷰어: "*저는 값의 검증에 관해서는 중복되어 보이더라도 무조건 여러 번 하는 것을 좋아*합니다. *view와 model은 지금은 패키지만 분리되어 있지만, 최종적으로는 별개의 모듈로 분리될 수 있다고 생각*하시면 좋습니다. 만약 *채드님이 취업하신 조직의 부서 구조가 셀 단위가 아니라, 모듈 단위로 부서가 분리되어 있을 경우, model을 담당하는 부서와 view를 담당하는 부서가 아예 다를 수* 있습니다. 그리고 저는 *팀원에게는 강한 신뢰를 갖지만, 외부 조직 및 부서는 같은 팀이 아니기에 실체를 알 수 없는 블랙박스*라 생각합니다. 즉 *view를 담당하는 부서에서 입력값 검증을 했다고는 하지만, 실제로는 제대로 검증을 안 하는 경우*가 생깁니다. 설사 채드님이 view와 model을 둘 다 구현하셨다고 하더라도, *model보다 더 빈번히 수정되는 view는 언제든지 다른 누군가에 의해 검증 로직이 잘못되거나 삭제될 수* 있습니다. 그러므로 저는 *차라리 배포 전에 테스트를 실패하거나 동작이 실패하게끔, model과 view 상관없이 검증 로직을 최대한 많이 넣는 편*입니다." — *부서 단위 분리* + *외부 조직 블랙박스* 시나리오.
|
|
22
|
+
|
|
23
|
+
### 2. `toString` 남용? — MVC 분리
|
|
24
|
+
|
|
25
|
+
학생: "*model에 대한 출력을 할 때 저는 toString을 통해서 출력에 대해 컨트롤하였는데 다른 크루들은 toString을 많이 안 써서 구현을 했던데 toString을 남용하는 것이 안 좋은 습관인지, get 함수를 통해서 그냥 model에서 값만 받아오고 outputView에서 값을 가공해서 출력하는 것이 더 좋은 방법인지* 궁금합니다."
|
|
26
|
+
|
|
27
|
+
리뷰어: "*이전과 달리, 문구를 const로 선언해 주신 점 좋*습니다. 👍 *toString을 남용하는 것은 안 좋은 습관이 아닙니다. toString이 view와 연관이 깊다는 게 문제*입니다. *MVC의 핵심은 Model과 View가 얼마나 잘 분리되어 있는지*이고, *이 분리는 model에서 제공하는 함수 및 값이 view의 요구사항 변경에 영향을 받으면 안 된다는 것을 뜻*합니다. *문구의 수정은 개발팀의 리팩토링이 아닌, 기획팀의 문구 첨삭을 통해서 이루어*집니다. 그렇기에 저는 *모델에서 한국어 문구를 렌더링하는 모든 문구를 view의 영역이라고* 보고 있습니다." 추가: "*개발에 더 좋은 방법은 없습니다. 상황에 필요한 방법만이 있을 뿐*입니다. *돈을 받고 일하는 입장에서, 만약 돈을 주는 조직이 model에서 toString을 통해 문구를 렌더링하라는 요구를 한다면 저는 채드가 구현하신 것처럼 할 것*입니다." — *조직 요구사항 우선*.
|
|
28
|
+
|
|
29
|
+
### 3. `object` vs `class` — 블로그 인용
|
|
30
|
+
|
|
31
|
+
학생: "*view에 대한 파일들을 object로 선언한 크루도 있고 class로 선언한 크루도 있던데, object와 class를 선언하는 기준을 어떻게 잡아야 하는지* 궁금합니다."
|
|
32
|
+
|
|
33
|
+
리뷰어: "관련해서 *[안드로이드 6기 동기분이 좋은 블로그 글](https://velog.io/@kmkim2689/Kotlin-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%B3%80%EC%88%98%EA%B0%80-%EC%97%86%EB%8A%94-%EA%B2%BD%EC%9A%B0-class%EC%99%80-object-%EC%A4%91-%EC%96%B4%EB%96%A4-%EA%B2%83%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%A0%EA%B9%8C)을 쓰셨던데*, 참고를 하시면 좋을 것 같습니다. 제가 위 블로그 글에서 제일 마음에 들었던 부분은 *'No Silver Bullet'*입니다. 제 정말 솔직한 심정은 *'어느 쪽으로 해도 크게 상관없다, 성능 상 크게 차이도 없으며, 설사 있더라도 그 장단점을 느끼기에는 프로젝트의 요구사항이 많지 않다.'*입니다. *view에 대해서 너무 시간을 쏟기 보다는 model에 대해서 시간을 더 쏟으면 좋을 텐데*입니다. *view는 보통 직접 구현하는 경우가 없고, 전적으로 프레임워크에 의존하게 되는 경우가 많*습니다."
|
|
34
|
+
|
|
35
|
+
### 4. 테스트 패키지 = main 미러링
|
|
36
|
+
|
|
37
|
+
리뷰어: "*test/kotlin 내부의 패키지 구조를 main/kotlin의 패키지 구조와 동일하게* 해 주세요. 가령 *model과 관련 테스트들은 test/kotlin/model 안에 배치하면, 테스트에 필요한 model 클래스를 import할 필요가 없게* 됩니다."
|
|
38
|
+
|
|
39
|
+
### 5. *테스트 클래스 1:1 매핑*
|
|
40
|
+
|
|
41
|
+
리뷰어: "*테스트 클래스 명은, 테스트하려는 클래스와 1대 1 매핑이 되도록* 해 주시면 좋습니다. 가령 *InputTest는 실제로는 InputValidator에 대한 테스트를 해 주고 있으므로, InputValidatorTest라고 이름을 바꿔* 주시면 좋습니다.".
|
|
42
|
+
|
|
43
|
+
### 6. *모델 책임 = 테스트 가능성*
|
|
44
|
+
|
|
45
|
+
리뷰어: "*모델의 역할과 책임이 잘 나누어져 있는가? 를 판단할 때, 저는 '각 모델 클래스 별로 유의미한 테스트를 편하게 작성할 수 있는가?'로 판단*합니다. *모델 클래스에서 테스트하는 내용이, 곧 그 모델의 역할과 책임이기 때문*입니다. *테스트 주도 개발*을 하면 *테스트를 실제 구현보다 앞서 우선시*해야 합니다. *모델의 역할과 책임 또한 그 모델의 실제 구현 로직이 아니라, 테스트를 통해 판단할 수 있어야* 합니다. 왜냐하면 *테스트를 실패했을 시, 요구사항이 바뀐 게 아니라면 수정해야 할 것은 테스트가 아니라 실제 구현체이기 때문*입니다." + "*특히 model 안에 있는 LottoGenerator로 인하여 테스트가 어려우리라 생각*되는데, *자동차 미션 때와 마찬가지로, 로또 미션에서 학습해 내야 하는 모델 설계의 핵심*이라고 강조해 봅니다."
|
|
46
|
+
|
|
47
|
+
### 7. `Pair` *값 의미 모호*
|
|
48
|
+
|
|
49
|
+
```kotlin
|
|
50
|
+
val result: Pair<Int, Int> =. // ⚠ first가 뭐고 second가 뭐?
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
리뷰어: "*제가 채드님의 동료라고 생각했을 때, pair의 값이 무슨 값인지 예상하기 어렵다는 생각*이 듭니다.. *다른 동료가 이 코드를 유지보수할 때는 pair의 값(pair.first와 pair.second)을 로직을 확인해야 알 수* 있지 않을까요? 관련 글을 하나 추천해 봅니다. *[Prefer Data Classes Over Pairs](https://proandroiddev.com/prefer-data-classes-over-pairs-42b8a39e5e37)*" — *Pair*는 *호출자 컨벤션 의존*. *data class*는 *필드 이름*.
|
|
54
|
+
|
|
55
|
+
### 8. *매직넘버 -1, 0*
|
|
56
|
+
|
|
57
|
+
리뷰어: "*-1은 매직넘버이군요. model, view, util 상관없이 매직넘버는 피해* 주세요. *null의 경우에는 차라리 exception을 던져서 예외 처리를 하는 것은 어떨까요*?" + "*0도 마찬가지로 매직넘버라 생각하고 상수화* 부탁드립니다!"
|
|
58
|
+
|
|
59
|
+
### 9. `drawWinningLotto` *네이밍* — UI인 줄
|
|
60
|
+
|
|
61
|
+
리뷰어: "*컨트롤러에서 drawWinningLotto라는 이름을 보았을 때, displayPurchaseResult나, analyzeWinningStatics와 마찬가지로, 리턴값 없이 view와 상호작용하는 메서드라고 생각*했습니다. *drawWinningLotto보다 좀 더 좋은 네이밍을 고민*해 주시면 좋을 것 같습니다." — `draw`는 *그리다*로 오해.
|
|
62
|
+
|
|
63
|
+
### 10. `LottoPrize.ordinal` 의존 — enum 추가 시 위험
|
|
64
|
+
|
|
65
|
+
```kotlin
|
|
66
|
+
result[lottoPrize.ordinal] =. // ⚠ ordinal에 의존
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
리뷰어: "*로또 규칙에 따라 LottoPrize enum 숫자가 정해져 있기 때문에, 지금은 ordinal을 result list에 넣어도 상관이 없어 보입니다*. 하지만 이렇게 규칙이 명확하고 정해져 있으면 상관없지만, *개발을 하다 보면 기획 변경에 따라 enum은 바뀔 여지가 있고, 위와 같은 코드는 enum 추가, 수정, 삭제될 때마다 유지보수 중 실수를 발생시킬 수* 있어 보입니다. *enum을 추가하거나 삭제하더라도 수정할 코드가 없도록 개선*해 주시면 좋을 것 같습니다." — *ordinal 의존성*은 *순서 깨짐 위험*.
|
|
70
|
+
|
|
71
|
+
### 11. `repeat + index` 활용 vs `forEach`
|
|
72
|
+
|
|
73
|
+
리뷰어: "*index를 직접 사용해야 하는 상황이라면, repeat 문이나 전통적인 for 문이나 별 차이가 없어 보입니다*. *repeat 대신 차라리 forEach를 활용*하면 어떨지 제안해 봅니다."
|
|
74
|
+
|
|
75
|
+
### 12. *exception 던지기 vs 예외 처리* — 리뷰어 *자기 정정*
|
|
76
|
+
|
|
77
|
+
리뷰어: "exception을 던져 보면 어떨까? 라는 피드백을 드렸는데, *잘못된 입력을 했을 때, 앱을 강제 종료하는 것을 원하는 상황이 아니므로, exception은 던지지 않도록 하는 게 맞는 것* 같습니다. *과제를 다시 자세히 읽어 보다가, 힌트 중에 '예외 처리를 통해 에러가 발생하지 않도록 한다.' 조건이 있어서 피드백 수정*하려고 연락드렸습니다." + "*혹시 제가 드린 피드백 중에서 궁금하거나 납득이 어려운 부분이 있다면, 언제든 질문주세요! 제가 실수를 했을 수도 있으니까요*!" — *리뷰어의 솔직한 자기 정정*. 패턴.
|
|
78
|
+
|
|
79
|
+
### 13. *수동/자동 발행 통합* — 인터페이스 추상화
|
|
80
|
+
|
|
81
|
+
리뷰어: "*수동 로또 발행은 publishHandpickedLottos를 통해서, 자동 로또 발행은 lottos.publishLottos로 하고 있는 듯이* 보입니다. *깊게 생각해 보면 '수동 로또 발행'과 '자동 로또 발행'은 수동으로 사용자가 입력해서 숫자를 생성하느냐, 자동으로 랜덤 라이브러리가 숫자를 생성하느냐에 대한 차이만이 있을 뿐 둘 다 똑같은 '로또 발행'이라는 것*을 알 수 있습니다. *'수동 로또 발행'과 '자동 로또 발행'이 최대한 동일한 로직, 동일한 함수, 동일한 객체, 동일한 인터페이스를 활용할 수 있도록 리팩토링에 도전*해 보시기 바랍니다. *힌트는 '로또 발행'에 관련한 인터페이스를 선언해서 implements해 보기*를 바랍니다."
|
|
82
|
+
|
|
83
|
+
후속 (재사용 범위 명확화): "동일하다는 말이 잘못 전달될까봐 한 번 더 말씀드리면, *제가 동일한 함수와 동일한 객체를 사용해 달라는 말은, 재사용 가능한 함수와 객체의 영역을 넓혀 달라는 말*이었습니다. *재사용성이 핵심이지 동일해야 한다가 핵심은 아닙니다*. 의도를 다시 한 번 말씀드려봅니다."
|
|
84
|
+
|
|
85
|
+
### 14. *get/toString 테스트* — 빡세게 vs 실용
|
|
86
|
+
|
|
87
|
+
리뷰어: "*get이나 toString이 테스트할 로직이 없을만큼 간단하다면 괜찮*습니다. *연습해 보시라는 의미에서 빡세게 피드백 드렸지만 꼭 전부 지킬 필요는 없*습니다."
|
|
88
|
+
|
|
89
|
+
### 15. `package lotto` 최상위 추가 이유
|
|
90
|
+
|
|
91
|
+
리뷰어: "*리팩토링을 하시면서 최상위에 package lotto를 새로 만드신 이유가 있*나요?"
|
|
92
|
+
|
|
93
|
+
## 리뷰어 의견 요지
|
|
94
|
+
|
|
95
|
+
- **이중 검증 *환영*** — *부서 분리 시나리오*.
|
|
96
|
+
- **`toString` *MVC 분리 문제* — model 변경 ≠ 기획 문구 변경**.
|
|
97
|
+
- **`object` vs `class` *No Silver Bullet*** — view는 *프레임워크 의존*이라 *고민 가치 낮음*.
|
|
98
|
+
- **테스트 패키지 = main 미러링**.
|
|
99
|
+
- **테스트 클래스 = *1:1 매핑***.
|
|
100
|
+
- **모델 *책임 = 테스트 가능성***.
|
|
101
|
+
- **`Pair` → *data class***.
|
|
102
|
+
- **매직 넘버 *전부 상수화***.
|
|
103
|
+
- **`drawWinningLotto` *네이밍 오해***.
|
|
104
|
+
- **`enum.ordinal` 의존 위험**.
|
|
105
|
+
- **`repeat + index` → `forEach`**.
|
|
106
|
+
- **수동/자동 발행 *재사용성 확장***.
|
|
107
|
+
|
|
108
|
+
- **대표 인용:**
|
|
109
|
+
> 저는 *값의 검증에 관해서는 중복되어 보이더라도 무조건 여러 번 하는 것을 좋아*합니다.. *외부 조직 및 부서는 같은 팀이 아니기에 실체를 알 수 없는 블랙박스*라 생각합니다.
|
|
110
|
+
> — @ghojeong on `src/main/kotlin/util/InputValidator.kt`
|
|
111
|
+
|
|
112
|
+
> MVC의 핵심은 Model과 View가 얼마나 잘 분리되어 있는지이고, *이 분리는 model에서 제공하는 함수 및 값이 view의 요구사항 변경에 영향을 받으면 안 된다는 것을 뜻*합니다.
|
|
113
|
+
> — @ghojeong on `src/main/kotlin/model/WinningStatistic.kt`
|
|
114
|
+
|
|
115
|
+
> 모델의 역할과 책임이 잘 나누어져 있는가?를 판단할 때, 저는 *'각 모델 클래스 별로 유의미한 테스트를 편하게 작성할 수 있는가?'로 판단*합니다.
|
|
116
|
+
> — @ghojeong on `src/test/kotlin/model/LottoTest.kt`
|
|
117
|
+
|
|
118
|
+
> 재사용성이 핵심이지 *동일해야 한다가 핵심은 아닙니다*.
|
|
119
|
+
> — @ghojeong on `src/main/kotlin/model/LottoGenerator.kt`
|
|
120
|
+
|
|
121
|
+
## 얻은 교훈
|
|
122
|
+
|
|
123
|
+
- ***이중 검증 환영*** — *모듈 분리 시나리오*. *외부 부서 = 블랙박스*. *View 변경 잦음* → *Model 자체 invariant*가 *최후 안전망*.
|
|
124
|
+
- **`toString` *MVC 분리 위반* 여부 = 조직 요구사항 + Model 영향도**.
|
|
125
|
+
- **`object` vs `class` *No Silver Bullet*** — *view는 프레임워크 의존*이므로 *큰 차이 없음*. *시간은 Model에*.
|
|
126
|
+
- **테스트 패키지 = *main 미러링*** — *import 없이 같은 패키지 내에서 접근*. *위치 발견* 비용.
|
|
127
|
+
- **테스트 *클래스 1:1*** — `InputTest` X, `InputValidatorTest` O.
|
|
128
|
+
- **모델 *책임 = 테스트 가능성*** — *테스트할 수 있는 게 책임*. *TDD*의 본질.
|
|
129
|
+
- **`Pair` *호출자 컨벤션* → *data class*** — `pair.first`/`pair.second`는 *위치 의존*. *data class*는 *이름 의존*.
|
|
130
|
+
- **매직 넘버 *모든 곳에서* 상수화** — `-1`, `0`도. *시그널이 있는 숫자*는 *상수*.
|
|
131
|
+
- **`drawWinningLotto` 네이밍** — *draw = 그리다*로 오해. *getWinningLotto*나 *receiveWinningLotto*.
|
|
132
|
+
- **`enum.ordinal` 의존 = *순서 깨짐 위험*** — *enum 추가/삭제* 시 *모든 ordinal 의존 코드 깨짐*. PR #131 (krrong) *else 회피*와 본질 같음.
|
|
133
|
+
- **`repeat + index` 어색하면 `forEach`** — Kotlin idiom. *index가 진짜 필요한지* 의심.
|
|
134
|
+
- **수동/자동 *재사용성 확장*** — *동일 인터페이스 활용 영역 넓히기*. *완전 동일 강제 X*.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# PR #112 — [로또 1단계] 타마
|
|
2
|
+
|
|
3
|
+
- **Reviewee:** [@etama123](https://github.com/etama123)
|
|
4
|
+
- **PR:** https://github.com/woowacourse/kotlin-lotto/pull/112
|
|
5
|
+
- **단계:** 1단계
|
|
6
|
+
- **리뷰어:** @KwonDae (제이든)
|
|
7
|
+
- **라인 코멘트:** 31
|
|
8
|
+
|
|
9
|
+
## 잘한 점 (설계 관점)
|
|
10
|
+
|
|
11
|
+
- **`Lotto` invariant 강제** — `require`로 크기/범위/중복 검증.
|
|
12
|
+
- **`LottoNumber` `data class`** — 학생 자가 학습 후 `equals`/`hashCode` 필요성 인지.
|
|
13
|
+
- **README에 기능 목록 상세** — 작업 추적 가시화.
|
|
14
|
+
- **`fun interface LottoNumberGenerator`** 도입 (이후 라운드) — 랜덤 외부 주입.
|
|
15
|
+
- **`runCatching` 기반 `retryWhenException` 유틸** 도입.
|
|
16
|
+
- **자가 의문 PR에 풍부히 명시** — *입력 검증의 위치*, *runCatching 적용*, *함수형 인터페이스 첫 도입* 등.
|
|
17
|
+
|
|
18
|
+
## 못한 점 (설계 관점)
|
|
19
|
+
|
|
20
|
+
### 1. `Lotto(val lottoNums: List<Int>)` — `LottoNumber` 객체 부재
|
|
21
|
+
|
|
22
|
+
```kotlin
|
|
23
|
+
class Lotto(val lottoNums: List<Int>) {
|
|
24
|
+
init {
|
|
25
|
+
require(lottoNums.size == DEFAULT_LOTTO_SIZE)
|
|
26
|
+
require(lottoNums.all { it in MIN_RANGE..MAX_RANGE })
|
|
27
|
+
require(lottoNums.size == lottoNums.toSet().size)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
리뷰어: "lottoNums의 각 원소들이 1~45 사이에 들어간다는 걸 확신할 수 있을까요? 아마 Lotto의 초기화 부분을 보지 않는다면 해당 원소가 1..45에 해당함을 쉽게 알지 못할 겁니다." 학생: "Lotto 객체를 만들고 보니 내부에 들어가는 수도 LottoNumber 객체를 만드는 것이 낫겠다 !!" — 후속 라운드에서 `LottoNumber` 분리.
|
|
33
|
+
|
|
34
|
+
### 2. `Lotto` 자료구조로 중복 표현
|
|
35
|
+
|
|
36
|
+
리뷰어: "명시적으로 중복이 불가능함을 알려주기도 하면서, 자료구조를 사용하여 방지할 수 있습니다. 자동차를 예로 들어서, 2인승 자동차에 인원을 태우면서 더 이상 타지 못함을 알리는 것보다 2인승 자동차라는 것을 알려준다면 보다 효과적으로 외부에도 알리기도 쉽고 추가 검증도 필요 없겠죠?" — `Set<LottoNumber>`로 *중복 불가가 타입에 표현*.
|
|
37
|
+
|
|
38
|
+
### 3. `Purchase` 명명 — 객체의 *역할*
|
|
39
|
+
|
|
40
|
+
```kotlin
|
|
41
|
+
class Purchase(val amountOfPurchase: String) {. }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
리뷰어: "구매금액을 관리하는 객체로 PurchaseAmount..? 같은 명시적인 이름이 더 좋아 보이네요." 학생: "사용자의 입장에서 클래스 이름을 명시했던 것 같습니다. 앞으로는 **객체 그 자체의 역할**에 맞게 네이밍해 보겠습니다."
|
|
45
|
+
|
|
46
|
+
### 4. `Purchase`가 `String`을 받음 — 도메인이 입력 형식 인식
|
|
47
|
+
|
|
48
|
+
```kotlin
|
|
49
|
+
class Purchase(val amountOfPurchase: String) {
|
|
50
|
+
init {
|
|
51
|
+
require(amountOfPurchase.all { it.isDigit() })
|
|
52
|
+
.
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
리뷰어: "예외 발생 시 에러 노출로 프로그램을 종료하는 것보다, 사용자로 하여금 재시도를 하게 유도하는 건 어떨까요? 코틀린에는 runCatching이라는 게 존재하는데요 참고해 보셔도 좋습니다." — *재시도 흐름*은 *예외 던지기*가 아니라 *null/Result*.
|
|
58
|
+
|
|
59
|
+
### 5. `WinningLotto` 보너스 검증이 String 처리까지
|
|
60
|
+
|
|
61
|
+
```kotlin
|
|
62
|
+
class WinningLotto(val winningLotto: Lotto, val bounusNumber: String) { // ⚠ typo: bounus
|
|
63
|
+
init {
|
|
64
|
+
require(bounusNumber.all { it.isDigit() })
|
|
65
|
+
require(bounusNumber.isNotBlank())
|
|
66
|
+
require(!winningLotto.lottoNums.contains(bounusNumber.toInt()))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
리뷰어: "보너스 번호도 어떻게 보면 하나의 로또번호입니다. 로또번호라는 일급 컬렉션이 존재한다면 중복되는 검증은 없어질 수 있겠네요!" — `LottoNumber` 분리 후 `bonusNumber: LottoNumber`.
|
|
72
|
+
|
|
73
|
+
### 6. `LottoFactory` — 테스트 불가 (랜덤 내장)
|
|
74
|
+
|
|
75
|
+
```kotlin
|
|
76
|
+
class LottoFactory {
|
|
77
|
+
fun generateLottoNumbers(): List<Int> = (MIN..MAX).shuffled().take(LOTTO_SIZE).sorted()
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
리뷰어: "현재 LottoFactory는 테스트가 가능한 구조일까요? 테스트가 불가하다면 어떤 이유이고, 어떻게 개선할 수 있을까요?" 후속: "현재 `generateLottoNumbers()` 함수는 shuffled() 함수에 의존적이어서, 우리는 결과를 예측할 수 없습니다. 만약 로또 번호를 생성하는 알고리즘을 외부로부터 주입받는다면 어떻게 변할까요? … Functional interface를 참고해 보시어 구현해 보셨으면 좋겠습니다." — 학생 후속 라운드에서 `fun interface LottoNumberGenerator` 도입.
|
|
82
|
+
|
|
83
|
+
### 7. `InputView`/`Validator`/`Controller` — *변환과 검증 책임* 흩어짐
|
|
84
|
+
|
|
85
|
+
```kotlin
|
|
86
|
+
class InputValidator { fun validateNumber(value: String) {. } }
|
|
87
|
+
// Controller
|
|
88
|
+
val price = inputView.inputPurchasePrice()
|
|
89
|
+
val amount = Purchase(price).calculateAmountOfLottos()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
리뷰어: "객체는 입력값에 대해 스스로 검증하여 정상값을 반환하거나 그렇지 않으면 null을 반환한다(null을 반환한다는 건 어떤 의미일까?). 구입금액, 로또번호를 어디서 판단해야 할까? InputView는 split된 문자열만 파싱하여 도메인으로 던져도 되지 않을까?" — *`도메인에 맞는 타입`은 도메인의 책임*.
|
|
93
|
+
|
|
94
|
+
### 8. `runCatching` 패턴 — *두 함수 반복*
|
|
95
|
+
|
|
96
|
+
```kotlin
|
|
97
|
+
fun inputPurchasePrice(): String? {
|
|
98
|
+
return runCatching {
|
|
99
|
+
val input = readln().also { InputValidator.ValidateNumber(it) }
|
|
100
|
+
input
|
|
101
|
+
}.getOrNull()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fun retryInputPurchasePrice(): String {
|
|
105
|
+
var input: String? = null
|
|
106
|
+
while (input == null) { input = inputPurchasePrice() }
|
|
107
|
+
return input
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
학생: "runCatching을 활용해 잘못된 값 입력 시 재입력을 받을 수 있도록 코드를 작성. 하지만 하나의 입력값을 검증할 때마다 두 개의 함수 형식이 반복되는 것이 어색." → 학생이 *유틸리티 함수*로 추상화. 리뷰어: "프로그램 내에서 반드시 필요한 값이라면 `fun inputWinningNumbers(): Set<Lotto> { val input = inputView.inputWinningNumber() ?: inputWinningNumbers() }`."
|
|
112
|
+
|
|
113
|
+
### 9. 학생 리뷰 반영 누락 다수
|
|
114
|
+
|
|
115
|
+
리뷰어: "어려우셨던 걸까요? 반영이 안 된 부분이 많은데 어려우셨다면 공유 부탁드려요! 어느 부분을 어려워하시는지 알아야 타마가 완주하기까지 도움을 드릴 수 있습니다." 학생: "수정 커밋 후 push를 잊었습니다." — *반영 사실 확인*은 push 후 리뷰어 재확인까지.
|
|
116
|
+
|
|
117
|
+
### 10. `data class` vs `class` 학생 자가 학습
|
|
118
|
+
|
|
119
|
+
리뷰어 질문: "`User`/`User2` 동등성 비교 결과는?" 학생 답: "`==`는 내부적으로 `equals()`를 호출. data class는 `equals()`를 자동 생성하며 모든 프로퍼티 값이 같으면 true. 일반 클래스는 참조 비교(=== 동일). `===`는 동일 객체(참조 비교)." — *data class의 의미*를 학생이 정확히 정리.
|
|
120
|
+
|
|
121
|
+
### 11. `getWinningNumber` Controller에서 String 변환
|
|
122
|
+
|
|
123
|
+
```kotlin
|
|
124
|
+
private fun getWinningNumber(): Lotto {
|
|
125
|
+
val winningLotto: String = inputView.inputWinningNumber()
|
|
126
|
+
return Lotto(winningLotto.split(",").map {. LottoNumber(it.toInt()) })
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
리뷰어: "inputView로부터 넘어오는 타입이 `List<Int>`로 받으면 어떨까요? `val winningLotto = WinningLotto(inputView.inputWinningNumber())` WinningLotto 스스로 Int 리스트를 받아 당첨번호를 만들 수 있지 않을까요?" — Controller에서 변환 대신 *도메인에서 자기 형식*.
|
|
131
|
+
|
|
132
|
+
## 리뷰어 의견 요지
|
|
133
|
+
|
|
134
|
+
1. **`LottoNumber` 분리 = 범위 검증을 타입에 위임** — Lotto는 *크기*만 검증.
|
|
135
|
+
2. **자료구조 = invariant 표현** — Set이 *중복 불가*를 *타입에 새겨줌*.
|
|
136
|
+
3. **객체명 = *객체 자체의 역할*** — *사용자 관점 명사*는 도메인 어휘 부재 신호.
|
|
137
|
+
4. **`Purchase`가 String을 받으면 도메인이 *입력 형식*을 알게 됨** — 변환은 어딘가 *외부*.
|
|
138
|
+
5. **랜덤 의존은 *함수형 인터페이스*로 상위 주입** — TDD 정합성.
|
|
139
|
+
6. **`runCatching`은 *재시도+null* 양쪽 가능** — 두 함수 반복 시 *유틸리티*로 추상.
|
|
140
|
+
7. **null의 의미** — "이 값을 만들 수 없다." 호출자가 *다음 행동*을 결정.
|
|
141
|
+
8. **리뷰 반영 = 명시적 확인** — push 누락도 흔한 실수.
|
|
142
|
+
9. **Tell, Don't Ask** — `WinningLotto(inputView.inputWinningNumber())` 한 줄로.
|
|
143
|
+
10. **2인승 자동차 비유** — *자료구조 선택*이 *제약을 외부에 친절하게 알림*.
|
|
144
|
+
|
|
145
|
+
- **대표 인용:**
|
|
146
|
+
> 2인승 자동차에 인원을 태우면서 더 이상 타지 못함을 알리는 것보다 2인승 자동차라는 것을 알려준다면 보다 효과적으로 외부에도 알리기도 쉽고 추가 검증도 필요 없겠죠?
|
|
147
|
+
> — @KwonDae on `src/main/kotlin/lotto/domain/Lotto.kt`
|
|
148
|
+
|
|
149
|
+
> 현재 `generateLottoNumbers()` 함수는 shuffled() 함수에 의존적이어서, 우리는 결과를 예측할 수 없습니다.
|
|
150
|
+
> — @KwonDae on `src/main/kotlin/lotto/domain/LottoFactory.kt`
|
|
151
|
+
|
|
152
|
+
> `도메인에 맞는 타입`이라는 건 누구의 책임인지 생각해 볼 만한데요 1. 객체는 입력값에 대해 스스로 검증하여 정상값을 반환하거나 그렇지 않으면 null을 반환한다 2. 구입금액, 로또번호를 어디서 판단해야 할까? InputView는 split된 문자열만 파싱하여 도메인으로 던져도 되지 않을까?
|
|
153
|
+
> — @KwonDae on `src/main/kotlin/lotto/controller/LottoController.kt`
|
|
154
|
+
|
|
155
|
+
> inputView로부터 넘어오는 타입이 `List<Int>`로 받으면 어떨까요? WinningLotto 스스로 Int 리스트를 받아 당첨번호를 만들 수 있지 않을까요?
|
|
156
|
+
> — @KwonDae on `src/main/kotlin/lotto/controller/LottoController.kt`
|
|
157
|
+
|
|
158
|
+
## 얻은 교훈
|
|
159
|
+
|
|
160
|
+
- **`LottoNumber` 분리**는 *범위*를 타입에 위임 — Lotto는 *크기* 검증만, *각 번호의 유효성*은 LottoNumber.
|
|
161
|
+
- **자료구조 선택 = invariant의 *외부 알림*** — `Set<LottoNumber>`이면 호출자가 *중복 불가*를 즉시 인지.
|
|
162
|
+
- **객체명은 *역할*** — `Purchase`(행위)보다 `PurchaseAmount`(값) — *명사형*이 객체의 정체성에 더 가까움.
|
|
163
|
+
- **도메인이 *입력 형식*을 알면 결합** — `Purchase(amount: String)`이면 *입력 형식 변경*이 도메인 변경.
|
|
164
|
+
- **`runCatching`의 *두 표정*** — `.getOrNull()`로 null화, `.getOrElse { retry() }`로 재시도. *어느 표정*이 적절한지 호출 위치에서 결정.
|
|
165
|
+
- **null의 의미는 *값 부재*** — *재시도/기본값/실패*는 호출자가 결정.
|
|
166
|
+
- **`fun interface`** = SAM 람다 주입의 표준 — *생성 알고리즘*을 외부에서.
|
|
167
|
+
- **2인승 자동차 비유** — *제약을 사후 검증*이 아니라 *타입으로 외부에 친절히* 알림.
|
|
168
|
+
- **`data class` = `equals`/`hashCode`/`toString` 자동** — *참조 비교*가 아니라 *값 비교* 의도면 data class. 단, *Flyweight*와 결합하면 정합성 점검 필요.
|
|
169
|
+
- **TDD 정합성 = 핵심 로직이 테스트 가능** — 랜덤이 *핵심*인 객체가 *테스트 불가*면 그건 TDD가 *아닌* 것.
|
|
170
|
+
- **리뷰 반영 = *push 후 재확인*** — 커밋만 하고 push 안 하면 리뷰어가 *반영되지 않음*으로 봄.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# PR #148 — [타마] 2단계 로또
|
|
2
|
+
|
|
3
|
+
- **Reviewee:** [@etama123](https://github.com/etama123)
|
|
4
|
+
- **PR:** https://github.com/woowacourse/kotlin-lotto/pull/148
|
|
5
|
+
- **단계:** 2단계 (수동 로또)
|
|
6
|
+
- **리뷰어:** @KwonDae (제이든)
|
|
7
|
+
- **라인 코멘트:** 17
|
|
8
|
+
|
|
9
|
+
## 잘한 점 (설계 관점)
|
|
10
|
+
|
|
11
|
+
- **`LottoCalculator` 별도 객체** — *구매 금액 ↔ 로또 수 계산* 책임 분리.
|
|
12
|
+
- **`LottoNumberGenerator` 인터페이스 + `AutoLottoNumberGenerator`/`ManualLottoNumberGenerator`** — Strategy. 리뷰어 칭찬: "👍".
|
|
13
|
+
- **후속 라운드 적극 반영** — *sealed class*로 Profit 메시지 분리, *거스름돈 반환* 기능 추가, *논리 오류 vs null* 정리.
|
|
14
|
+
- **PR 본문에 *변경 커밋 링크* 첨부** — 리뷰어가 *후속 변경*을 즉시 확인 가능.
|
|
15
|
+
|
|
16
|
+
## 못한 점 (설계 관점)
|
|
17
|
+
|
|
18
|
+
### 1. *1000원 단위 미만* — *오류* vs *거스름돈* 선택
|
|
19
|
+
|
|
20
|
+
```kotlin
|
|
21
|
+
class LottoCalculator(
|
|
22
|
+
val amountOfPurchase: Int,
|
|
23
|
+
private val lottoPrice: Int = LOTTO_PRICE,
|
|
24
|
+
) {
|
|
25
|
+
private fun validatePurchaseAmount() {
|
|
26
|
+
require(amountOfPurchase % lottoPrice == 0) { "로또는 ${lottoPrice}원 단위로 구입해 주세요" }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
리뷰어: "*1000원 단위가 아니라면 오류를 발생시키는 건 미션의 요구사항 이었을까요*? 혹은 단위로 입력하지 않고 *나머지가 발생했을 때 오류 대신 거스름을 반환하거나 추가 금액을 요구*할 수 있지 않을까요? *실제 현실세계에 대입*해 보면 쉽게 답이 나올 거에요." 학생: "*Price 도메인 내부에서 금액 입력 시에, 거스름돈을 반환한다는 메시지를 추가*했습니다! 나중에 *추가 금액을 요구하는 기능*도 추가할 수 있을 것 같습니다." — *현실 메타포*로 도메인 행동 재해석.
|
|
32
|
+
|
|
33
|
+
### 2. *Domain에 View 로직* — `println("거스름돈")`
|
|
34
|
+
|
|
35
|
+
```kotlin
|
|
36
|
+
class Price(val price: Int) {
|
|
37
|
+
init {
|
|
38
|
+
validatePurchaseAmount()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private fun validatePurchaseAmount() {
|
|
42
|
+
if (price % LOTTO_PRICE != 0) {
|
|
43
|
+
val change = price % LOTTO_PRICE
|
|
44
|
+
println("구입 금액이 1000원 단위가 아닙니다. 거스름돈 ${change}원을 반환합니다.") // ⚠ View
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
리뷰어: "도메인에 *view 로직이 들어가* 있네요..! 이런 부분은 *sealed class를 활용*하면 좋을까요?" — *도메인은 상태만, 표현은 View*. 학생: "도메인에 message가 있다는 것은 *view의 책임을 domain이 대신*하게 되어, 수익 계산하는 역할을 넘어 *UI의 역할까지* 하게 됩니다. 그래서 *value만 가지는 것이 아니라 메시지 속성을 별도의 데이터로 추가할 수 있는 sealed class*로 분리해 보았습니다!"
|
|
51
|
+
|
|
52
|
+
### 3. `Profit` enum이 *message* 보유
|
|
53
|
+
|
|
54
|
+
```kotlin
|
|
55
|
+
enum class Profit(val message: String) { // ⚠ View 문구
|
|
56
|
+
.
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
리뷰어: "현재 *domain에 있는 Profit enum class에 view에서 사용되는 프로퍼티*가 있습니다. *도메인 모델에 message가 존재하는 거에 대해 어떻게 생각*하시나요? 만약 *출력 메시지가 변경되어야 한다면 도메인이 변경*되어야 할 거에요." + 후속 위치: "toString 부분은 *View의 로직*이 아닐까요? 도메인에서는 *sealed class로만 상태를 정의*하고, 해당 상태에 대한 *출력 문구는 View에서 정의*해 줘도 괜찮을 것 같습니다." 학생 후속:
|
|
61
|
+
|
|
62
|
+
```kotlin
|
|
63
|
+
sealed class ProfitMessage {
|
|
64
|
+
object LossMessage : ProfitMessage() {
|
|
65
|
+
override fun toString(): String = "기준이 1이기 때문에 결과적으로 손해라는 의미임"
|
|
66
|
+
}
|
|
67
|
+
.
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
— 여전히 *도메인 폴더에 메시지*가 남음. *Profit 상태 → View 매핑*은 *View 측*에 둬야 완전 분리.
|
|
71
|
+
|
|
72
|
+
### 4. `getManualLottoNumbers(): List<List<Int>>` — *원시 컬렉션 중첩*
|
|
73
|
+
|
|
74
|
+
```kotlin
|
|
75
|
+
private fun getManualLottoNumbers(amount: Int): List<List<Int>> {
|
|
76
|
+
val manualLottoNumbers = mutableListOf<List<Int>>()
|
|
77
|
+
outputView.printManualLottoMessage()
|
|
78
|
+
|
|
79
|
+
repeat(amount) {
|
|
80
|
+
val numbers = inputView.inputManualLottoNumber()
|
|
81
|
+
manualLottoNumbers.add(numbers)
|
|
82
|
+
}
|
|
83
|
+
return manualLottoNumbers
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
리뷰어: "*List<List<Int>> 조금 어색해 보이지 않나요*? 함수 이름이 ManualLottoNumber라면 *반환값도 List<Lotto>이어야 자연스러울 것* 같아요." 학생: "Controller 내부에서 *List<Int>를 받을 때마다, 수동 로또를 발행*하는 방식으로 변경해 보았습니다. 이렇게 변경함에 따라 *입력한 수동 로또 번호 하나마다, 유효성을 검증*할 수 있게 되었습니다." — *지연된 도메인 변환*은 검증 시점도 지연.
|
|
88
|
+
|
|
89
|
+
### 5. `LottoSeller`가 `LottoFactory` 의존 — *검증 미완 입력*
|
|
90
|
+
|
|
91
|
+
```kotlin
|
|
92
|
+
class LottoSeller(
|
|
93
|
+
val price: Int,
|
|
94
|
+
val manualLottoAmount: Int,
|
|
95
|
+
val manualLottos: List<List<Int>>, // ⚠ 검증 미완
|
|
96
|
+
) {. }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
리뷰어: "*들어오는 값이 검증이 되어 있지 않으니 LottoSeller가 LottoFactory까지 의존*하게 되고 있네요. *프로퍼티 자체를 List<Lotto>로 받아* 보는 건 어떨까요?" — *불변/검증된 도메인 타입*을 인자로 받아야 *재검증/팩토리 호출*이 불필요.
|
|
100
|
+
|
|
101
|
+
### 6. *논리 오류 vs null* — `toInt` vs `toIntOrNull`
|
|
102
|
+
|
|
103
|
+
```kotlin
|
|
104
|
+
fun inputPurchasePrice(): Int {
|
|
105
|
+
println("구입금액을 입력해 주세요.")
|
|
106
|
+
val input = readln().also { InputValidator.ValidateNumber(it) } // 검증 후 toInt
|
|
107
|
+
return input.toInt()
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
리뷰어: "> *논리적인 오류가 아니면 예외를 던지지 말고 null을 반환*한다. *InputValidator에서 검증하는 항목은 논리 오류에 해당*할까요? 기존 kotlin에서 *toInt와 toIntOrNull 둘 다 개발한 건 어떤 의도*였을까요?" 학생 정리:
|
|
112
|
+
```
|
|
113
|
+
**구입 금액에 문자가 입력된 경우**
|
|
114
|
+
1. InputValidator로 숫자인지 확인
|
|
115
|
+
2. 문자로 오입력 확인 -> null을 반환
|
|
116
|
+
3. 구입 금액 도메인에서 논리 오류 검사 (require() 사용)
|
|
117
|
+
4. 사용자에게 재 입력 요구
|
|
118
|
+
```
|
|
119
|
+
"이렇게 로직을 짜면, *View에서 입출력 오류를 막을 수 있을 뿐만 아니라, 논리 오류에도 대처가 가능*하다고 생각합니다!" + 후속: "1. 입력 오류인 경우, *InputValidator에서 null을 반환해 재입력*. 2. 논리 오류인 경우, *controller에서 runCatching을 활용*해, *domain에서 던져진 예외*가 있는 경우 *재입력*."
|
|
120
|
+
|
|
121
|
+
### 7. `runCatching` 권장
|
|
122
|
+
|
|
123
|
+
리뷰어 (calculator 위에): "아래 코멘트와 마찬가지입니다. 코틀린에는 *runCatching*이라는 게 존재하는데요 참고해 보셔도 좋습니다."
|
|
124
|
+
|
|
125
|
+
### 8. InputView가 *Lotto 객체*까지 만들 수도
|
|
126
|
+
|
|
127
|
+
```kotlin
|
|
128
|
+
private fun getWinningNumbers(): Lotto {
|
|
129
|
+
while (true) {
|
|
130
|
+
val input = inputView.inputWinningNumber()
|
|
131
|
+
runCatching {
|
|
132
|
+
return Lotto(input.map { number -> LottoNumber(number) })
|
|
133
|
+
}.onFailure { e ->
|
|
134
|
+
outputView.printErrorMessage(e.message)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
리뷰어: "**_Lotto 객체 생성 시 스스로 검증에 대한 로직이 잘 정리되어 있다면_** *InputView로부터 정상 입력 시 Lotto 객체를 전달받는다면* 어떨까요? 현재 *OutputView에서는 Model을 빌려 출력*을 하는데, *InputView에서도 입력받은 값을 통해 객체를 만들어 컨트롤러로 던져* 줄 수 있을 거에요."
|
|
141
|
+
```kotlin
|
|
142
|
+
//inputView
|
|
143
|
+
fun inputWinningNumber(): Lotto {
|
|
144
|
+
val winningLotto = readln(). ?: inputWinningNumber()
|
|
145
|
+
return winningLotto
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
— *대칭 원칙*: View는 *입/출력 모두 도메인 객체와의 변환*.
|
|
149
|
+
|
|
150
|
+
## 리뷰어 의견 요지
|
|
151
|
+
|
|
152
|
+
- **요구사항 외 *현실 메타포*** — 1000원 미만은 *오류*가 아닌 *거스름돈*도 가능.
|
|
153
|
+
- **도메인 ≠ View** — `println`, `message`, `toString` 모두 View로.
|
|
154
|
+
- **`sealed class`로 *상태와 메시지 분리***.
|
|
155
|
+
- **원시 컬렉션 중첩 (`List<List<Int>>`) → 도메인 타입**.
|
|
156
|
+
- **검증 미완 인자 = *팩토리 의존* 신호**.
|
|
157
|
+
- **`InputValidator` = *입력 형식*, *도메인 = 논리 오류***.
|
|
158
|
+
- **`InputView`도 *도메인 객체* 반환 가능** — 대칭.
|
|
159
|
+
|
|
160
|
+
- **대표 인용:**
|
|
161
|
+
> 1000원 단위가 아니라면 오류를 발생시키는 건 미션의 요구사항 이었을까요? *실제 현실세계에 대입*해 보면 쉽게 답이 나올 거에요.
|
|
162
|
+
> — @KwonDae on `src/main/kotlin/lotto/domain/LottoCalculator.kt`
|
|
163
|
+
|
|
164
|
+
> 만약 *출력 메시지가 변경되어야 한다면 도메인이 변경*되어야 할 거에요.
|
|
165
|
+
> — @KwonDae on `src/main/kotlin/lotto/controller/LottoController.kt`
|
|
166
|
+
|
|
167
|
+
> List<List<Int>> 조금 어색해 보이지 않나요? *반환값도 List<Lotto>이어야 자연스러울 것* 같아요.
|
|
168
|
+
> — @KwonDae on `src/main/kotlin/lotto/controller/LottoController.kt`
|
|
169
|
+
|
|
170
|
+
> 들어오는 값이 검증이 되어 있지 않으니 *LottoSeller가 LottoFactory까지 의존*하게 되고 있네요. *프로퍼티 자체를 List<Lotto>로 받아* 보는 건 어떨까요?
|
|
171
|
+
> — @KwonDae on `src/main/kotlin/lotto/domain/LottoSeller.kt`
|
|
172
|
+
|
|
173
|
+
## 얻은 교훈
|
|
174
|
+
|
|
175
|
+
- **요구사항 외 *현실 메타포*** — *1000원 미만*은 *오류*가 아닌 *거스름돈*. *물리적 키오스크*가 가르치는 도메인 행동.
|
|
176
|
+
- **도메인 ≠ View** — `println`, `message: String`, `toString`는 모두 *View 책임*. 도메인 변경 = View 변경이 되어선 안 됨.
|
|
177
|
+
- **`sealed class`로 *상태와 메시지 분리*** — 도메인은 *상태 ID*만, *문구*는 View에서 매핑.
|
|
178
|
+
- **원시 컬렉션 중첩은 *지연된 도메인***. `List<List<Int>>` → `List<Lotto>`로 *생성 시점*을 앞당기면 *검증도 앞당김*.
|
|
179
|
+
- **검증 미완 인자 = *팩토리 의존* 신호** — `LottoSeller(manualLottos: List<List<Int>>)`처럼 *원시값 입력*은 *내부에서 도메인 변환*을 강요.
|
|
180
|
+
- **입력 형식 오류 = null + 재입력, 논리 오류 = require + runCatching 재입력** — *두 경로*를 명시적으로 분리한 학생의 4단계 정리는 *경계 패턴*의 모범.
|
|
181
|
+
- **`runCatching`은 입력 경계의 일관 패턴** — `try-catch`보다 *Result*가 *재시도 위치*를 명확.
|
|
182
|
+
- **`InputView`도 *도메인 객체 반환* 가능** — *OutputView가 도메인 사용*과 *대칭*. `fun inputWinningNumber(): Lotto`처럼.
|
|
183
|
+
- **변경 커밋 링크를 PR 댓글에 첨부** — 후속 라운드 응답을 *URL로*. 리뷰 효율 + 변경 추적.
|