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
Introduction of Jooby. DevIO2016
Search
komuro-sapporo
October 08, 2016
Technology
0
1.3k
Introduction of Jooby. DevIO2016
Developers IO 2016 in Fukuoka
komuro-sapporo
October 08, 2016
Tweet
Share
More Decks by komuro-sapporo
See All by komuro-sapporo
How To 脆弱性対応
komurosappro
0
730
内容は話せないけどGamedayのススメ
komurosappro
0
540
ゲンバのサービス運用
komurosappro
2
1.4k
大阪勉強会_第5回.pdf
komurosappro
0
4
運用事件簿
komurosappro
0
2.6k
Other Decks in Technology
See All in Technology
What's new in OpenShift 4.19
redhatlivestreaming
1
260
生成AIをテストプロセスに活用し"よう"としている話 #jasstnano
makky_tyuyan
0
170
DenoとJSRで実現する最速MCPサーバー開発記 / Building MCP Servers at Lightning Speed with Deno and JSR
yamanoku
1
100
Rubyで作る論理回路シミュレータの設計の話 - Kashiwa.rb #12
kozy4324
1
310
2025/6/21 日本学術会議公開シンポジウム発表資料
keisuke198619
2
370
Whats_new_in_Podman_and_CRI-O_2025-06
orimanabu
3
180
(非公式) AWS Summit Japan と 海浜幕張 の歩き方 2025年版
coosuke
PRO
1
270
New Cache Hierarchy for Container Images and OCI Artifacts in Kubernetes Clusters using Containerd / KubeCon + CloudNativeCon Japan
pfn
PRO
0
160
從四件事帶你見識見識 事件驅動架構設計 (EDA)
line_developers_tw
PRO
0
110
「伝える」を加速させるCursor術
naomix
0
640
Create a Rails8 responsive app with Gemini and RubyLLM
palladius
0
120
CIでのgolangci-lintの実行を約90%削減した話
kazukihayase
0
290
Featured
See All Featured
Being A Developer After 40
akosma
90
590k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
357
30k
Bash Introduction
62gerente
614
210k
Building an army of robots
kneath
306
45k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Intergalactic Javascript Robots from Outer Space
tanoku
271
27k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
1.3M
Automating Front-end Workflow
addyosmani
1370
200k
Large-scale JavaScript Application Architecture
addyosmani
512
110k
Raft: Consensus for Rubyists
vanstee
140
7k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
228
22k
Transcript
%FWFMPQFST*0 " খࣨܒ ϞόΠϧΞϓϦαʔϏε෦ DMBTTNFUIPEJOD ⡥$MBTTNFUIPE *OD ݄ /FX/FYU8FC"QQMJDBUJPO 'SBNFXPSL
ʮ+PPCZʯ
⡥$MBTTNFUIPE *OD ࣗݾհ w খࣨܒ w ϞόΠϧΞϓϦαʔϏε෦όοΫΤϯυάϧʔϓ w ։ൃऀ 1-
ࡳຈΦϑΟεϚωʔδϟ w ࡛ۄݝग़࡛ۄҭͪ Կނ͔ࠓւಓࡳຈࢢࡏॅ w ϞόΠϧΞϓϦ(ͷํΦϑΟε্ཱͪ͛෦ୂʢࣗশ w 5XJUUFS!DPNED
+BWB 1ZUIPO "OESPJE "84 6OSFBM &OHJOF 4QSJOH ,PUMJO
+BWB 4QSJOH#PPU ࢭΉʹࢭ·Εͣ࣌ʑ1MBZGPS4DBMB
ւಓࡳຈࢢ͔Βདྷ·ͨ͠ɻ
None
ւಓͱ͍͑ɾɾɾ
None
None
ઇ
None
Նྋͯ͘͠ͱͯշదͰ ͢ɻ ౙനͯ͘ ۃΊͯ פ͍Ͱ͢ ʢͨͩ͠෦ͷதॵ͍ʣ
,"/8",:6%"*
⡥$MBTTNFUIPE *OD N @@ N w ͜ͷࢿྉɺ+PPCZ$3Λରʹॻ͍ͯ͋Γ ·͢ w +PPCZͷϦϦʔε͕ૣ͍ͨΊɺॻ͍ͯΔ్தͰόʔ
δϣϯ্͕͕Γ·ͨ͠ w ݱࡏ$3͕ϦϦʔεࡁΈ
+PPCZͲ͏Ͱ͠ΐ͏
⡥$MBTTNFUIPE *OD ͡Ίʹ w 8FCΞϓϦέʔγϣϯ։ൃͷϑϨʔϜϫʔΫʹඞཁ ͳཁૉͱͲΜͳͷͰ͠ΐ͏͔ʁ %* 8FC4FSWFS 4DBMBCJMJUZ
.PEVMF "TTFUT ๛ͳ֎෦ϥΠϒϥϦ
⡥$MBTTNFUIPE *OD ஶ໊ͳ8FCΞϓϦ։ൃϑϨʔϜϫʔΫ
KPPCZͱ w ࡞ऀ&EHBS&TQJOB !KLOBDL ͞Μ w +BWBωΠςΟϒ w .JDSP8FCΞϓϦέʔγϣϯ։ൃͷϑϨʔϜϫʔΫ
w ར༻ՄೳͳϞδϡʔϧ͕ެࣜͰ݁ߏ༻ҙ͞ΕͯΔ w طଘͷ+BWBϥΠϒϥϦΛ͍·ΘͤΔ w εςʔτϨεͳͷͰεέʔϧ͍͢͠ w /FUUZ +FUUZ VOEFSUPX ਖ਼֬ͳൃԻෆ໌ɾɾ
⡥$MBTTNFUIPE *OD 8FC'SBNFXPSL#FODINBSL IUUQTXXXUFDIFNQPXFSDPNCFODINBSLT TFDUJPOEBUBSIXQFBLUFTUKTPO
,JMMFSGFBUVSF w+BWB/BUJWF w4DBMBCMF wNPEVMBS wNVMUJMBOHVBHF +BWB +BWBTDSJQU wNJDSPXFCGSBNFXPSL
+BWB/BUJWF
⡥$MBTTNFUIPE *OD +BWB/BUJWF w ಈ࡞࠷͕݅+BWBҎ্ w +BWBҎ্ NBWFOYҎ্ w ϥϜμࣜΛͬͨهड़͕ެࣜͷελϯμʔυ
w ίϯύΫτʹॻ͚Δ w ܕਪͱϥϜμࣜͷԸܙʹ༩Γ·͠ΐ͏ w +BWBҎ߱ͷ+7.͕ಈ࡞ج൫ w IUUQPUOEOMEPSBDMFDPKQPOEFNBOEKBWBEBZ QEG$+BWB%BZQEG
⡥$MBTTNFUIPE *OD 4DBMBCMF w εςʔτϨε w ࢄڥʹͯεέʔϧ͍ͤ͢͞ w "84ͷڥ͕εέʔϧ͢Δͷͱɺ૬ੑ͕ྑ͍
ͣ w ະݕূ
'BTU
⡥$MBTTNFUIPE *OD 'BTU w ىಈ͕ૣ͍ w σϑΥϧτͷαʔόʔ/FUUZ w IUUQOFUUZJP w
ͦͷଞαϙʔτ w +FUUZ IUUQXXXFDMJQTFPSHKFUUZ w VOEFSUPX IUUQVOEFSUPXJP w 5PNDBUͳ͍ IUUQKPPCZPSHEPDTFSWFST
⡥$MBTTNFUIPE *OD ىಈ͕ૣ͍ͷਖ਼ٛ
⡥$MBTTNFUIPE *OD $PEF w ίʔυ͕εοΩϦ ίϯύΫτʹ public class App
extends Jooby { Logger log = LoggerFactory.getLogger(App.class); { get("/", request -> "Hello jooby"); } public static void main(final String[] args) throws Throwable { run(App::new, args); } } ͜Ε͚ͩ
⡥$MBTTNFUIPE *OD $PNQBSF @Controller public class SimpleController { @RequestMapping("/simple") public
@ResponseBody String simple() { return "Hello world!"; } } { get("/simple", () -> "Hello World!"); } 4QSJOH.7$ +PPCZ ͪΐͬͱଇؾຯɾɾʁ
⡥$MBTTNFUIPE *OD ಛ w SPVUFTίʔυͰॻ͘ w 4JOBUSBFYQSFTTKT͔ΒΠϯεύΠΞ w εΫϦϓτͬΆ͘ॻ͚ΔߏจΛ༻ҙ w
جຊϥϜμࣜ w 'VODUJPOབྷΈͷγϯλοΫεγϡΨʔʹΑͬͯɺѹతʹه ड़ྔ͕গͳ͍ɻϥϜμࣜͱܕਪڧྗɻ w ܕਪʹΑΓهड़͕লུ͞ΕΔ͕ɺ*%&ͷྗʹཔΔͱಛʹࠔΒ ͳ͍ɻ w ΠϯελϯεΠχγϟϥΠβ w ίϯετϥΫλͰهड़͢ΔΑΓݟ௨͕͠ྑ͍ ڧ੍Ͱͳ͍
⡥$MBTTNFUIPE *OD ڧྗͳػೳΛհ wϞδϡʔϧͰػೳͷ֦ு wΠϯλʔηϓτͰϑΟϧλɺϩά wઃఆϑΝΠϧϨεʢඞཁ࠷খݶʣ
࡞ͨ͠ΞϓϦέʔγϣϯ NPEVMF NPEVMF NPEVMF NPEVMF .PEVMBS
⡥$MBTTNFUIPE *OD Ϟδϡʔϧͱʁ w +PPCZͷಛͷҰͭɻऔΓࠐΈɺऔΓ֎ָ͕͠ w +PPCZʹ͓͍ͯͷϞδϡʔϧͱɺ+PPCZʹΈࠐ Ή͜ͱͷͰ͖Δίϯϙʔωϯτʢ࣮ࡍʹΠϯλϑΣʔ ε
w +PPCZ.PEVMFͱ͍͏ΠϯλϑΣʔεΛ࣮ɺϒ Ϧοδͯ͋͛͠ΕطଘͷϥΠϒϥϦΛऔΓࠐΉͷ ͱͯ؆୯ w ͪΐͬͱ಄ͷྑ͍ϑΝΫτϦͷΑ͏ͳΠϝʔδ
⡥$MBTTNFUIPE *OD ϞδϡʔϧͷΠϝʔδ +PPCZ طଘͷϥΠϒϥϦ ͜ͷ··ͩͱऔΓࠐΊͳ͍ʂ
⡥$MBTTNFUIPE *OD ϞδϡʔϧͷΠϝʔδ +PPCZ +PPCZ.PEVMF طଘͷϥΠϒϥϦ +PPCZ.PEVMFΠϯλϑΣʔε Ͱϥοϓ͢Δ͚ͩͰ ؆୯ʹϞδϡʔϧԽ
⡥$MBTTNFUIPE *OD ϞδϡʔϧͷಡΈࠐΈ ΞϓϦέʔγϣϯ؆୯ʹΈࠐΊΔ public class App extends Jooby
{ { use(new MyModule()); } { get("/", req -> { MyService service = req.require(MyService.class); return service.doSomething(); }); } } ϞδϡʔϧΛϩʔυ ϞδϡʔϧͰϩʔυ͞ΕͨΫ ϥεΛΠϯελϯεԽ Ϟδϡʔϧ͕ϩʔυ͞Εͯͳ͍ ͱ/VMM1PJOUFS&YDFQUJPO ϞδϡʔϧͰ४උ͞Εͨ αʔϏεΫϥε
⡥$MBTTNFUIPE *OD .PEVMFΛՃ͢Δ ࡞ͨ͠ΞϓϦέʔγϣϯ "84.PEVMF -PHCBDL "844%,͕ ͍͍ͨ
⡥$MBTTNFUIPE *OD .PEVMFͷ४උ <dependency> <groupId>org.jooby</groupId> <artifactId>jooby-aws</artifactId> <version>1.0.0.CR7</version> </dependency> QPNYNMʹґଘՃ
aws.accessKey = ACCCESSSSSSSSSKEYYYYYYYYYYYYYYYY aws.secretKey = seeeeeCreeeeeeetKeeeyyyyyyyyyyy BQQMJDBUJPODPOGʹઃఆՃ IUUQTHJUIVCDPNKPPCZQSPKFDUKPPCZUSFFNBTUFSKPPCZBXT
⡥$MBTTNFUIPE *OD "84.PEVMFΛϩʔυ public class App extends Jooby {
{ use(new Aws() .with(creds -> new AmazonS3Client(creds)) ); } } ϞδϡʔϧՃ "NB[PO4Λ͏߹ public class App extends Jooby { { get("/", req -> { AmazonS3Client s3Client = req.require(AmazonS3Client.class); log.info("get s3Client. {}”, s3Client); return “AmazonS3Client"; }); } } ඞཁͳΠϯελϯεऔಘ
⡥$MBTTNFUIPE *OD "84.PEVMFͷதΛ͍ͯΈΔ private Builder<BiFunction<AWSCredentialsProvider, Config, AmazonWebServiceClient>> callbacks =
ImmutableList.builder(); public Aws with( final BiFunction<AWSCredentialsProvider, Config, AmazonWebServiceClient> callback) { requireNonNull(callback, "Callback is required."); callbacks.add(callback); return this; } public Aws with(final Function<AWSCredentialsProvider, AmazonWebServiceClient> callback) { return with((creds, conf) -> callback.apply(creds)); } "XTϞδϡʔϧXJUIϝιουΛͬͯ$BMMCBDLΛొ ͠·͢ɻSFUVSOUIJTͰࣗΛฦ٫͍ͯ͠ΔͨΊCVJMEFS ͱͯ͠ར༻Ͱ͖·͢ɻ
⡥$MBTTNFUIPE *OD ॳظԽॲཧ @Override public void configure(final Env env,
final Config config, final Binder binder) { callbacks.build().forEach(it -> { ConfigCredentialsProvider creds = new ConfigCredentialsProvider(config); AmazonWebServiceClient service = it.apply(creds, config); creds.service(service.getServiceName()); Class serviceType = service.getClass(); Class[] interfaces = serviceType.getInterfaces(); if (interfaces.length > 0) { // pick first binder.bind(interfaces[0]).toInstance(service); } binder.bind(serviceType).toInstance(service); env.onStop(new AwsShutdownSupport(service)); after(env, binder, config, service); }); } DPOpHVSF.PEVMF͕ϩʔυ͞ΕͨࡍʹॳظԽॲཧΛ࣮ߦ͠·͢ɻ ඞཁͳ$MJFOUͷΫϥεΛDBMMCBDLT͔ΒऔΓग़͠ɺ$SFEFOUJBMͱڞʹॳ ظԽͭͭ͠ɺΫϥεʹରͯ͠ͷΠϯελϯεΛొ͍͖ͯ͠·͢ɻ
⡥$MBTTNFUIPE *OD ϞδϡʔϧϥΠϑαΠΫϧ .PEVMF Πϯελϯεੜ ΞϓϦέʔγϣϯ ॳظԽ ΞϓϦέʔγϣϯ ऴྃ
.PEVMF ऴྃ .PEVMFDPOpHVSF &OWPO4UBSU &OWPO4UPQ .PEVMFͰඞཁͳॳظઃఆͳͲDPOpHVSFͰ࣮ߦɻ PO4UBSU PO4UPQͷఆٛͪ͜ΒͰߦ͓͚ͬͯྑ͍ɻ
⡥$MBTTNFUIPE *OD +BDLTPO.PEVMF ࡞ͨ͠ΞϓϦέʔγϣϯ "84.PEVMF -PHCBDL ϨεϙϯεΛ+40/ ʹ͍ͨ͠ +BDLTPO
.PEVMF
⡥$MBTTNFUIPE *OD .PEVMFͷ४උ <dependency> <groupId>org.jooby</groupId> <artifactId>jooby-jackson</artifactId> <version>1.0.0.CR7</version> </dependency> QPNYNMʹґଘՃ
IUUQTHJUIVCDPNKPPCZQSPKFDUKPPCZUSFFNBTUFSKPPCZKBDLTPO public class App extends Jooby { { use(new Jackson()); } } ϞδϡʔϧՃ
⡥$MBTTNFUIPE *OD +BDLTPOϞδϡʔϧՃͷ#FGPSFͱ"GUFS IUUQTHJUIVCDPNKPPCZQSPKFDUKPPCZUSFFNBTUFSKPPCZKBDLTPO public class User { public
User(String name, boolean steamer) { this.name = name; this.steamer = steamer; } public String name; public boolean steamer; } get("/user/{name}", (req) -> { return new User(req.param("name").value(), true); }); Ϩεϙϯεʹ6TFSΛฦ٫͢Δ
⡥$MBTTNFUIPE *OD +BDLTPOϞδϡʔϧՃͷ#FGPSFͱ"GUFS jp.classmethod.User@d719ea9 #FGPSF IUUQTHJUIVCDPNKPPCZQSPKFDUKPPCZUSFFNBTUFSKPPCZKBDLTPO {"name":"komuro","steamer":true} "GUFS
⡥$MBTTNFUIPE *OD Ϟδϡʔϧಈ࡞·ͱΊ wΠϝʔδͱͯ͠ΠϯελϯεΛੜͯ͠ฦ٫͢Δ಄ ͷྑ͍ϑΝΫτϦʹΠϝʔδ͕͍ۙ wϞδϡʔϧʹϥΠϑαΠΫϧ͕͋Γɺϩʔυ࣌ʹϥ ΠϒϥϦͷॳظԽॲཧΛهड़͠ɺΞϓϦέʔγϣϯऴ ྃ࣌ʹഁغͷͨΊͷॲཧͳͲΛهड़Ͱ͖Δ wϞδϡʔϧͰར༻͢Δ͜ͱͷͰ͖Δࢦఆ͞ΕͨΫϥε ʹରͯ͠ͷ࣮ʢΠϯελϯεʣΛఆٛ͢Δ
⡥$MBTTNFUIPE *OD ͦͷଞ༻ҙ͞ΕͯΔϞδϡʔϧ w KPPCZBLLB w KPPCZBTTFUTMFTTK w KPPCZBTTFUTOPEFKT w
KPPCZDBTTBOESB w KPPCZDPVDICBTF w KPPCZFMBTUJDTFBSDI w KPPCZKBDLTPO w KPPCZNPOHPEC w KPPCZTXBHHFS IUUQTHJUIVCDPNKPPCZQSPKFDUKPPCZ
&SSPS)BOEMJOH
⡥$MBTTNFUIPE *OD ΤϥʔϋϯυϦϯάʹ͍ͭͯ +PPCZʹ͓͍ͯͷΤϥʔϋϯυϦϯάҎԼͷछྨͰ ॲཧ͢Δ͜ͱ͕Ͱ͖·͢ɻ w)551εςʔλείʔυΛϋϯυϦϯά w&YDFQUJPOΫϥεΛϋϯυϦϯά wͦͷଞྫ֎ΛϋϯυϦϯά
⡥$MBTTNFUIPE *OD εςʔλείʔυ err(400, (req, rsp, ex) -> {
logger.error("Bad Request“, ex); rsp.send("Bad Request"); }); +PPCZ શͯͷύεʹରͯ͠ɺΤϥʔ͕ൃੜͨ͠߹ͷॲཧΛهड़͠·͢ɻ͜ͷ ߹ɺจࣈྻΛϨεϙϯεͰฦ٫
⡥$MBTTNFUIPE *OD &YDFQUJPOΫϥε err(NullPointerException.class, (req, rsp, ex) -> {
Map<String, String> messageMap = new HashMap<>(); messageMap.put("reaction", "Ψο"); messageMap.put("cause", "͵ΔΆʂ"); logger.error(“null pointer exception”, ex); rsp.send(messageMap.get(“reaction")); // ΤϥʔΛฦ٫ }); +PPCZ શͯͷύεʹରͯ͠ɺ/VMM1PJUFS&YDFQUJPO͕ൃੜͨ͠߹ͷॲཧΛهड़͠· ͢ɻ
⡥$MBTTNFUIPE *OD ͦͷଞྫ֎ public class App extends Jooby {
{ err((req, rsp, ex) -> { logger.error("ex: {}", ex); rsp.send("other error"); }); } } +PPCZ 3PVUFTFSS &SS)BOEMFSFSS ͷγϯλοΫεγϡΨʔͰ͢ɻ IUUQKPPCZPSHBQJEPDTPSHKPPCZ3PVUFTIUNMFSS PSHKPPCZ&SS)BOEMFS Ҿʹ͍ͭͯɺΠϯλϑΣʔε&SS)BOEMFSͷIBOEMFϝιουͰ֬ೝͰ͖ ·͢ɻ IUUQKPPCZPSHBQJEPDTPSHKPPCZ&SS)BOEMFSIUNM
*OUFSDFQUPST
⡥$MBTTNFUIPE *OD *OUFSDFQUPST w+PPCZͰͭͷ*OUFSDFQUPS͕༻ҙ͞Ε͍ͯ·͢ɻ wSFRVFTUॲཧલʹ࣮ߦ͞ΕΔ#FGPSF wSFTQPOTFΛฦ٫͢Δલʹ࣮ߦ͞ΕΔ"GUFS wSFTQPOTFฦ٫ޙʹ࣮ߦ͞ΕΔ$PNQMFUF 3FRVFTU 4FSWJDF
#FGPSF 3FTQPOTF "GUFS $PNQMFUF 4QSJOHͰݴ͏)BOEMFS*OUFSDFQUPS "TQFDU+૬ʁ
⡥$MBTTNFUIPE *OD #FGPSF before("/simple", (req, rsp) -> { logger.info("Header
: {}", req.headers()); }); TJNQMFʹϦΫΤετ͢ΔͱॲཧલʹΠϯλʔηϓτ͠·͢ɻϦΫΤετͷό ϦσʔγϣϯϑΟϧλʔ࣮ʹ༗ޮͰ͠ΐ͏͔ɻ $ curl http://localhost:8080/simple Hello World! [2016-10-06 17:51:23,524]-[netty task-3-4] INFO jp.classmethod.App - Header : {Host=[localhost:8080], Connection=[keep-alive], Cache-Control=[max-age=0], Upgrade-Insecure-Requests=[1], User-Agent=[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/ 537.36], Accept=[text/html,application/xhtml+xml,application/xml;q=0.9,image/ webp,*/*;q=0.8], Accept-Encoding=[gzip, deflate, sdch], Accept-Language=[ja,en- US;q=0.8,en;q=0.6], content-length=[0]} ग़ྗ
⡥$MBTTNFUIPE *OD "GUFS after("*", (req, rsp, result) -> {
logger.info("after process"); return result; }); ग़ྗ શͯͷύεʹରͯ͠ϨεϙϯεΛฦ٫͢ΔલʹΠϯλʔηϓτɻ Τϥʔ͕ൃੜͨ͠߹࣮ߦ͞Εͳ͍ͷͰҙɻ $ curl http://localhost:8080/simple/revisited revisited [2016-10-06 18:38:17,360]-[netty task-3-2] INFO jp.classmethod.App - after process [2016-10-06 18:38:17,361]-[netty task-3-2] INFO jp.classmethod.App - Call path: / simple/revisited
⡥$MBTTNFUIPE *OD $PNQMFUF complete("*", (req, rsp, cause) -> {
// intercept log logger.info("Call path: {}", req.path()); }); ग़ྗ શͯͷύεʹରͯ͠3FTQPOTFฦ٫ޙʹ࣮ߦ͞Ε·͢ɻϩάͱ͔ʹར༻Ͱ͖ͦ ͏ɻͪ͜Βਖ਼ৗɾΤϥʔؔͳ͘ඞ࣮ͣߦ͞Ε·͢ɻ [2016-10-06 18:42:21,520]-[netty task-3-6] INFO jp.classmethod.App - after process [2016-10-06 18:42:21,520]-[netty task-3-6] INFO jp.classmethod.App - Call path: / simple/revisited
⡥$MBTTNFUIPE *OD ·ͱΊ wܰྔ͔ͭಈ࡞͕ૣ͍ҹ wઃఆϑΝΠϧ͕ΑΓগͳ͘ίʔυͰهड़ wεέʔϧ͘͢͠ɺػೳͷΈࠐΈ͍͢͠ w4XBHHFSͷग़ྗͳͲ͋Γ㽂 wখ࢝͘͞ΊΔ8FCΞϓϦέʔγϣϯͷ։ൃϑϨʔϜ ϫʔΫ wͦΖͦΖ4QSJOHͷ࣍Λ୳ͦ͏
⡥$MBTTNFUIPE *OD ࢀরϦϯΫ w IUUQXXXJOGPXPSMEDPNBSUJDMFKBWBKPPCZGSBNFXPSL TJNQMJpFTKBWBXFCEFWFMPQNFOUIUNM w IUUQKBNFTXBSOFSCMPHMPHEPXODPNQPTUTKPPCZ GSBNFXPSLFBTZUPIBOEMFKBWBXFCEFWFMPQNFOU w
IUUQKPPCZPSH w IUUQTHJUIVCDPNKPPCZQSPKFDUKPPCZ w IUUQRJJUBDPNLB[VLJ[PPJUFNTCDGDDEC w IUUQXXXJSBTVUPZBDPN
%FWFMPQFST*0 " ⡥$MBTTNFUIPE *OD #cmdevio2016 ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻ ϒϩάͰҾ͖ଓ͖ใൃ৴͍͖ͯ͠·͢ɻ