Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
NestJSのDIコンテナで作るクリーンなレイヤー境界
Search
kimutyam
May 20, 2022
Technology
2
1.3k
NestJSのDIコンテナで作るクリーンなレイヤー境界
NestJS Meetup Online #2 にて発表した内容です。
https://nest-jp.connpass.com/event/244015/
kimutyam
May 20, 2022
Tweet
Share
More Decks by kimutyam
See All by kimutyam
Embulk / Presto / Sparkを用いたETL事情
kimutyam
4
1.9k
セプテーニで分析基盤(Treasure Data)を導入した話
kimutyam
0
1.3k
Reactive Messaging Patternsを使った境界づけられたコンテキストの統合
kimutyam
3
1.1k
アジャイルでのドメイン・ユースケースモデリング
kimutyam
5
2k
Introduction of ScalaTest
kimutyam
3
2k
Scalaで学ぶヘキサゴナルアーキテクチャ実践入門
kimutyam
15
6.3k
Other Decks in Technology
See All in Technology
障害対応をちょっとずつよくしていくための 演習の作りかた
heleeen
0
220
Delivering Millions of Messages within seconds @ Duolingo
pelelgrino
0
350
web-application-security
matsuihidetoshi
0
170
require(ESM)とECMAScript仕様
uhyo
3
720
ChatGPT for IT Service Management (IT Pro)
dahatake
7
1.6k
ChatworkのSRE部って実は 半分くらいPlatform Engineering部かもしれない
saramune
0
160
ServiceNow Knowledge Learning Rise up
manarobot
0
210
リテール金融(キャッシュレス・ネット銀行・ネット証券)の競争環境と経済圏
8maki
0
1.2k
Janus
bkuhlmann
1
490
GraphQL 成熟度モデルの紹介と、プロダクトに当てはめた事例 / GraphQL maturity model
mh4gf
7
1.3k
一生覚えておきたい「システム開発=コミュニケーション」〜初めての実務案件振り返りLT〜
maimyyym
0
140
サーバー間 GraphQL と webmock-graphql の話 / server-to-server graphql and webmock-graphql
qsona
2
190
Featured
See All Featured
5 minutes of I Can Smell Your CMS
philhawksworth
199
19k
Gamification - CAS2011
davidbonilla
76
4.6k
How GitHub (no longer) Works
holman
304
140k
10 Git Anti Patterns You Should be Aware of
lemiorhan
648
58k
Designing for Performance
lara
601
67k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
125
32k
Producing Creativity
orderedlist
PRO
337
39k
Documentation Writing (for coders)
carmenintech
60
3.9k
Code Review Best Practice
trishagee
55
15k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
244
20k
GraphQLの誤解/rethinking-graphql
sonatard
50
9.2k
Code Reviewing Like a Champion
maltzj
514
39k
Transcript
גࣜձࣾΧέϋγɹଜজ /FTU+4ͷ%*ίϯςφͰ࡞ΔΫϦʔϯͳϨ ΠϠʔڥք /FTU+4NFFUVQ0OMJOFɹ
ࢹௌऀɾಡऀͷఆ w ҎԼͷ͍ͣΕ͔ʹ֘ w /FTU+4Ͱ8FCΞϓϦέʔγϣϯΛΜͩ͜ͱ͕͋Δ w /FTU+4ͷ1SPWJEFS.PEVMFΛ৮ͬͨ͜ͱ͕͋Δ w ΦϒδΣΫτࢦͰͷઃܭɾ։ൃܦݧ͕͋Δ
ࣗݾհ w גࣜձࣾΧέϋγ d w ҩྍൃɾཧ࠷దԽྖҬͷ৽نࣄۀͷ্ཱͪ͛Λ୲த w ιϑτΣΞΤϯδχΞσʔλΤϯδχΞΞʔΩςΫτ w
Χέϋγͱ5ZQF4DSJQU w גࣜձࣾΧέϋγY5ZQF4DSJQU"EWFOU$BMFOEBS w ͳͥόοΫΤϯυ5ZQF4DSJQU͔ʁٕज़બఆഎܠͱ࣮ફྫΛհ͠·͢ ଜজ ͖ΉΒ͖͋ͻΖ ˞ܦྺͷৄࡉ/FTU+4.FFUVQ0OMJOFͷΠϕϯτϖʔδʹهࡌ͍ͯ͠·͢
5XJUUFSIUUQTUXJUUFSDPNLJNVUZBN .FEJVNIUUQTLJNVUZBNNFEJVNDPN 4QFBLFS%FDLIUUQTTQFBLFSEFDLDPNLJNVUZBN (JUIVCIUUQTHJUIVCDPNLJNVUZBN 'PMMPX.F
ΞδΣϯμ ΫϦʔϯͳϨΠϠʔڥքΛΉͨΊͷߟ͑ํ /FTU+4ͷ%*ίϯςφͷΈ ϨΠϠʔڥքΛߏ͢Δ औΓѻΘͳ͍͜ͱ w ΫϦʔϯΞʔΩςΫνϟͷਂ͍આ໌
w υϝΠϯϞσϦϯάͷํ ηΫγϣϯ
ΫϦʔϯͳϨΠϠʔڥքΛΉͨΊͷߟ ͑ํ ηΫγϣϯ
w ΫϦʔϯΞʔΩςΫνϟͰ༗໊ͳਤͰ͢ɻ w ͜ΕΒͷಉ৺ԁͷଆʹ͍͘΄ͲιϑτΣΞͱ ্ͯ͠ҐϞδϡʔϧʹͳΔ͜ͱ͕ࣔ͞Ε͍ͯ·͢ɻ w υϝΠϯۦಈઃܭͷจ຺ͩͱɺ&OUJUJFTศ্ٓυ ϝΠϯϞσϧͱදݱ͢Δ߹͕͋Γ·͢ɻ w ಉ৺ԁͷΛϨΠϠʔڥքͱݺͼ·͢ɻ
w ಉ৺ԁͷΛԣஅ͢ΔʮˠʯґଘؔͰ͢ɻ ΫϦʔϯͳϨΠϠʔڥքΛΉͨΊͷߟ͑ํ ΫϦʔϯΞʔΩςΫνϟ IUUQTCMPHDMFBODPEFSDPNVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM
ΫϦʔϯͳϨΠϠʔڥքΛΉͨΊͷߟ͑ํ ґଘؔٯసͷݪଇ %FQFOEFODZ*OWFSTJPO1SJODJQMF l্ҐϞδϡʔϧ͍͔ͳΔͷԼҐϞδϡʔϧ ͔Β࣋ͪࠐΜͰͳΒͳ͍ɻํͱநʢྫͱ͠ ͯΠϯλʔϑΣʔεʣʹґଘ͢Δ͖Ͱ͋Δɻz˞ lநৄࡉʹґଘͯ͠ͳΒͳ͍ɻৄࡉʢ۩త ͳ࣮༰ʣ͕நʹґଘ͢Δ͖Ͱ͋Δɻz˞ ґଘؔٯసύλʔϯͰɺΠϯλʔϑΣʔεΛհ ͤ͞Δ͜ͱͰ্ҐϞδϡʔϧͷґଘΛճආ͍ͯ͠·
͢ɻ ,MPESߘऀࣗʹΑΔஶ࡞ $$දࣔܧঝ IUUQTDPNNPOTXJLJNFEJBPSHXJOEFYQIQ DVSJEʹΑΔ ,MPESߘऀࣗʹΑΔஶ࡞ $$දࣔܧঝ IUUQTDPNNPOTXJLJNFEJBPSHXJOEFYQIQ DVSJEʹΑΔ ӈ্ը૾ ӈԼը૾ ˞ IUUQTKBXJLJQFEJBPSHXJLJґଘੑٯసͷݪଇ ैདྷͷϨΠϠʔύλʔϯ ґଘؔٯసύλʔϯ
w ґଘؔٯసͷݪଇʹैͬͨઃܭΛ͢Δͱɺ࠷ऴ తʹΠϯλʔϑΣʔεͷ࣮Λඥ͚Δඞཁ͕ग़ͯ ͖·͢ɻͦ͜Ͱɺґଘੑͷೖ %* Λߦ͍·͢ɻ w ্ͷίʔυɺίϯετϥΫλͰґଘੑΛೖ͢ ΔྫͰ͢ ίϯετϥΫλ%*
ɻ w %*ͱɺґଘؔΛΫϥεؔͷ֎͔Β͢͜ͱ Ͱ͢ɻ w ͳ͓ɺԼͷίʔυɺ.FDIBOJTN෦Ͱ1PMJDZͷ ۩Ϋϥε͕ґଘͯ͠ɺґଘੑͷzೖzʹͳͬͯ ͍·ͤΜɻ ΫϦʔϯͳϨΠϠʔڥքΛΉͨΊͷߟ͑ํ ґଘੑͷೖ %FQFOEFODZ*OKFDUJPO export class MechanismService { constructor(private policyService: IPolicyService) { } // ུ } export class MechanismService { private policyService: IPolicyService = new PolicyService( ) // ུ }
w %*ΛͬͨΫϥεઃܭΛਐΊΔͱɺґଘؔΛղ ܾ͢ΔӈͷΑ͏ͳίʔυ͕ੜ·Ε·͢ɻ w Ϋϥε͕গͳ͍͏ͪʹͳΓ·ͤΜ͕ɺ૿͑ ͖ͯͨ࣌ෆཁͳෳࡶੑΛੜΉ߹͕͋Γ·͢ɻ w %*ίϯςφӈͷίʔυΛॻ͔ͣʹґଘؔͷϏ δωεϩδοΫͷ֎ଆͰཧ͢Δ͜ͱ͕Ͱ͖· ͢ɻ
w ͦͯ͠ɺ%*ίϯςφ/FTU+4ͷओཁͳػೳͷͭ Ͱ͢ɻ ΫϦʔϯͳϨΠϠʔڥքΛΉͨΊͷߟ͑ํ %*ίϯςφԿΛղܾ͢Δ͔ʁ new UtilityService ( new MechanismService ( new PolicyService( ) ) )
/FTU+4ͷ%*ίϯςφͷΈ ηΫγϣϯ
w ·ͣ؆୯ͳྫ͔Βɻ w $BUT4FSWJDFͷґଘؔɺ!.PEVMFΛӈͷΑ͏ ʹఆٛ͢Ε%*Ͱ͖·͢ɻ w ͜ͷྫɺ$BUT4FSWJDFzΫϥεzΛ$BUT$POUSPMMFS ͰίϯετϥΫλ%*͍ͯ͠·͢ɻ /FTU+4ͷ%*ίϯςφͷΈ 1SPWJEFS
@Controlle r class CatsService { } @Controller("cats") class CatsController { constructor(private catsService: CatsService) { } // ུ } @Module( { controllers: [CatsController] , providers: [CatsService] , } ) export class AppModule {}
w ΠϯλʔϑΣʔεͷ߹ɺ͕ඞཁͰ͢ɻ w +BWB4DSJQUΠϯλʔϑΣʔεΛαϙʔτͯ͠ ͍ͳ͍ͨΊɺ5ZQF4DSJQUΛ+BWB4DSJQUʹίϯ ύΠϧ͢ΔͱɺΠϯλʔϑΣʔεͷఆٛফࣦ ͠·͢ɻ w τʔΫϯ ྫͰ$"54@4&37*$&@50,&/
Ͱ /FTU+4ͷ*OKFDUFSͰࣝผՄೳͳঢ়ଶʹ͢Δඞཁ͕ ͋Γ·͢ɻ /FTU+4ͷ%*ίϯςφͷΈ 1SPWJEFS interface ICatsService { } class CatsService implements ICatsService { } const CATS_SERVICE_TOKEN = "CATS_SERVICE_TOKEN" ; class CatsController { constructor ( @Inject(CATS_SERVICE_TOKEN ) private catsService: ICatsServic e ) { } // ུ } export const CatServiceProvider: Provider = { provide: CAT_SERVICE_TOKEN , useClass: CatsService , } ; @Module( { controllers: [CatsController] , providers: [CatServiceProvider] , } ) export class AppModule { }
/FTU+4ͷ%*ίϯςφͷΈ !.PEVMFͱԿ͔ʁ .PEVMF# .PEVMF" FYQPSUT JNQPSUT JNQPSUT 1SPWJEFS 1SPWJEFS $POUSPMMFS
QSPWJEFST /FTU+4ͷΠϯδΣΫλʹΑͬͯΠϯελϯ εԽ͞Εɺগͳ͘ͱϞδϡʔϧͰڞ༗ ͞ΕΔ DPOUSPMMFST .PEVMFͷΠϯελϯεԽ͢Δඞཁ͕͋Δ $POUSPMMFSҰཡ JNQPSUT ͜ͷϞδϡʔϧʹFYQPSU͢ΔผͷϞδϡʔ ϧͷҰཡ FYQPSUT ผͷϞδϡʔϧͰ͜ͷϞδϡʔϧΛJNQPSU ͢ΔͨΊͷɺެ։͢ΔϓϩόΠμٴͼτʔ ΫϯͷϦετ
ϨΠϠʔڥքΛߏ͢Δ ηΫγϣϯ
ϨΠϠʔڥքΛߏ͢Δ IUUQTCMPHDMFBODPEFSDPNVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM ߏ͢ΔΫϥεਤ
ϨΠϠʔڥքΛߏ͢Δ ߏ͢ΔΫϥεਤ w ֤ϨΠϠʔຖ EPNBJOVTF$BTF QSJNBSZ"EBQUPSTFDPOEBSZ"EBQUPS ʹͦΕͧ Ε.PEVMFΛ࡞͠·͢ w ͦΕΒΛ࠷ऴతʹ.PEVMFΛଋͶ·͢ɻ
w <ิ>QSJNBSZͱTFDPOEBSZͱʁ w ΫϦʔϯΞʔΩςΫνϟͷલਐͱͳͬͨ 1PSUT"EBQUPSTΞʔΩςΫνϟͷ༻ޠͰ ͢ɻ w QSJNBSZʮۦಈ͢ΔʯΞμϓλ w TFDPOEBSZʮۦಈ͞ΕΔʯΞμϓλ
w 3FQPTJUPSZ3FRVFTUFS*'ͷΈͰ͢ɻ w ۩ମతͳ࣮Λ͢ΔͱΞμϓλٴͼͦΕΑ Γ֎ͷԁʹґଘͯ͠͠·͏ͨΊͰ͢ɻ w Ұ൪্ҐϞδϡʔϧʹͳΔͨΊzೖz͢Δґଘ ͳ͍ͨΊɺ.PEVMF͋Γ·ͤΜɻ w ͳ͓ɺτʔΫϯϢʔεέʔεͷ࣮Ͱ͏ͨ
Ίɺ͜͜ʹஔ͍ͯ͠·͢ɻ ϨΠϠʔڥքΛߏ͢Δ υϝΠϯ EPNBJO export type Cat = Readonly< { id: CatId ; name: CatName ; age: CatAge ; }> ; export const CAT_REPOSITORY_TOKEN = 'CAT_REPOSITORY_TOKEN' ; export interface ICatRepository { store(cat: Cat): Promise<void> ; } export const CAT_REQUESTER_TOKEN = 'CAT_REQUESTER_TOKEN' ; export interface ICatRequester { get(catId: CatId): Promise<Cat | undefined> ; }
w ʮೣΛड͚औΔʯͱ͍͏ϢʔεέʔεΛఆͨ͠ ࣮Ͱ͢ɻ w 6TF$BTF*'Ͱɺͦͷ࣮Ϋϥε *OUFSBDUPS Ͱ͢ɻ w *OUFSBDUPSͰυϝΠϯͰఆٛͨ͠*'Λίϯε τϥΫλͷҾʹఆ͍ٛͯ͠·͢ɻ
ϨΠϠʔڥքΛߏ͢Δ Ϣʔεέʔε 6TF$BTF export interface UseCase<In, Out> { run(input: In): Promise<Out> ; } export type Input = Readonly< { catId: CatId ; }> ; export type Output = Omit<Cat, 'id'> ; export type ReceiveCatUseCase = UseCase<Input, Output> ; export const RECEIVE_CAT_USECASE_TOKEN = 'RECEIVE_CAT_USECASE_TOKEN' ; @Injectable( ) export class ReceiveCatInteractor implements ReceiveCatUseCase { constructor ( @Inject(CAT_REPOSITORY_TOKEN ) private catRepository: ICatRepository , @Inject(CAT_REQUESTER_TOKEN ) private catRequester: ICatRequester , ) { } async run({ catId }: Input): Promise<Output> { const cat = await this.catRequester.get(catId); // ུ } }
ϨΠϠʔڥքΛߏ͢Δ Ϣʔεέʔε 6TF$BTF w ηΧϯμϦʔΞμϓλͷΫϥε Λ࣮ͱͯ͠ ࠾༻͢Δ͜ͱʹ͢Δ߹ɺηΧϯμϦΞμϓλ ͷ.PEVMFΛJNQPSU͠·͢ɻ w
͜ΕΛ౿·͑ͯ࣍ͷϖʔδͰ.PEVMFΛ࡞͠ ·͢ɻ
w ηΧϯμϦʔΞμϓλͷ.PEVMF ޙड़ ΛJNQPSU ͍ͯ͠·͢ w ϢʔεέʔεΛଞͷ.PEVMFͰར༻Ͱ͖ΔΑ͏ʹ ϓϩόΠμΛFYQPSU͍ͯ͠·͢ w ϓϥΠϚϦΞμϓλͷ.PEVMF
ޙड़ ͰJNQPSU ͠·͢ɻ w <ิ>6TF$BTF.PEVMFΠϯλʔϑΣʔεΞμ ϓλʹґଘ͍ͯ͠·͕͢ɺϩδοΫ͕ґଘ͍ͯ͠ ΔΘ͚Ͱ͋Γ·ͤΜɻ ϨΠϠʔڥքΛߏ͢Δ Ϣʔεέʔε 6TF$BTF export const ReceiveCatUseCaseProvider: Provider = { provide: RECEIVE_CAT_USECASE_TOKEN , useClass: ReceiveCatInteractor , } ; @Module( { imports: [AdaptorMemoryStoreModule, AdaptorPetShopApiModule] , providers: [ReceiveCatUseCaseProvider] , exports: [ReceiveCatUseCaseProvider] , } ) export class UseCaseModule { }
w υϝΠϯͷϦϙδτϦͷ*'Λ࣮͍ͯ͠·͢ɻ w ϦϙδτϦͷϓϩόΠμΛଞͷ.PEVMFͰ׆༻Ͱ ͖ΔΑ͏ʹFYQPSU͍ͯ͠·͢ɻ w Ϣʔεέʔεͷ.PEVMFͰJNQPSU͠·͢ɻ ϨΠϠʔڥքΛߏ͢Δ ΠϯλʔϑΣʔεΞμϓλ 4FDPOEBSZ"EBQUPS
@Injectable( ) export class CatRepository implements ICatRepository { store(cat: Cat): Promise<void> { // ུ } } export const CatRepositoryProvider: Provider = { provide: CAT_REPOSITORY_TOKEN , useClass: CatRepository , } ; @Module( { providers: [CatRepositoryProvider] , exports: [CatRepositoryProvider] , } ) export class AdaptorMemoryStoreModule { }
w $BU$POUSPMMFSͷ࣮Ͱ͢ɻ w 6TF$BTF*'ͱ1SFTFOUFSͷΫϥεΛίϯετ ϥΫλͰఆ͍ٛͯ͠·͢ɻ ϨΠϠʔڥքΛߏ͢Δ ΠϯλʔϑΣʔεΞμϓλ 1SJNBSZ"EBQUPS @Injector( )
export class CatPresenter { } @Controller('/cat' ) export class CatController { constructor ( @Inject(RECEIVE_CAT_USECASE_TOKEN ) private receiveCatUseCase: ReceiveCatUseCase , private catPresenter: CatPresenter , ) { } @Put(':id' ) async receive(@Param('id') id: string): Promise<CatViewModel> { // ུ } }
ϨΠϠʔڥքΛߏ͢Δ ΠϯλʔϑΣʔεΞμϓλ 1SJNBSZ"EBQUPS w $POUSPMMFS ੨ ͷґଘؔΛղܾ͢ΔͨΊʹɺ *OUFSBDUPSͷϓϩόΠμʔΛؚΉϢʔεέʔεͷ .PEVMF͕ඞཁʹͳΓ·͢ɻ w
·ͨɺ͜ͷྫͰ1SFTFOUFSͷґଘؔղܾ͢ Δඞཁ͕͋Γ·͢ɻ w ͜ΕΛ౿·͑ͯ࣍ͷϖʔδͰ.PEVMFΛ࡞͠ ·͢ɻ
w Ϣʔεέʔεͷ.PEVMFΛJNQPSUͯ͠ɺ $POUSPMMFSͷίϯετϥΫλ%*͍ͯ͠·͢ɻ w DPOUSPMMFSTʹίϯτϩʔϥʔͷࢦఆΛ͍ͯ͠· ͢ɻ w 1SFTFOUFSΫϥεΛQSPWJEFSTʹࢦఆ͍͠·͢ɻ w <ิ>͜͜ͰϢʔεέʔεͷ*'ʹґଘ͍ͯ͠·
͕͢ɺϢʔεέʔεͷ*'ΛΒͣʹ۩Ϋϥεͷ ࢦఆͰ͋Γ·ͤΜɻ w *'ʹґଘ͍ͤͯ͞Δͷ$POUSPMMFSͷςελ ϏϦςΟΛ্ͤ͞ΔͨΊͰ͢ɻ ϨΠϠʔڥքΛߏ͢Δ ΠϯλʔϑΣʔεΞμϓλ 1SJNBSZ"EBQUPS @Module( { imports: [UseCaseModule] , controllers: [CatController] , providers: [CatPresenter] , } ) export class AdaptorWebApiModule { }
w ࠷ऴతʹ"QQ.PEVMFͰࠓ·Ͱ࡞ͨ͠.PEVMF ΛJNQPSU͠·͢ w ηΧϯμϦʔΞμϓλ6TF$BTF.PEVMFͰ JNQPSU͞Ε͍ͯΔͨΊ͜͜ͰෆཁͰ͢ɻ w "QQ.PEVMFΛ/FTU'BDUPSZDSFBUFʹͯ͠ Ͱ͢ɻ ϨΠϠʔڥքΛߏ͢Δ
"QQ.PEVMF @Module( { imports: [UseCaseModule, AdaptorWebApiModule] , } ) export class AppModule { } export async function main() { const app = await NestFactory.create(AppModule) ; await app.listen(80, '0.0.0.0') ; }
࠷ޙʹ
࠷ޙʹ w ΫϦʔϯͳϨΠϠʔڥքΛ࡞ΔͨΊʹɺґଘؔٯసͷݪଇΛ׆༻͠ґଘؔΛೖ͠·͢ɻ w %*ίϯςφΛ͏ͱɺ࣮ͱઃఆΛ͚ͯґଘؔͷઢ͕Ͱ͖·͢ɻ w /FTU+4ͷ.PEVMFTΛ͑ґଘؔͷೖͷׂ౷࣏͕ՄೳͰ͢ɻ w ࠓճϨΠϠʔผʹઢͷׂ౷࣏Λ͠·ͨ͠ɻ w
αϯϓϧίʔυҎԼ w IUUQTHJUIVCDPNLJNVUZBNOFTUKTDMFBOBSDIJUFDUVSFUSFF ·ͱΊ
࠷ޙʹ w Έํͷߟ͑ํಉ͡Ͱ͕͢ɺ͜͜Ͱઆ໌ͨ͠ͷύλʔϯͰ͢ɻ w ϓϩδΣΫτͷঢ়گϓϩμΫτͷಛʹΑͬͯͲ͏͍͏ߏʹ͢Δ͔ݕ౼͠·͠ΐ͏ɻ w ͨͩ͠ɺߏΛΉ্Ͱͷߟ͑ํಉ͡Ͱ͢ɻ w lΞʔΩςΫνϟͷϧʔϧͲΕಉ͡Ͱ͋Δʂz˞ ҙॻ͖
˞3PCFSU$.BSUJO ʰ$MFBO"SDIJUFDUVSFୡਓʹֶͿιϑτΣΞͷߏͱઃܭʱʮংจʯΑΓ
࠷ޙʹ 8FBSF)JSJOH w ΤϯδχΞʹؔͯ͠ଟ͘ͷٻਓ͕͍͟͝·͢ɻ˞ w όοΫΤϯυʹؔͯ͠5ZQF4DSJQUΛओཁݴޠͱ ͨ͠ืू͋Γ·͢ɻ˞ w 8FCͷϑϩϯτΤϯυ5ZQF4DSJQUΛར༻͍ͯ͠ ·͢ɻ˞
IUUQTIFSQDBSFFSTWLBLFIBTIJ ˞ొஃ࣌ͰͷٻਓใͰ͢ɻ