From 4718349995bb702e18e756c7d279f03ae7ac1370 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 18 Sep 2023 07:56:48 +0000 Subject: [PATCH] deploy: 2491e3129d09cdbab0bc178dcf2ff58995dd7fdc --- 2022-retrospective.html | 10 +-- 404.html | 8 +- accidental-duplication.html | 10 +-- ...-null-2b12c13f4f420a335c9e55dbea503f1b.png | Bin 0 -> 227445 bytes ...-null-95b3bbdce99ef36ba843986413e0421a.png | Bin 0 -> 197243 bytes ...31cc3.e832cb29.js => 00931cc3.94b71d28.js} | 2 +- ...2f8fc.04b8abb6.js => 0462f8fc.d191c72d.js} | 2 +- ...44f5f.2031d661.js => 04644f5f.b526cda7.js} | 2 +- ...bb6bd.dcff2b7f.js => 09fbb6bd.454abeb1.js} | 2 +- ...71de2.13b9004c.js => 0c071de2.96df65c8.js} | 2 +- ...7646f.abc0fc85.js => 0d47646f.04fe4885.js} | 2 +- assets/js/101cf32b.c05aa69b.js | 1 - assets/js/101cf32b.cc00cd31.js | 1 + ...beba7.55ec34b6.js => 12cbeba7.0896b1b8.js} | 2 +- ...997fc.034c8bb9.js => 1bb997fc.cb9e0f6d.js} | 2 +- ...700de.c27d727c.js => 226700de.4a36650b.js} | 2 +- ...346fa.b38c6387.js => 270346fa.f3b605a3.js} | 2 +- ...2e534.f340f2ea.js => 2832e534.9b8cccfb.js} | 2 +- ...0a69c.4e824af5.js => 2e10a69c.9fb45902.js} | 2 +- ...370be.a84b65cc.js => 302370be.bdf8f944.js} | 2 +- ...2299c.6deb4095.js => 32b2299c.56edc850.js} | 2 +- ...93ec4.06faed4f.js => 35293ec4.fd6496e4.js} | 2 +- ...ddb85.a0bcd890.js => 366ddb85.905cf70f.js} | 2 +- ...8699e.c4935117.js => 38d8699e.5a2f1220.js} | 2 +- ...d95bc.c9f19c2d.js => 3b0d95bc.bbfd78b6.js} | 2 +- ...16fd0.69ad4ad7.js => 3fc16fd0.5d70fe41.js} | 2 +- assets/js/4515250b.7c65c757.js | 1 + ...bd227.9c343d08.js => 459bd227.d754b82a.js} | 2 +- ...d1d32.ff33e518.js => 48bd1d32.00513cdb.js} | 2 +- ...882d1.c40a80ba.js => 494882d1.73f8d425.js} | 2 +- ...9fc42.c6328d60.js => 4959fc42.6cdbaa8f.js} | 2 +- assets/js/53e4509a.c8f25e5c.js | 1 + ...b095e.20ac0662.js => 54cb095e.a3fc1b31.js} | 2 +- ...1b25c.fcb5e683.js => 5f81b25c.8a3fc417.js} | 2 +- ...3f82b.e76b4eb3.js => 6093f82b.729560f0.js} | 2 +- ...a92d5.aca340d5.js => 635a92d5.eeaf5e2b.js} | 2 +- ...68a43.fd9f94bb.js => 64868a43.2f5e5632.js} | 2 +- ...28c32.535e44e8.js => 69c28c32.4b72ca56.js} | 2 +- assets/js/6d4355d3.3a49ae35.js | 1 + ...1c948.4d90960d.js => 6dd1c948.5efd0dca.js} | 2 +- ...75fcd.2c549c8b.js => 70275fcd.437d81af.js} | 2 +- ...fb852.5bbaace5.js => 754fb852.26719bd8.js} | 2 +- ...2a24e.cee52218.js => 7762a24e.a15ab1a0.js} | 2 +- ...1d52f.47b6475a.js => 7af1d52f.c77778a9.js} | 2 +- ...60b4b.abe732c9.js => 80960b4b.4272e931.js} | 2 +- assets/js/814f3328.11618e69.js | 1 + assets/js/814f3328.b3fcd2fa.js | 1 - ...70fc3.fa990a2e.js => 87070fc3.0f052f4b.js} | 2 +- ...c1e5a.cf85e1ed.js => 871c1e5a.87654b34.js} | 2 +- ...5b77c.23797b44.js => 8d05b77c.241f55bf.js} | 2 +- ...d512b.27bf5da5.js => 8fbd512b.23ff3cd4.js} | 2 +- assets/js/905ecccc.06ce4047.js | 1 - assets/js/905ecccc.806a92d0.js | 1 + ...dae60.b0a3bff9.js => 96adae60.b3f5d5ae.js} | 2 +- ...f7647.b7931b3d.js => 981f7647.3ede4ce8.js} | 2 +- ...e8fd1.9d0c80a1.js => 9cfe8fd1.0b589244.js} | 2 +- ...77a5e.66ca6253.js => 9e477a5e.85b777bc.js} | 2 +- assets/js/9f586ca3.0edad504.js | 1 + assets/js/a1877440.7a23249c.js | 1 - assets/js/a1877440.88e9cb51.js | 1 + ...f2942.341ff912.js => a43f2942.90e79db4.js} | 2 +- ...57bb9.31255d4d.js => a5557bb9.dae55ef5.js} | 2 +- assets/js/a710d533.3086487e.js | 1 - assets/js/a710d533.a76e7019.js | 1 + assets/js/b2b675dd.b8d348e9.js | 1 - assets/js/b2b675dd.f512bfe6.js | 1 + ...1ebb7.a60899a2.js => b421ebb7.867062fd.js} | 2 +- ...cb85b.f345a857.js => b88cb85b.d779ea6a.js} | 2 +- ...bedb9.da75fb17.js => c29bedb9.64d8690b.js} | 2 +- ...d205b.6a9b956d.js => c55d205b.8cce9cb6.js} | 2 +- assets/js/c573638f.2758b3b4.js | 1 + assets/js/c573638f.8593a47b.js | 1 - ...1b628.3470f566.js => caf1b628.0cf6df3a.js} | 2 +- assets/js/cc3bdf2f.5664f42d.js | 1 + assets/js/cd68cda1.68c11170.js | 1 + ...4cdf1.c290633a.js => d0e4cdf1.421afec6.js} | 2 +- ...ef389.6c21f19a.js => d1cef389.a646f88e.js} | 2 +- ...11248.4b920029.js => d2611248.928af5e7.js} | 2 +- ...1ad2d.2e82e064.js => d361ad2d.f08f5569.js} | 2 +- ...fd269.00a8a343.js => d50fd269.9ae96e63.js} | 2 +- ...f7a37.b39dc39a.js => d86f7a37.bbe448b1.js} | 2 +- ...df5ef.fe2623c5.js => d8cdf5ef.4c076bff.js} | 2 +- assets/js/e2326195.ed359df7.js | 1 + ...bfe18.8b575fa3.js => e4ebfe18.985f0369.js} | 2 +- ...33099.50f7c47e.js => eec33099.8543c571.js} | 2 +- ...b2427.9c5560c3.js => ef5b2427.51a774af.js} | 2 +- ...2d221.a33f27f9.js => f332d221.b5f875cb.js} | 2 +- ...308ad.77e4f1b1.js => f3e308ad.4ced4843.js} | 2 +- ...49e13.f75cc00c.js => f4f49e13.a45671c0.js} | 2 +- ...a8651.6bd03765.js => f75a8651.e89d591b.js} | 2 +- ...09a7e.e092ca80.js => f8409a7e.48009fa4.js} | 2 +- ...57548.5fe6f500.js => fbd57548.7dba7e8f.js} | 2 +- assets/js/main.2e13b410.js | 2 - assets/js/main.b4ffb42c.js | 2 + ...CENSE.txt => main.b4ffb42c.js.LICENSE.txt} | 0 assets/js/runtime~main.52d2c86e.js | 1 + assets/js/runtime~main.55afa7c4.js | 1 - async-exception.html | 29 +++++++ atom.xml | 16 +++- blackjack-retrospective.html | 10 +-- blog.html | 10 +-- book-leadership-and-self-deception.html | 10 +-- book-writer.html | 10 +-- chess-retrospective.html | 10 +-- cloudwatch.html | 10 +-- composite.html | 10 +-- custom-jdbc-template.html | 10 +-- db-replication.html | 10 +-- docs.html | 10 +-- docs/book/getting-out-of-the-box.html | 10 +-- docs/culture/postmortem.html | 10 +-- docs/design/package.html | 10 +-- docs/etc/communication.html | 10 +-- docs/etc/develop-with-spring.html | 10 +-- docs/etc/experience-and-self-question.html | 10 +-- docs/etc/healthful-growth.html | 10 +-- docs/jpa/key.html | 10 +-- docs/linux/shell.html | 8 +- docs/linux/swap.html | 8 +- docs/monitoring/intro.html | 10 +-- docs/network/load-balancing-algorithm.html | 10 +-- docs/network/load-balancing.html | 10 +-- docs/nginx/command.html | 10 +-- docs/nginx/static-file.html | 10 +-- docs/performance/throughput-latency.html | 10 +-- docs/performance/throughput.html | 10 +-- docs/performance/types.html | 10 +-- docs/tags.html | 8 +- docs/tags/book.html | 8 +- docs/tags/etc.html | 8 +- docs/tags/jpa.html | 8 +- docs/tags/latency.html | 8 +- docs/tags/load-balancing.html | 8 +- docs/tags/monitoring.html | 8 +- docs/tags/network.html | 8 +- docs/tags/nginx.html | 8 +- docs/tags/package.html | 8 +- docs/tags/performance.html | 8 +- docs/tags/postmortem.html | 8 +- docs/tags/test.html | 8 +- docs/tags/throughput.html | 8 +- docs/test/benefit.html | 10 +-- docs/test/first.html | 10 +-- docs/test/heuristics.html | 10 +-- docs/test/stairstep.html | 10 +-- docusaurus.html | 10 +-- grasp.html | 10 +-- index.html | 8 +- innodb-lock.html | 10 +-- intellij-settings.html | 10 +-- java-class-file.html | 10 +-- java-spring-springboot.html | 10 +-- jenkins.html | 10 +-- jsr-310.html | 10 +-- kotlin-null.html | 10 +-- ladder-retrospective.html | 10 +-- level2-interview-retrospective.html | 10 +-- mock-static-method.html | 10 +-- mysql-lock.html | 10 +-- order-retrospective.html | 10 +-- page/10.html | 24 +++--- page/11.html | 31 +++---- page/12.html | 38 ++++----- page/13.html | 43 +++++----- page/14.html | 40 +++++---- page/15.html | 32 ++++---- page/16.html | 38 ++++----- page/17.html | 39 ++++----- page/18.html | 35 ++++---- page/19.html | 42 ++++------ page/2.html | 52 +++++++----- page/20.html | 43 +++++----- page/21.html | 36 ++++----- page/22.html | 41 +++++----- page/23.html | 46 +++++------ page/24.html | 45 ++++++----- page/25.html | 49 +++++------ page/26.html | 61 +++++++------- page/27.html | 58 +++++++------ page/28.html | 42 +++++----- page/29.html | 46 +++++------ page/3.html | 76 +++++------------- page/30.html | 50 +++++++----- page/31.html | 44 +++++----- page/32.html | 38 +++++---- page/33.html | 52 ++++-------- page/34.html | 75 +++++++++-------- page/35.html | 46 +++++++++-- page/36.html | 41 ++-------- page/37.html | 70 ++++++++-------- page/38.html | 66 ++++++++------- page/39.html | 42 +++++++--- page/4.html | 76 +++++++++++++----- page/40.html | 17 ++-- page/41.html | 21 ++--- page/42.html | 27 ++++--- page/43.html | 35 +++----- page/44.html | 43 +++++----- page/45.html | 29 ++++--- page/46.html | 30 +++++++ page/5.html | 43 +++++----- page/6.html | 55 ++++++------- page/7.html | 46 +++++++---- page/8.html | 40 ++++----- page/9.html | 38 ++++++--- parameterized-tests.html | 10 +-- performance-test-type.html | 10 +-- racing-car-retrospective.html | 10 +-- route-image-async-with-event.html | 10 +-- route-image-implementation.html | 10 +-- route-image-intro.html | 10 +-- route-image-python.html | 10 +-- rss.xml | 16 +++- search.html | 8 +- shopping-cart-retrospective.html | 10 +-- sitemap.xml | 2 +- subway-retrospective.html | 10 +-- tags.html | 10 +-- tags/async.html | 42 +++------- tags/async/page/2.html | 51 ++++++++++++ tags/awt.html | 10 +-- tags/awt/page/2.html | 10 +-- tags/book.html | 10 +-- tags/book/page/2.html | 10 +-- tags/book/page/3.html | 10 +-- tags/class.html | 10 +-- tags/cloudwatch.html | 10 +-- tags/composite.html | 10 +-- tags/data-base.html | 10 +-- tags/data-base/page/2.html | 10 +-- tags/data-base/page/3.html | 10 +-- tags/documentation.html | 10 +-- tags/dto.html | 10 +-- tags/elastic-beanstalk.html | 10 +-- tags/event.html | 10 +-- tags/exception.html | 29 +++++++ tags/grasp.html | 10 +-- tags/image.html | 10 +-- tags/image/page/2.html | 10 +-- tags/image/page/3.html | 10 +-- tags/inno-db.html | 10 +-- tags/intelli-j.html | 10 +-- tags/isolation.html | 10 +-- tags/java.html | 10 +-- tags/java/page/2.html | 10 +-- tags/java/page/3.html | 10 +-- tags/java/page/4.html | 10 +-- tags/java/page/5.html | 10 +-- tags/jdbc.html | 10 +-- tags/jenkins.html | 10 +-- tags/kotlin.html | 10 +-- tags/lock.html | 10 +-- tags/lock/page/2.html | 10 +-- tags/log.html | 10 +-- tags/mock.html | 10 +-- tags/mockito.html | 10 +-- tags/monitoring.html | 10 +-- tags/my-sql.html | 10 +-- tags/mysql.html | 10 +-- tags/oop.html | 10 +-- tags/pattern.html | 10 +-- tags/performance-test.html | 10 +-- tags/python.html | 10 +-- tags/replication.html | 10 +-- tags/retrospective.html | 10 +-- tags/retrospective/page/10.html | 10 +-- tags/retrospective/page/11.html | 10 +-- tags/retrospective/page/12.html | 10 +-- tags/retrospective/page/13.html | 10 +-- tags/retrospective/page/14.html | 10 +-- tags/retrospective/page/15.html | 10 +-- tags/retrospective/page/16.html | 10 +-- tags/retrospective/page/17.html | 10 +-- tags/retrospective/page/2.html | 10 +-- tags/retrospective/page/3.html | 10 +-- tags/retrospective/page/4.html | 10 +-- tags/retrospective/page/5.html | 10 +-- tags/retrospective/page/6.html | 10 +-- tags/retrospective/page/7.html | 10 +-- tags/retrospective/page/8.html | 10 +-- tags/retrospective/page/9.html | 10 +-- tags/spring-boot.html | 10 +-- tags/spring.html | 10 +-- tags/static.html | 10 +-- tags/teco-chat.html | 10 +-- tags/teco-chat/page/2.html | 10 +-- tags/teco-chat/page/3.html | 10 +-- tags/test.html | 10 +-- tags/time.html | 10 +-- tags/transaction.html | 10 +-- tags/web-socket.html | 10 +-- tags/woowahan-techcourse.html | 10 +-- tags/woowahan-techcourse/page/10.html | 10 +-- tags/woowahan-techcourse/page/11.html | 10 +-- tags/woowahan-techcourse/page/12.html | 10 +-- tags/woowahan-techcourse/page/13.html | 10 +-- tags/woowahan-techcourse/page/2.html | 10 +-- tags/woowahan-techcourse/page/3.html | 10 +-- tags/woowahan-techcourse/page/4.html | 10 +-- tags/woowahan-techcourse/page/5.html | 10 +-- tags/woowahan-techcourse/page/6.html | 10 +-- tags/woowahan-techcourse/page/7.html | 10 +-- tags/woowahan-techcourse/page/8.html | 10 +-- tags/woowahan-techcourse/page/9.html | 10 +-- tecochat-retrospective-1.html | 10 +-- tecochat-retrospective-2.html | 10 +-- tecochat-retrospective-3.html | 10 +-- test-double.html | 10 +-- the-essence-of-object-orientation.html | 10 +-- tomcat-retrospective.html | 12 +-- transaction-and-isolation.html | 10 +-- web-racing-car-retrospective.html | 10 +-- websocket.html | 10 +-- woowacourse-level1-retrospective.html | 10 +-- woowacourse-level2-retrospective.html | 10 +-- woowacourse-level3-retrospective.html | 10 +-- 316 files changed, 2082 insertions(+), 1907 deletions(-) create mode 100644 assets/images/mdc-not-null-2b12c13f4f420a335c9e55dbea503f1b.png create mode 100644 assets/images/mdc-null-95b3bbdce99ef36ba843986413e0421a.png rename assets/js/{00931cc3.e832cb29.js => 00931cc3.94b71d28.js} (57%) rename assets/js/{0462f8fc.04b8abb6.js => 0462f8fc.d191c72d.js} (98%) rename assets/js/{04644f5f.2031d661.js => 04644f5f.b526cda7.js} (98%) rename assets/js/{09fbb6bd.dcff2b7f.js => 09fbb6bd.454abeb1.js} (57%) rename assets/js/{0c071de2.13b9004c.js => 0c071de2.96df65c8.js} (56%) rename assets/js/{0d47646f.abc0fc85.js => 0d47646f.04fe4885.js} (98%) delete mode 100644 assets/js/101cf32b.c05aa69b.js create mode 100644 assets/js/101cf32b.cc00cd31.js rename assets/js/{12cbeba7.55ec34b6.js => 12cbeba7.0896b1b8.js} (57%) rename assets/js/{1bb997fc.034c8bb9.js => 1bb997fc.cb9e0f6d.js} (57%) rename assets/js/{226700de.c27d727c.js => 226700de.4a36650b.js} (57%) rename assets/js/{270346fa.b38c6387.js => 270346fa.f3b605a3.js} (57%) rename assets/js/{2832e534.f340f2ea.js => 2832e534.9b8cccfb.js} (57%) rename assets/js/{2e10a69c.4e824af5.js => 2e10a69c.9fb45902.js} (57%) rename assets/js/{302370be.a84b65cc.js => 302370be.bdf8f944.js} (98%) rename assets/js/{32b2299c.6deb4095.js => 32b2299c.56edc850.js} (57%) rename assets/js/{35293ec4.06faed4f.js => 35293ec4.fd6496e4.js} (57%) rename assets/js/{366ddb85.a0bcd890.js => 366ddb85.905cf70f.js} (99%) rename assets/js/{38d8699e.c4935117.js => 38d8699e.5a2f1220.js} (57%) rename assets/js/{3b0d95bc.c9f19c2d.js => 3b0d95bc.bbfd78b6.js} (98%) rename assets/js/{3fc16fd0.69ad4ad7.js => 3fc16fd0.5d70fe41.js} (98%) create mode 100644 assets/js/4515250b.7c65c757.js rename assets/js/{459bd227.9c343d08.js => 459bd227.d754b82a.js} (98%) rename assets/js/{48bd1d32.ff33e518.js => 48bd1d32.00513cdb.js} (98%) rename assets/js/{494882d1.c40a80ba.js => 494882d1.73f8d425.js} (57%) rename assets/js/{4959fc42.c6328d60.js => 4959fc42.6cdbaa8f.js} (57%) create mode 100644 assets/js/53e4509a.c8f25e5c.js rename assets/js/{54cb095e.20ac0662.js => 54cb095e.a3fc1b31.js} (57%) rename assets/js/{5f81b25c.fcb5e683.js => 5f81b25c.8a3fc417.js} (57%) rename assets/js/{6093f82b.e76b4eb3.js => 6093f82b.729560f0.js} (57%) rename assets/js/{635a92d5.aca340d5.js => 635a92d5.eeaf5e2b.js} (57%) rename assets/js/{64868a43.fd9f94bb.js => 64868a43.2f5e5632.js} (57%) rename assets/js/{69c28c32.535e44e8.js => 69c28c32.4b72ca56.js} (57%) create mode 100644 assets/js/6d4355d3.3a49ae35.js rename assets/js/{6dd1c948.4d90960d.js => 6dd1c948.5efd0dca.js} (57%) rename assets/js/{70275fcd.2c549c8b.js => 70275fcd.437d81af.js} (57%) rename assets/js/{754fb852.5bbaace5.js => 754fb852.26719bd8.js} (57%) rename assets/js/{7762a24e.cee52218.js => 7762a24e.a15ab1a0.js} (57%) rename assets/js/{7af1d52f.47b6475a.js => 7af1d52f.c77778a9.js} (57%) rename assets/js/{80960b4b.abe732c9.js => 80960b4b.4272e931.js} (57%) create mode 100644 assets/js/814f3328.11618e69.js delete mode 100644 assets/js/814f3328.b3fcd2fa.js rename assets/js/{87070fc3.fa990a2e.js => 87070fc3.0f052f4b.js} (99%) rename assets/js/{871c1e5a.cf85e1ed.js => 871c1e5a.87654b34.js} (57%) rename assets/js/{8d05b77c.23797b44.js => 8d05b77c.241f55bf.js} (57%) rename assets/js/{8fbd512b.27bf5da5.js => 8fbd512b.23ff3cd4.js} (68%) delete mode 100644 assets/js/905ecccc.06ce4047.js create mode 100644 assets/js/905ecccc.806a92d0.js rename assets/js/{96adae60.b0a3bff9.js => 96adae60.b3f5d5ae.js} (57%) rename assets/js/{981f7647.b7931b3d.js => 981f7647.3ede4ce8.js} (97%) rename assets/js/{9cfe8fd1.9d0c80a1.js => 9cfe8fd1.0b589244.js} (57%) rename assets/js/{9e477a5e.66ca6253.js => 9e477a5e.85b777bc.js} (97%) create mode 100644 assets/js/9f586ca3.0edad504.js delete mode 100644 assets/js/a1877440.7a23249c.js create mode 100644 assets/js/a1877440.88e9cb51.js rename assets/js/{a43f2942.341ff912.js => a43f2942.90e79db4.js} (98%) rename assets/js/{a5557bb9.31255d4d.js => a5557bb9.dae55ef5.js} (52%) delete mode 100644 assets/js/a710d533.3086487e.js create mode 100644 assets/js/a710d533.a76e7019.js delete mode 100644 assets/js/b2b675dd.b8d348e9.js create mode 100644 assets/js/b2b675dd.f512bfe6.js rename assets/js/{b421ebb7.a60899a2.js => b421ebb7.867062fd.js} (98%) rename assets/js/{b88cb85b.f345a857.js => b88cb85b.d779ea6a.js} (98%) rename assets/js/{c29bedb9.da75fb17.js => c29bedb9.64d8690b.js} (57%) rename assets/js/{c55d205b.6a9b956d.js => c55d205b.8cce9cb6.js} (98%) create mode 100644 assets/js/c573638f.2758b3b4.js delete mode 100644 assets/js/c573638f.8593a47b.js rename assets/js/{caf1b628.3470f566.js => caf1b628.0cf6df3a.js} (98%) create mode 100644 assets/js/cc3bdf2f.5664f42d.js create mode 100644 assets/js/cd68cda1.68c11170.js rename assets/js/{d0e4cdf1.c290633a.js => d0e4cdf1.421afec6.js} (57%) rename assets/js/{d1cef389.6c21f19a.js => d1cef389.a646f88e.js} (57%) rename assets/js/{d2611248.4b920029.js => d2611248.928af5e7.js} (57%) rename assets/js/{d361ad2d.2e82e064.js => d361ad2d.f08f5569.js} (97%) rename assets/js/{d50fd269.00a8a343.js => d50fd269.9ae96e63.js} (57%) rename assets/js/{d86f7a37.b39dc39a.js => d86f7a37.bbe448b1.js} (97%) rename assets/js/{d8cdf5ef.fe2623c5.js => d8cdf5ef.4c076bff.js} (98%) create mode 100644 assets/js/e2326195.ed359df7.js rename assets/js/{e4ebfe18.8b575fa3.js => e4ebfe18.985f0369.js} (57%) rename assets/js/{eec33099.50f7c47e.js => eec33099.8543c571.js} (57%) rename assets/js/{ef5b2427.9c5560c3.js => ef5b2427.51a774af.js} (57%) rename assets/js/{f332d221.a33f27f9.js => f332d221.b5f875cb.js} (57%) rename assets/js/{f3e308ad.77e4f1b1.js => f3e308ad.4ced4843.js} (57%) rename assets/js/{f4f49e13.f75cc00c.js => f4f49e13.a45671c0.js} (57%) rename assets/js/{f75a8651.6bd03765.js => f75a8651.e89d591b.js} (57%) rename assets/js/{f8409a7e.e092ca80.js => f8409a7e.48009fa4.js} (98%) rename assets/js/{fbd57548.5fe6f500.js => fbd57548.7dba7e8f.js} (57%) delete mode 100644 assets/js/main.2e13b410.js create mode 100644 assets/js/main.b4ffb42c.js rename assets/js/{main.2e13b410.js.LICENSE.txt => main.b4ffb42c.js.LICENSE.txt} (100%) create mode 100644 assets/js/runtime~main.52d2c86e.js delete mode 100644 assets/js/runtime~main.55afa7c4.js create mode 100644 async-exception.html create mode 100644 page/46.html create mode 100644 tags/async/page/2.html create mode 100644 tags/exception.html diff --git a/2022-retrospective.html b/2022-retrospective.html index 8dee2a2b9..5994d7318 100644 --- a/2022-retrospective.html +++ b/2022-retrospective.html @@ -13,12 +13,12 @@ - - + +
-
본문으로 건너뛰기

2022년 회고

· 약 4분

적당한 전환점, 2022년을 돌아보며

전역

약 1년 6개월간의 공군 정보보호병 생활을 마치고 전역을 했다.
+

2022년 회고

· 약 4분

적당한 전환점, 2022년을 돌아보며

전역

약 1년 6개월간의 공군 정보보호병 생활을 마치고 전역을 했다.
조기 전역 때문에 2021년 12월에 나왔지만, 실제 전역 날짜는 2022년이니 회고에 적어도 상관없겠지.

조금 더 미래에 대한 생각을 해볼걸 그랬다.
전역을 했지만 뭐 하나 제대로 할 줄 아는 것도 없으니 넓은 바닷속에 덩그러니 놓아진 기분이 괜히 들었었다.
일찍 생각을 정리하여 방향을 잡지 못했기에 아쉬움이 많이 남았다.

자바

전역을 하고 진로를 고민하다 향로님의 자바 공화국 포스팅을 읽고 나서 자바 공부를 시작했다.
@@ -33,7 +33,7 @@ 적지 않은 시간을 투자해 준비를 했고, 감사하게도 이번에는 최종 합격을 했다.

난 사람들과 소통하고, 협업하는 능력이 부족하다고 생각을 많이 했다.
우아한 테크코스를 통해 그 빈 부분을 채우도록 노력해야겠다.

2023년에는

마음의 여유가 없었던 2022년이었던 것 같다.
하고 싶은 건 많지만, 이번에는 여유를 가지고 할 수 있는 것에 최선을 다해야겠다.

- - + + \ No newline at end of file diff --git a/404.html b/404.html index 0f2c0a237..52448ba26 100644 --- a/404.html +++ b/404.html @@ -13,13 +13,13 @@ - - + +

페이지를 찾을 수 없습니다.

원하는 페이지를 찾을 수 없습니다.

사이트 관리자에게 링크가 깨진 것을 알려주세요.

- - + + \ No newline at end of file diff --git a/accidental-duplication.html b/accidental-duplication.html index 848093356..c1cfab741 100644 --- a/accidental-duplication.html +++ b/accidental-duplication.html @@ -13,12 +13,12 @@ - - + +
-

중복과 우발적 중복

· 약 8분

장바구니 미션에서는 상품 추가와 상품 수정에 대한 요구사항이 있었다.
+

중복과 우발적 중복

· 약 8분

장바구니 미션에서는 상품 추가와 상품 수정에 대한 요구사항이 있었다.
요청에 담긴 Body를 통해 전달받은 값을 DTO로 매핑하여 추가와 수정을 했다.

장바구니 미션에서의 상품 추가 및 수정

중복1

클래스명을 제외하고 필드와 검증로직 그 외 모든게 같은 DTO를 보며 중복이라고 생각했다.
하지만 반대로 용도가 다르기 때문에 중복이 아니라고 생각하기도 했다.
위 경우는 중복일까? 중복이 아닐까?

이 부분에 대해서 다음과 같은 리뷰를 받았다.

ProductSaveRequestProductUpdateRequest가 완전히 동일한데, 재사용할 수 없을까? 라는 리뷰를 남겼었어요. 사실 생성과 수정은 서로 달라질 개연성이 높아서 미리 분리해놓는 게 더 좋은 방법이긴 한데, 그래도 중복은 싫어서 저도 요즘 이런저런 방법들을 시도해보는 중 입니다. 허브는 이 부분에 대해 어떤 생각을 가지고 있을지 궁금하네요 ㅎㅎ

질문에 대해 아래와 같이 답변을 했다.

저장과 수정할 때 필요한 필드값이 동일하여 현재 구조에서는 하나로 사용해도 된다고 생각을 하지만, 말씀해주신대로 요구사항이 변경된다면 달라질 가능성이 높다고 판단하였습니다!

중복과 우발적 중복

로버트 마틴님이 집필하신 클린 아키텍처는 아래와 같이 중복을 여러가지 종류로 나누어 설명하고 있다.

  • 진짜 중복: 한 인스턴스가 변경되면, 동일한 변경을 그 인스턴스의 모드 복사본에 반드시 적용해야 한다.
  • 거짓된 중복, 우발적 중복: 중복으로 보이는 두 코드 영역이 각자의 경로로 발전한다면, 즉 서로 다른 속도와 다른 이유로 변경된다면 이 두 코드는 진짜 중복이 아니다.

추가와 수정은 초기에는 중복으로 보이지만 초기 생성시에만 기입하는 데이터들이 추가되거나, 시간이 지나면서 서로 달라질 가능성이 높아진다. @@ -32,7 +32,7 @@ 상황에 맞춰 적재적소에 의존 역전을 이용해보는 것도 좋을 것 같다.

참고 자료

클린 아키텍처 16장 독립성, 로버트 C. 마틴
https://techblog.woowahan.com/2647/
https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/

- - + + \ No newline at end of file diff --git a/assets/images/mdc-not-null-2b12c13f4f420a335c9e55dbea503f1b.png b/assets/images/mdc-not-null-2b12c13f4f420a335c9e55dbea503f1b.png new file mode 100644 index 0000000000000000000000000000000000000000..6588471094a3db8b61ddad01804a468c4d6f1060 GIT binary patch literal 227445 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

2ThD*p_g+FIae#BJH$o?Um#XYw&cw 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ξFi*=_&yC?M&CfhP<7k!;5 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%0DVX^r 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

FGrsNEZUvy4j&RweA8-$dI!bCZN#h;b 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

h0F0EWFW1qE+FwdL*o!JUg=>xwRy{^sWJRH}A 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�lT@8`p+vx$UBh7Hnl@F~i=7#|%Q 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 literal 0 HcmV?d00001 diff --git a/assets/js/00931cc3.e832cb29.js b/assets/js/00931cc3.94b71d28.js similarity index 57% rename from assets/js/00931cc3.e832cb29.js rename to assets/js/00931cc3.94b71d28.js index ec2dee4c1..80145b033 100644 --- a/assets/js/00931cc3.e832cb29.js +++ b/assets/js/00931cc3.94b71d28.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5669],{92291:e=>{e.exports=JSON.parse('{"permalink":"/page/30","page":30,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/29","nextPage":"/page/31","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5669],{92291:e=>{e.exports=JSON.parse('{"permalink":"/page/30","page":30,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/29","nextPage":"/page/31","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/0462f8fc.04b8abb6.js b/assets/js/0462f8fc.d191c72d.js similarity index 98% rename from assets/js/0462f8fc.04b8abb6.js rename to assets/js/0462f8fc.d191c72d.js index 0200b2e90..3fffdf598 100644 --- a/assets/js/0462f8fc.04b8abb6.js +++ b/assets/js/0462f8fc.d191c72d.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[4481],{3905:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>f});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function l(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var p=n.createContext({}),c=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},s=function(e){var t=c(e.components);return n.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,i=e.originalType,p=e.parentName,s=o(e,["components","mdxType","originalType","parentName"]),d=c(r),f=a,m=d["".concat(p,".").concat(f)]||d[f]||u[f]||i;return r?n.createElement(m,l(l({ref:t},s),{},{components:r})):n.createElement(m,l({ref:t},s))}));function f(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=r.length,l=new Array(i);l[0]=d;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o.mdxType="string"==typeof e?e:a,l[1]=o;for(var c=2;c{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>u,frontMatter:()=>i,metadata:()=>o,toc:()=>c});var n=r(87462),a=(r(67294),r(3905));const i={title:"\ud14c\uc2a4\ud2b8 \ucf54\ub4dc\uac00 \uc8fc\ub294 \ud61c\ud0dd",slug:"/test/benefit",tags:["test"]},l=void 0,o={unversionedId:"\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\ucf54\ub4dc\uac00_\uc8fc\ub294_\ud61c\ud0dd",id:"\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\ucf54\ub4dc\uac00_\uc8fc\ub294_\ud61c\ud0dd",title:"\ud14c\uc2a4\ud2b8 \ucf54\ub4dc\uac00 \uc8fc\ub294 \ud61c\ud0dd",description:"\ub514\ubc84\uae45 \uac10\uc18c",source:"@site/docs/\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\ucf54\ub4dc\uac00_\uc8fc\ub294_\ud61c\ud0dd.mdx",sourceDirName:"\ud14c\uc2a4\ud2b8",slug:"/test/benefit",permalink:"/docs/test/benefit",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\ucf54\ub4dc\uac00_\uc8fc\ub294_\ud61c\ud0dd.mdx",tags:[{label:"test",permalink:"/docs/tags/test"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\ud14c\uc2a4\ud2b8 \ucf54\ub4dc\uac00 \uc8fc\ub294 \ud61c\ud0dd",slug:"/test/benefit",tags:["test"]},sidebar:"tutorialSidebar",previous:{title:"TDD heuristics",permalink:"/docs/test/heuristics"}},p={},c=[{value:"\ub514\ubc84\uae45 \uac10\uc18c",id:"\ub514\ubc84\uae45-\uac10\uc18c",level:3},{value:"\uc790\uc2e0 \uc788\uac8c \ubcc0\uacbd",id:"\uc790\uc2e0-\uc788\uac8c-\ubcc0\uacbd",level:3},{value:"\ub354 \ub098\uc740 \ubb38\uc11c \uc790\ub8cc",id:"\ub354-\ub098\uc740-\ubb38\uc11c-\uc790\ub8cc",level:3},{value:"\ub354 \ub2e8\uc21c\ud55c \ub9ac\ubdf0",id:"\ub354-\ub2e8\uc21c\ud55c-\ub9ac\ubdf0",level:3},{value:"\uc0ac\ub824 \uae4a\uc740 \uc124\uacc4",id:"\uc0ac\ub824-\uae4a\uc740-\uc124\uacc4",level:3},{value:"\uace0\ud488\uc9c8\uc758 \ub9b4\ub9ac\uc2a4\ub97c \ube60\ub974\uac8c",id:"\uace0\ud488\uc9c8\uc758-\ub9b4\ub9ac\uc2a4\ub97c-\ube60\ub974\uac8c",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],s={toc:c};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},s,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ub514\ubc84\uae45-\uac10\uc18c"},"\ub514\ubc84\uae45 \uac10\uc18c"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\ub97c \ud55c \ubc88 \uc791\uc131\ud574\ub450\uba74 \ud504\ub85c\uc81d\ud2b8\uc758 \uc0dd\uc874 \uc8fc\uae30\ub3d9\uc548 \uac12\ube44\uc2fc \uacb0\ud568\uc744 \uc608\ubc29\ud574 \uc900\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub610\ud55c \uc804\uccb4\uc801\uc778 \uacb0\ud568\uc774 \ud574\uacb0\ub418\uc5b4 \ub514\ubc84\uae45\uc5d0\uc11c \ud574\ubc29\uc2dc\ucf1c\uc900\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc7a5\uc778 \uc815\uc2e0 \uc774\uc57c\uae30\uc5d0\uc11c \uc5b8\uae09\ub41c ",(0,a.kt)("inlineCode",{parentName:"p"},"TDD heuristics 15. \ub514\ubc84\uac70 \uc0ac\uc6a9\uc744 \ud53c\ud558\ub77c")," \uc640 \uc5f0\uacb0\ub418\ub294 \ub0b4\uc6a9\uc774\ub2e4."),(0,a.kt)("h3",{id:"\uc790\uc2e0-\uc788\uac8c-\ubcc0\uacbd"},"\uc790\uc2e0 \uc788\uac8c \ubcc0\uacbd"),(0,a.kt)("p",null,"\uc18c\ud504\ud2b8\uc6e8\uc5b4\ub294 \ud56d\uc0c1 \ubcc0\uacbd\ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub9ac\ud329\ud130\ub9c1\ud560 \ub54c \uc790\uc2e0\uac10\uc744 \uac00\uc9c0\uace0 \ubcc0\uacbd \uc0ac\ud56d\uc744 \ubc18\uc601\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\ub354-\ub098\uc740-\ubb38\uc11c-\uc790\ub8cc"},"\ub354 \ub098\uc740 \ubb38\uc11c \uc790\ub8cc"),(0,a.kt)("p",null,"\ud558\ub098\uc758 \ud589\uc704\ub9cc \uc9d1\uc911\ud574 \uac80\uc99d\ud558\ub294 \ud14c\uc2a4\ud2b8\ub294 \uc2e4\ud589 \uac00\ub2a5\ud55c \ubb38\uc11c\uc640 \uac19\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc774 \ub54c \ud14c\uc2a4\ud2b8\ub294 \uba85\ud655\ud558\uace0 \uac04\uacb0\ud574\uc57c\uc9c0\ub9cc \ubb38\uc11c \uc790\ub8cc\ub85c\uc11c\uc758 \uc5ed\ud560\uc744 \ud6cc\ub96d\ud788 \uc218\ud589\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\ub354-\ub2e8\uc21c\ud55c-\ub9ac\ubdf0"},"\ub354 \ub2e8\uc21c\ud55c \ub9ac\ubdf0"),(0,a.kt)("p",null,"\uc815\ud655\uc131, \uadf9\ub2e8 \uc0c1\ud669, \uc624\ub958 \uc0c1\ud669 \ub4f1\uc758 \ub2e4\uc591\ud55c \uce21\uba74\uc5d0\uc11c \ucf54\ub4dc\ub97c \uac80\uc0ac\ud574\uc8fc\ub294 \ud14c\uc2a4\ud2b8\uac00 \uc900\ube44\ub418\uc5b4 \uc788\ub2e4\uba74 \ub9ac\ubdf0\uc5b4\uac00 \uac80\uc99d\ud558\ub294 \uc2dc\uac04\uc744 \ud06c\uac8c \uc904\uc5ec\uc900\ub2e4. "),(0,a.kt)("h3",{id:"\uc0ac\ub824-\uae4a\uc740-\uc124\uacc4"},"\uc0ac\ub824 \uae4a\uc740 \uc124\uacc4"),(0,a.kt)("p",null,"\uc0c8\ub85c \uc791\uc131\ud55c \ucf54\ub4dc\uc758 \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub294 \uc77c\uc740 \uc2e4\uc9c8\uc801\uc73c\ub85c \ud574\ub2f9 \ucf54\ub4dc\uc758 API\uac00 \uc798 \uc124\uacc4\ub418\uc5b4 \uc788\ub294\uc9c0\ub97c \uc2dc\ud5d8\ud558\ub294 \ud589\uc704\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud14c\uc2a4\ud2b8\ud558\uae30 \uc5b4\ub824\uc6b4 \ucf54\ub4dc\ub294 \ub108\ubb34 \ub9ce\uc740 \ucc45\uc784\uc744 \uac00\uc9c0\uace0 \uc788\uac70\ub098, \uc758\uc874\uc131\uc774 \ubcf5\uc7a1\ud55c \uacbd\uc6b0\uac00 \ub9ce\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc798 \uc124\uacc4\ub41c \ucf54\ub4dc\ub77c\uba74 \ubaa8\ub4c8\ud654\uac00 \uc798 \ub418\uc5b4\uc788\uc5b4\uc57c \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"\uace0\ud488\uc9c8\uc758-\ub9b4\ub9ac\uc2a4\ub97c-\ube60\ub974\uac8c"},"\uace0\ud488\uc9c8\uc758 \ub9b4\ub9ac\uc2a4\ub97c \ube60\ub974\uac8c"),(0,a.kt)("p",null,"\uc790\ub3d9\ud654\ub41c \ud14c\uc2a4\ud2b8\ub97c \uac16\ucd98\ub2e4\uba74 \uc0c8\ub85c\uc6b4 \ubc84\uc804\uc744 \ub9b4\ub9ac\uc2a4\ud560 \ub54c \ubd88\uc548\uc5d0 \ub5a8\uc9c0 \uc54a\uc544\ub3c4 \ub41c\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uad6c\uae00 \uc5d4\uc9c0\ub2c8\uc5b4\ub294 \uc774\ub807\uac8c \uc77c\ud55c\ub2e4, \ud0c0\uc774\ud130\uc2a4 \uc708\ud130\uc2a4, \ud1b0 \ub9e8\uc26c\ub809, \ud558\uc774\ub7fc \ub77c\uc774\ud2b8 p.288"))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[4481],{3905:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>f});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function l(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var p=n.createContext({}),c=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},s=function(e){var t=c(e.components);return n.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,i=e.originalType,p=e.parentName,s=o(e,["components","mdxType","originalType","parentName"]),d=c(r),f=a,m=d["".concat(p,".").concat(f)]||d[f]||u[f]||i;return r?n.createElement(m,l(l({ref:t},s),{},{components:r})):n.createElement(m,l({ref:t},s))}));function f(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=r.length,l=new Array(i);l[0]=d;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o.mdxType="string"==typeof e?e:a,l[1]=o;for(var c=2;c{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>u,frontMatter:()=>i,metadata:()=>o,toc:()=>c});var n=r(87462),a=(r(67294),r(3905));const i={title:"\ud14c\uc2a4\ud2b8 \ucf54\ub4dc\uac00 \uc8fc\ub294 \ud61c\ud0dd",slug:"/test/benefit",tags:["test"]},l=void 0,o={unversionedId:"\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\ucf54\ub4dc\uac00_\uc8fc\ub294_\ud61c\ud0dd",id:"\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\ucf54\ub4dc\uac00_\uc8fc\ub294_\ud61c\ud0dd",title:"\ud14c\uc2a4\ud2b8 \ucf54\ub4dc\uac00 \uc8fc\ub294 \ud61c\ud0dd",description:"\ub514\ubc84\uae45 \uac10\uc18c",source:"@site/docs/\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\ucf54\ub4dc\uac00_\uc8fc\ub294_\ud61c\ud0dd.mdx",sourceDirName:"\ud14c\uc2a4\ud2b8",slug:"/test/benefit",permalink:"/docs/test/benefit",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\ucf54\ub4dc\uac00_\uc8fc\ub294_\ud61c\ud0dd.mdx",tags:[{label:"test",permalink:"/docs/tags/test"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\ud14c\uc2a4\ud2b8 \ucf54\ub4dc\uac00 \uc8fc\ub294 \ud61c\ud0dd",slug:"/test/benefit",tags:["test"]},sidebar:"tutorialSidebar",previous:{title:"TDD heuristics",permalink:"/docs/test/heuristics"}},p={},c=[{value:"\ub514\ubc84\uae45 \uac10\uc18c",id:"\ub514\ubc84\uae45-\uac10\uc18c",level:3},{value:"\uc790\uc2e0 \uc788\uac8c \ubcc0\uacbd",id:"\uc790\uc2e0-\uc788\uac8c-\ubcc0\uacbd",level:3},{value:"\ub354 \ub098\uc740 \ubb38\uc11c \uc790\ub8cc",id:"\ub354-\ub098\uc740-\ubb38\uc11c-\uc790\ub8cc",level:3},{value:"\ub354 \ub2e8\uc21c\ud55c \ub9ac\ubdf0",id:"\ub354-\ub2e8\uc21c\ud55c-\ub9ac\ubdf0",level:3},{value:"\uc0ac\ub824 \uae4a\uc740 \uc124\uacc4",id:"\uc0ac\ub824-\uae4a\uc740-\uc124\uacc4",level:3},{value:"\uace0\ud488\uc9c8\uc758 \ub9b4\ub9ac\uc2a4\ub97c \ube60\ub974\uac8c",id:"\uace0\ud488\uc9c8\uc758-\ub9b4\ub9ac\uc2a4\ub97c-\ube60\ub974\uac8c",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],s={toc:c};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},s,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ub514\ubc84\uae45-\uac10\uc18c"},"\ub514\ubc84\uae45 \uac10\uc18c"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\ub97c \ud55c \ubc88 \uc791\uc131\ud574\ub450\uba74 \ud504\ub85c\uc81d\ud2b8\uc758 \uc0dd\uc874 \uc8fc\uae30\ub3d9\uc548 \uac12\ube44\uc2fc \uacb0\ud568\uc744 \uc608\ubc29\ud574 \uc900\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub610\ud55c \uc804\uccb4\uc801\uc778 \uacb0\ud568\uc774 \ud574\uacb0\ub418\uc5b4 \ub514\ubc84\uae45\uc5d0\uc11c \ud574\ubc29\uc2dc\ucf1c\uc900\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc7a5\uc778 \uc815\uc2e0 \uc774\uc57c\uae30\uc5d0\uc11c \uc5b8\uae09\ub41c ",(0,a.kt)("inlineCode",{parentName:"p"},"TDD heuristics 15. \ub514\ubc84\uac70 \uc0ac\uc6a9\uc744 \ud53c\ud558\ub77c")," \uc640 \uc5f0\uacb0\ub418\ub294 \ub0b4\uc6a9\uc774\ub2e4."),(0,a.kt)("h3",{id:"\uc790\uc2e0-\uc788\uac8c-\ubcc0\uacbd"},"\uc790\uc2e0 \uc788\uac8c \ubcc0\uacbd"),(0,a.kt)("p",null,"\uc18c\ud504\ud2b8\uc6e8\uc5b4\ub294 \ud56d\uc0c1 \ubcc0\uacbd\ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub9ac\ud329\ud130\ub9c1\ud560 \ub54c \uc790\uc2e0\uac10\uc744 \uac00\uc9c0\uace0 \ubcc0\uacbd \uc0ac\ud56d\uc744 \ubc18\uc601\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\ub354-\ub098\uc740-\ubb38\uc11c-\uc790\ub8cc"},"\ub354 \ub098\uc740 \ubb38\uc11c \uc790\ub8cc"),(0,a.kt)("p",null,"\ud558\ub098\uc758 \ud589\uc704\ub9cc \uc9d1\uc911\ud574 \uac80\uc99d\ud558\ub294 \ud14c\uc2a4\ud2b8\ub294 \uc2e4\ud589 \uac00\ub2a5\ud55c \ubb38\uc11c\uc640 \uac19\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc774 \ub54c \ud14c\uc2a4\ud2b8\ub294 \uba85\ud655\ud558\uace0 \uac04\uacb0\ud574\uc57c\uc9c0\ub9cc \ubb38\uc11c \uc790\ub8cc\ub85c\uc11c\uc758 \uc5ed\ud560\uc744 \ud6cc\ub96d\ud788 \uc218\ud589\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\ub354-\ub2e8\uc21c\ud55c-\ub9ac\ubdf0"},"\ub354 \ub2e8\uc21c\ud55c \ub9ac\ubdf0"),(0,a.kt)("p",null,"\uc815\ud655\uc131, \uadf9\ub2e8 \uc0c1\ud669, \uc624\ub958 \uc0c1\ud669 \ub4f1\uc758 \ub2e4\uc591\ud55c \uce21\uba74\uc5d0\uc11c \ucf54\ub4dc\ub97c \uac80\uc0ac\ud574\uc8fc\ub294 \ud14c\uc2a4\ud2b8\uac00 \uc900\ube44\ub418\uc5b4 \uc788\ub2e4\uba74 \ub9ac\ubdf0\uc5b4\uac00 \uac80\uc99d\ud558\ub294 \uc2dc\uac04\uc744 \ud06c\uac8c \uc904\uc5ec\uc900\ub2e4. "),(0,a.kt)("h3",{id:"\uc0ac\ub824-\uae4a\uc740-\uc124\uacc4"},"\uc0ac\ub824 \uae4a\uc740 \uc124\uacc4"),(0,a.kt)("p",null,"\uc0c8\ub85c \uc791\uc131\ud55c \ucf54\ub4dc\uc758 \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub294 \uc77c\uc740 \uc2e4\uc9c8\uc801\uc73c\ub85c \ud574\ub2f9 \ucf54\ub4dc\uc758 API\uac00 \uc798 \uc124\uacc4\ub418\uc5b4 \uc788\ub294\uc9c0\ub97c \uc2dc\ud5d8\ud558\ub294 \ud589\uc704\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud14c\uc2a4\ud2b8\ud558\uae30 \uc5b4\ub824\uc6b4 \ucf54\ub4dc\ub294 \ub108\ubb34 \ub9ce\uc740 \ucc45\uc784\uc744 \uac00\uc9c0\uace0 \uc788\uac70\ub098, \uc758\uc874\uc131\uc774 \ubcf5\uc7a1\ud55c \uacbd\uc6b0\uac00 \ub9ce\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc798 \uc124\uacc4\ub41c \ucf54\ub4dc\ub77c\uba74 \ubaa8\ub4c8\ud654\uac00 \uc798 \ub418\uc5b4\uc788\uc5b4\uc57c \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"\uace0\ud488\uc9c8\uc758-\ub9b4\ub9ac\uc2a4\ub97c-\ube60\ub974\uac8c"},"\uace0\ud488\uc9c8\uc758 \ub9b4\ub9ac\uc2a4\ub97c \ube60\ub974\uac8c"),(0,a.kt)("p",null,"\uc790\ub3d9\ud654\ub41c \ud14c\uc2a4\ud2b8\ub97c \uac16\ucd98\ub2e4\uba74 \uc0c8\ub85c\uc6b4 \ubc84\uc804\uc744 \ub9b4\ub9ac\uc2a4\ud560 \ub54c \ubd88\uc548\uc5d0 \ub5a8\uc9c0 \uc54a\uc544\ub3c4 \ub41c\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uad6c\uae00 \uc5d4\uc9c0\ub2c8\uc5b4\ub294 \uc774\ub807\uac8c \uc77c\ud55c\ub2e4, \ud0c0\uc774\ud130\uc2a4 \uc708\ud130\uc2a4, \ud1b0 \ub9e8\uc26c\ub809, \ud558\uc774\ub7fc \ub77c\uc774\ud2b8 p.288"))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/04644f5f.2031d661.js b/assets/js/04644f5f.b526cda7.js similarity index 98% rename from assets/js/04644f5f.2031d661.js rename to assets/js/04644f5f.b526cda7.js index d9a927e94..b530d2f5c 100644 --- a/assets/js/04644f5f.2031d661.js +++ b/assets/js/04644f5f.b526cda7.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2625],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),p=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(c.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},s=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,c=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),s=p(n),d=a,f=s["".concat(c,".").concat(d)]||s[d]||m[d]||l;return n?r.createElement(f,i(i({ref:t},u),{},{components:n})):r.createElement(f,i({ref:t},u))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,i=new Array(l);i[0]=s;var o={};for(var c in t)hasOwnProperty.call(t,c)&&(o[c]=t[c]);o.originalType=e,o.mdxType="string"==typeof e?e:a,i[1]=o;for(var p=2;p{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>m,frontMatter:()=>l,metadata:()=>o,toc:()=>p});var r=n(87462),a=(n(67294),n(3905));const l={title:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",slug:"/etc/communication",tags:["etc"]},i=void 0,o={unversionedId:"\uae30\ud0c0/\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",id:"\uae30\ud0c0/\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",title:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",description:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798 \ud558\ub294 \uac1c\ubc1c\uc790?",source:"@site/docs/\uae30\ud0c0/\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00.mdx",sourceDirName:"\uae30\ud0c0",slug:"/etc/communication",permalink:"/docs/etc/communication",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uae30\ud0c0/\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00.mdx",tags:[{label:"etc",permalink:"/docs/tags/etc"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",slug:"/etc/communication",tags:["etc"]},sidebar:"tutorialSidebar",previous:{title:"\uacbd\ud5d8\uacfc \uc9c8\ubb38",permalink:"/docs/etc/experience-and-self-question"},next:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",permalink:"/docs/network/load-balancing-algorithm"}},c={},p=[{value:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798 \ud558\ub294 \uac1c\ubc1c\uc790?",id:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158-\uc798-\ud558\ub294-\uac1c\ubc1c\uc790",level:3},{value:"\ubcc0\ud654\ub97c \ub9cc\ub4e4\ub824\uba74 \uc2b5\uad00\uc774 \ud544\uc694\ud558\ub2e4.",id:"\ubcc0\ud654\ub97c-\ub9cc\ub4e4\ub824\uba74-\uc2b5\uad00\uc774-\ud544\uc694\ud558\ub2e4",level:3},{value:"\uc88b\uc740 \uc2b5\uad00 4\uac00\uc9c0",id:"\uc88b\uc740-\uc2b5\uad00-4\uac00\uc9c0",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:p};function m(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158-\uc798-\ud558\ub294-\uac1c\ubc1c\uc790"},"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798 \ud558\ub294 \uac1c\ubc1c\uc790?"),(0,a.kt)("p",null,"\uc2a4\ud399 \uad6c\ud604\ud615 \uac1c\ubc1c\uc790 vs \ubb38\uc81c \ud574\uacb0\ud615 \uac1c\ubc1c\uc790 "),(0,a.kt)("p",null,"\uac1c\ubc1c\uc790 != \uc2a4\ud399\uc744 \uc8fc\uba74 \uc798 \uad6c\ud604\ud558\ub294 \uc0ac\ub78c",(0,a.kt)("br",{parentName:"p"}),"\n","\uad6c\ud604\uc5d0 \uc9d1\uc911\ud55c\ub2e4\uba74 \uc77c\uc758 \uc2dc\uc57c\uac00 \uc881\uc544\uc9c4\ub2e4. "),(0,a.kt)("p",null,"\uc758\ub3c4\uc640 \ub9e5\ub77d\uc744 \uc774\ud574\ud574\uc11c, \ub354 \uc88b\uc740 \uc2a4\ud399\uc744 \ub9cc\ub4e4\uc5b4\ub0b4\ub824\uace0 \ud558\ub294 \uac1c\ubc1c\uc790\uac00 \ub418\uc5b4\uc57c \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"\ubcc0\ud654\ub97c-\ub9cc\ub4e4\ub824\uba74-\uc2b5\uad00\uc774-\ud544\uc694\ud558\ub2e4"},"\ubcc0\ud654\ub97c \ub9cc\ub4e4\ub824\uba74 \uc2b5\uad00\uc774 \ud544\uc694\ud558\ub2e4."),(0,a.kt)("p",null,"\uc8fc\ub2c8\uc5b4 \uac1c\ubc1c\uc790\ub294 \uc2a4\ud399 \uad6c\ud604 \uac1c\ubc1c\uc790\uc5d0\uc11c \uc2dc\uc791\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ubcc0\ud654\ub97c \uc704\ud574 \uc2e4\uc81c\ub85c \ud589\ub3d9\ud558\ub294 ",(0,a.kt)("inlineCode",{parentName:"p"},"\uc2b5\uad00"),"\uc774 \ud544\uc694\ud558\ub2e4. "),(0,a.kt)("h3",{id:"\uc88b\uc740-\uc2b5\uad00-4\uac00\uc9c0"},"\uc88b\uc740 \uc2b5\uad00 4\uac00\uc9c0"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"\ud574\uacb0\ud558\ub824\ub294 ",(0,a.kt)("inlineCode",{parentName:"li"},"\ubb38\uc81c"),"\uc640 ",(0,a.kt)("inlineCode",{parentName:"li"},"\uc758\ub3c4/\uc0c1\ud669"),"\uc5d0 \ub300\ud574 \ubb3b\ub294\ub2e4. "),(0,a.kt)("li",{parentName:"ol"},"\uc0c1\ub300\ubc29\uc758 \ub9d0\uc744 \ub4e3\uace0 ",(0,a.kt)("inlineCode",{parentName:"li"},"\ub0b4\uac00")," \uc774\ud574\ud55c \ubc14\ub97c \uc694\uc57d\ud558\uc5ec \uacf5\uc720\ud55c\ub2e4. ",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"\ud55c \ubc88 \uc815\ub9ac\ud55c\ub2e4\uba74 1... 2... \uc774\ub807\uac8c \uc774\ud574\ud588\ub294\ub370 \ub9de\uc744\uae4c\uc694?"))),(0,a.kt)("li",{parentName:"ol"},"\uc548 \ub41c\ub2e4\uace0 \ub9d0\ud560 \ub54c\ub294 \uc0c1\ub300\ubc29\uc5d0 \uad00\uc810\uc5d0\uc11c \ub300\uc548\uc744 \uc81c\uc2dc\ud55c\ub2e4. ",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"\uc81c\uc57d\uc744 \ub35c \ubc1b\ub294 \ub2e4\ub978 \ubc29\ud5a5\uc131/\ub300\uc548 \uc81c\uc2dc"))),(0,a.kt)("li",{parentName:"ol"},"\ubb38\uc81c\ub97c \ud574\uacb0\ud560 \ub610 \ub2e4\ub978 \ubc29\ubc95\uc740 \uc5c6\uc744\uc9c0 \uace0\ubbfc\ud574\ubcf8\ub2e4. ",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"\ub2e8\uc815\ub300\uc2e0 \ud55c \ubc88 \ub354 \uc9c8\ubb38\ud558\uace0 \uc0dd\uac01")))),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00 - \uc1a1\ubc94\uadfc, INFCON 2023"))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2625],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),p=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(c.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},s=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,c=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),s=p(n),d=a,f=s["".concat(c,".").concat(d)]||s[d]||m[d]||l;return n?r.createElement(f,i(i({ref:t},u),{},{components:n})):r.createElement(f,i({ref:t},u))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,i=new Array(l);i[0]=s;var o={};for(var c in t)hasOwnProperty.call(t,c)&&(o[c]=t[c]);o.originalType=e,o.mdxType="string"==typeof e?e:a,i[1]=o;for(var p=2;p{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>m,frontMatter:()=>l,metadata:()=>o,toc:()=>p});var r=n(87462),a=(n(67294),n(3905));const l={title:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",slug:"/etc/communication",tags:["etc"]},i=void 0,o={unversionedId:"\uae30\ud0c0/\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",id:"\uae30\ud0c0/\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",title:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",description:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798 \ud558\ub294 \uac1c\ubc1c\uc790?",source:"@site/docs/\uae30\ud0c0/\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00.mdx",sourceDirName:"\uae30\ud0c0",slug:"/etc/communication",permalink:"/docs/etc/communication",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uae30\ud0c0/\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00.mdx",tags:[{label:"etc",permalink:"/docs/tags/etc"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",slug:"/etc/communication",tags:["etc"]},sidebar:"tutorialSidebar",previous:{title:"\uacbd\ud5d8\uacfc \uc9c8\ubb38",permalink:"/docs/etc/experience-and-self-question"},next:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",permalink:"/docs/network/load-balancing-algorithm"}},c={},p=[{value:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798 \ud558\ub294 \uac1c\ubc1c\uc790?",id:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158-\uc798-\ud558\ub294-\uac1c\ubc1c\uc790",level:3},{value:"\ubcc0\ud654\ub97c \ub9cc\ub4e4\ub824\uba74 \uc2b5\uad00\uc774 \ud544\uc694\ud558\ub2e4.",id:"\ubcc0\ud654\ub97c-\ub9cc\ub4e4\ub824\uba74-\uc2b5\uad00\uc774-\ud544\uc694\ud558\ub2e4",level:3},{value:"\uc88b\uc740 \uc2b5\uad00 4\uac00\uc9c0",id:"\uc88b\uc740-\uc2b5\uad00-4\uac00\uc9c0",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:p};function m(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158-\uc798-\ud558\ub294-\uac1c\ubc1c\uc790"},"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798 \ud558\ub294 \uac1c\ubc1c\uc790?"),(0,a.kt)("p",null,"\uc2a4\ud399 \uad6c\ud604\ud615 \uac1c\ubc1c\uc790 vs \ubb38\uc81c \ud574\uacb0\ud615 \uac1c\ubc1c\uc790 "),(0,a.kt)("p",null,"\uac1c\ubc1c\uc790 != \uc2a4\ud399\uc744 \uc8fc\uba74 \uc798 \uad6c\ud604\ud558\ub294 \uc0ac\ub78c",(0,a.kt)("br",{parentName:"p"}),"\n","\uad6c\ud604\uc5d0 \uc9d1\uc911\ud55c\ub2e4\uba74 \uc77c\uc758 \uc2dc\uc57c\uac00 \uc881\uc544\uc9c4\ub2e4. "),(0,a.kt)("p",null,"\uc758\ub3c4\uc640 \ub9e5\ub77d\uc744 \uc774\ud574\ud574\uc11c, \ub354 \uc88b\uc740 \uc2a4\ud399\uc744 \ub9cc\ub4e4\uc5b4\ub0b4\ub824\uace0 \ud558\ub294 \uac1c\ubc1c\uc790\uac00 \ub418\uc5b4\uc57c \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"\ubcc0\ud654\ub97c-\ub9cc\ub4e4\ub824\uba74-\uc2b5\uad00\uc774-\ud544\uc694\ud558\ub2e4"},"\ubcc0\ud654\ub97c \ub9cc\ub4e4\ub824\uba74 \uc2b5\uad00\uc774 \ud544\uc694\ud558\ub2e4."),(0,a.kt)("p",null,"\uc8fc\ub2c8\uc5b4 \uac1c\ubc1c\uc790\ub294 \uc2a4\ud399 \uad6c\ud604 \uac1c\ubc1c\uc790\uc5d0\uc11c \uc2dc\uc791\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ubcc0\ud654\ub97c \uc704\ud574 \uc2e4\uc81c\ub85c \ud589\ub3d9\ud558\ub294 ",(0,a.kt)("inlineCode",{parentName:"p"},"\uc2b5\uad00"),"\uc774 \ud544\uc694\ud558\ub2e4. "),(0,a.kt)("h3",{id:"\uc88b\uc740-\uc2b5\uad00-4\uac00\uc9c0"},"\uc88b\uc740 \uc2b5\uad00 4\uac00\uc9c0"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"\ud574\uacb0\ud558\ub824\ub294 ",(0,a.kt)("inlineCode",{parentName:"li"},"\ubb38\uc81c"),"\uc640 ",(0,a.kt)("inlineCode",{parentName:"li"},"\uc758\ub3c4/\uc0c1\ud669"),"\uc5d0 \ub300\ud574 \ubb3b\ub294\ub2e4. "),(0,a.kt)("li",{parentName:"ol"},"\uc0c1\ub300\ubc29\uc758 \ub9d0\uc744 \ub4e3\uace0 ",(0,a.kt)("inlineCode",{parentName:"li"},"\ub0b4\uac00")," \uc774\ud574\ud55c \ubc14\ub97c \uc694\uc57d\ud558\uc5ec \uacf5\uc720\ud55c\ub2e4. ",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"\ud55c \ubc88 \uc815\ub9ac\ud55c\ub2e4\uba74 1... 2... \uc774\ub807\uac8c \uc774\ud574\ud588\ub294\ub370 \ub9de\uc744\uae4c\uc694?"))),(0,a.kt)("li",{parentName:"ol"},"\uc548 \ub41c\ub2e4\uace0 \ub9d0\ud560 \ub54c\ub294 \uc0c1\ub300\ubc29\uc5d0 \uad00\uc810\uc5d0\uc11c \ub300\uc548\uc744 \uc81c\uc2dc\ud55c\ub2e4. ",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"\uc81c\uc57d\uc744 \ub35c \ubc1b\ub294 \ub2e4\ub978 \ubc29\ud5a5\uc131/\ub300\uc548 \uc81c\uc2dc"))),(0,a.kt)("li",{parentName:"ol"},"\ubb38\uc81c\ub97c \ud574\uacb0\ud560 \ub610 \ub2e4\ub978 \ubc29\ubc95\uc740 \uc5c6\uc744\uc9c0 \uace0\ubbfc\ud574\ubcf8\ub2e4. ",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"\ub2e8\uc815\ub300\uc2e0 \ud55c \ubc88 \ub354 \uc9c8\ubb38\ud558\uace0 \uc0dd\uac01")))),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00 - \uc1a1\ubc94\uadfc, INFCON 2023"))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/09fbb6bd.dcff2b7f.js b/assets/js/09fbb6bd.454abeb1.js similarity index 57% rename from assets/js/09fbb6bd.dcff2b7f.js rename to assets/js/09fbb6bd.454abeb1.js index e4bf90f6e..4c5b7d569 100644 --- a/assets/js/09fbb6bd.dcff2b7f.js +++ b/assets/js/09fbb6bd.454abeb1.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5964],{41679:e=>{e.exports=JSON.parse('{"permalink":"/page/16","page":16,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/15","nextPage":"/page/17","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5964],{41679:e=>{e.exports=JSON.parse('{"permalink":"/page/16","page":16,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/15","nextPage":"/page/17","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/0c071de2.13b9004c.js b/assets/js/0c071de2.96df65c8.js similarity index 56% rename from assets/js/0c071de2.13b9004c.js rename to assets/js/0c071de2.96df65c8.js index 2ba1e8fb2..bef7ef2a9 100644 --- a/assets/js/0c071de2.13b9004c.js +++ b/assets/js/0c071de2.96df65c8.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[321],{23125:e=>{e.exports=JSON.parse('{"permalink":"/page/2","page":2,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/","nextPage":"/page/3","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[321],{23125:e=>{e.exports=JSON.parse('{"permalink":"/page/2","page":2,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/","nextPage":"/page/3","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/0d47646f.abc0fc85.js b/assets/js/0d47646f.04fe4885.js similarity index 98% rename from assets/js/0d47646f.abc0fc85.js rename to assets/js/0d47646f.04fe4885.js index b82053682..b877436cf 100644 --- a/assets/js/0d47646f.abc0fc85.js +++ b/assets/js/0d47646f.04fe4885.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2342],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>d});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),c=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(p.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=c(r),d=o,f=m["".concat(p,".").concat(d)]||m[d]||s[d]||i;return r?n.createElement(f,a(a({ref:t},u),{},{components:r})):n.createElement(f,a({ref:t},u))}));function d(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:o,a[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>s,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var n=r(87462),o=(r(67294),r(3905));const i={title:"\ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131",slug:"/monitoring/intro",tags:["monitoring"]},a=void 0,l={unversionedId:"\ubaa8\ub2c8\ud130\ub9c1/\ubaa8\ub2c8\ud130\ub9c1_\ud658\uacbd_\uad6c\uc131",id:"\ubaa8\ub2c8\ud130\ub9c1/\ubaa8\ub2c8\ud130\ub9c1_\ud658\uacbd_\uad6c\uc131",title:"\ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131",description:"\ubaa8\ub2c8\ud130\ub9c1 3\ub2e8\uacc4",source:"@site/docs/\ubaa8\ub2c8\ud130\ub9c1/\ubaa8\ub2c8\ud130\ub9c1_\ud658\uacbd_\uad6c\uc131.mdx",sourceDirName:"\ubaa8\ub2c8\ud130\ub9c1",slug:"/monitoring/intro",permalink:"/docs/monitoring/intro",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ubaa8\ub2c8\ud130\ub9c1/\ubaa8\ub2c8\ud130\ub9c1_\ud658\uacbd_\uad6c\uc131.mdx",tags:[{label:"monitoring",permalink:"/docs/tags/monitoring"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131",slug:"/monitoring/intro",tags:["monitoring"]},sidebar:"tutorialSidebar",previous:{title:"\ud130\ubbf8\ub110 \uc258 \ud504\ub86c\ud504\ud2b8 \uc124\uc815",permalink:"/docs/linux/shell"},next:{title:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c",permalink:"/docs/culture/postmortem"}},p={},c=[{value:"\ubaa8\ub2c8\ud130\ub9c1 3\ub2e8\uacc4",id:"\ubaa8\ub2c8\ud130\ub9c1-3\ub2e8\uacc4",level:3},{value:"\ubaa8\ub2c8\ud130\ub9c1 \ub300\uc0c1",id:"\ubaa8\ub2c8\ud130\ub9c1-\ub300\uc0c1",level:3},{value:"\uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ucd94\uc801",id:"\uc560\ud50c\ub9ac\ucf00\uc774\uc158-\ucd94\uc801",level:3},{value:"\ub85c\uadf8",id:"\ub85c\uadf8",level:3},{value:"\ubaa8\ub2c8\ud130\ub9c1",id:"\ubaa8\ub2c8\ud130\ub9c1",level:3},{value:"\uc54c\ub78c",id:"\uc54c\ub78c",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:c};function s(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h3",{id:"\ubaa8\ub2c8\ud130\ub9c1-3\ub2e8\uacc4"},"\ubaa8\ub2c8\ud130\ub9c1 3\ub2e8\uacc4"),(0,o.kt)("p",null,"\ub300\uc2dc\ubcf4\ub4dc",(0,o.kt)("br",{parentName:"p"}),"\n","\uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ucd94\uc801 - \ud540\ud3ec\uc778\ud2b8",(0,o.kt)("br",{parentName:"p"}),"\n","\ub85c\uadf8 "),(0,o.kt)("h3",{id:"\ubaa8\ub2c8\ud130\ub9c1-\ub300\uc0c1"},"\ubaa8\ub2c8\ud130\ub9c1 \ub300\uc0c1"),(0,o.kt)("p",null,"\uc2dc\uc2a4\ud15c \uba54\ud2b8\ub9ad(CPU, \uba54\ubaa8\ub9ac)",(0,o.kt)("br",{parentName:"p"}),"\n","\uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uba54\ud2b8\ub9ad(Thread Pool, Connection Pool, \ud638\ucd9c \uc218)",(0,o.kt)("br",{parentName:"p"}),"\n","\ube44\uc988\ub2c8\uc2a4 \uba54\ud2b8\ub9ad"),(0,o.kt)("h3",{id:"\uc560\ud50c\ub9ac\ucf00\uc774\uc158-\ucd94\uc801"},"\uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ucd94\uc801"),(0,o.kt)("p",null,"\uac01\uac01\uc758 HTTP \uc694\uccad \ucd94\uc801",(0,o.kt)("br",{parentName:"p"}),"\n","\ud540\ud3ec\uc778\ud2b8, \uc2a4\uce74\uc6b0\ud2b8, \uc640\ud0ed, \uc81c\ub2c8\ud37c "),(0,o.kt)("h3",{id:"\ub85c\uadf8"},"\ub85c\uadf8"),(0,o.kt)("p",null,"\uac00\uc7a5 \uc138\uc138\ud55c \ucd94\uc801",(0,o.kt)("br",{parentName:"p"}),"\n","\uac19\uc740 HTTP \uc694\uccad\uc744 \ubb36\uc5b4\uc11c \ud655\uc778\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc774 \uc911\uc694\ud558\ub2e4.",(0,o.kt)("br",{parentName:"p"}),"\n","MDC(Mapped Diagnostic Context) \uc801\uc6a9"),(0,o.kt)("p",null,"\ud30c\uc77c\ub85c \uc9c1\uc811 \ub85c\uadf8\ub97c \ub0a8\uae30\ub294 \uacbd\uc6b0 \u2192 \uc77c\ubc18 \ub85c\uadf8\uc640 \uc5d0\ub7ec \ub85c\uadf8 \ud30c\uc77c\uc744 \uad6c\ubd84\ud574\uc11c \ub0a8\uaca8\uc57c \ud55c\ub2e4.",(0,o.kt)("br",{parentName:"p"}),"\n","\ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc800\uc7a5\ud558\ub294 \uacbd\uc6b0 \u2192 \uac80\uc0c9\uc774 \uc798 \ub418\ub3c4\ub85d \uad6c\ubd84\ud55c\ub2e4. "),(0,o.kt)("h3",{id:"\ubaa8\ub2c8\ud130\ub9c1"},"\ubaa8\ub2c8\ud130\ub9c1"),(0,o.kt)("p",null,"\uad00\ucc30\uc758 \uacbd\uc6b0 \uc804\uccb4 \u2192 \uc881\uac8c"),(0,o.kt)("h3",{id:"\uc54c\ub78c"},"\uc54c\ub78c"),(0,o.kt)("p",null,"\uc54c\ub78c\uc758 \uacbd\uc6b0 2\uac00\uc9c0 \uc885\ub958(\uacbd\uace0, \uc2ec\uac01)\ub85c \uad6c\ubd84\ud574\uc11c \uad00\ub9ac\ud55c\ub2e4."),(0,o.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,o.kt)("p",null,"\uc2a4\ud504\ub9c1 \ubd80\ud2b8 \ud575\uc2ec \uc6d0\ub9ac\uc640 \ud65c\uc6a9, \uae40\uc601\ud55c"))}s.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2342],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>d});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),c=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(p.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=c(r),d=o,f=m["".concat(p,".").concat(d)]||m[d]||s[d]||i;return r?n.createElement(f,a(a({ref:t},u),{},{components:r})):n.createElement(f,a({ref:t},u))}));function d(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:o,a[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>s,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var n=r(87462),o=(r(67294),r(3905));const i={title:"\ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131",slug:"/monitoring/intro",tags:["monitoring"]},a=void 0,l={unversionedId:"\ubaa8\ub2c8\ud130\ub9c1/\ubaa8\ub2c8\ud130\ub9c1_\ud658\uacbd_\uad6c\uc131",id:"\ubaa8\ub2c8\ud130\ub9c1/\ubaa8\ub2c8\ud130\ub9c1_\ud658\uacbd_\uad6c\uc131",title:"\ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131",description:"\ubaa8\ub2c8\ud130\ub9c1 3\ub2e8\uacc4",source:"@site/docs/\ubaa8\ub2c8\ud130\ub9c1/\ubaa8\ub2c8\ud130\ub9c1_\ud658\uacbd_\uad6c\uc131.mdx",sourceDirName:"\ubaa8\ub2c8\ud130\ub9c1",slug:"/monitoring/intro",permalink:"/docs/monitoring/intro",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ubaa8\ub2c8\ud130\ub9c1/\ubaa8\ub2c8\ud130\ub9c1_\ud658\uacbd_\uad6c\uc131.mdx",tags:[{label:"monitoring",permalink:"/docs/tags/monitoring"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131",slug:"/monitoring/intro",tags:["monitoring"]},sidebar:"tutorialSidebar",previous:{title:"\ud130\ubbf8\ub110 \uc258 \ud504\ub86c\ud504\ud2b8 \uc124\uc815",permalink:"/docs/linux/shell"},next:{title:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c",permalink:"/docs/culture/postmortem"}},p={},c=[{value:"\ubaa8\ub2c8\ud130\ub9c1 3\ub2e8\uacc4",id:"\ubaa8\ub2c8\ud130\ub9c1-3\ub2e8\uacc4",level:3},{value:"\ubaa8\ub2c8\ud130\ub9c1 \ub300\uc0c1",id:"\ubaa8\ub2c8\ud130\ub9c1-\ub300\uc0c1",level:3},{value:"\uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ucd94\uc801",id:"\uc560\ud50c\ub9ac\ucf00\uc774\uc158-\ucd94\uc801",level:3},{value:"\ub85c\uadf8",id:"\ub85c\uadf8",level:3},{value:"\ubaa8\ub2c8\ud130\ub9c1",id:"\ubaa8\ub2c8\ud130\ub9c1",level:3},{value:"\uc54c\ub78c",id:"\uc54c\ub78c",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:c};function s(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h3",{id:"\ubaa8\ub2c8\ud130\ub9c1-3\ub2e8\uacc4"},"\ubaa8\ub2c8\ud130\ub9c1 3\ub2e8\uacc4"),(0,o.kt)("p",null,"\ub300\uc2dc\ubcf4\ub4dc",(0,o.kt)("br",{parentName:"p"}),"\n","\uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ucd94\uc801 - \ud540\ud3ec\uc778\ud2b8",(0,o.kt)("br",{parentName:"p"}),"\n","\ub85c\uadf8 "),(0,o.kt)("h3",{id:"\ubaa8\ub2c8\ud130\ub9c1-\ub300\uc0c1"},"\ubaa8\ub2c8\ud130\ub9c1 \ub300\uc0c1"),(0,o.kt)("p",null,"\uc2dc\uc2a4\ud15c \uba54\ud2b8\ub9ad(CPU, \uba54\ubaa8\ub9ac)",(0,o.kt)("br",{parentName:"p"}),"\n","\uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uba54\ud2b8\ub9ad(Thread Pool, Connection Pool, \ud638\ucd9c \uc218)",(0,o.kt)("br",{parentName:"p"}),"\n","\ube44\uc988\ub2c8\uc2a4 \uba54\ud2b8\ub9ad"),(0,o.kt)("h3",{id:"\uc560\ud50c\ub9ac\ucf00\uc774\uc158-\ucd94\uc801"},"\uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ucd94\uc801"),(0,o.kt)("p",null,"\uac01\uac01\uc758 HTTP \uc694\uccad \ucd94\uc801",(0,o.kt)("br",{parentName:"p"}),"\n","\ud540\ud3ec\uc778\ud2b8, \uc2a4\uce74\uc6b0\ud2b8, \uc640\ud0ed, \uc81c\ub2c8\ud37c "),(0,o.kt)("h3",{id:"\ub85c\uadf8"},"\ub85c\uadf8"),(0,o.kt)("p",null,"\uac00\uc7a5 \uc138\uc138\ud55c \ucd94\uc801",(0,o.kt)("br",{parentName:"p"}),"\n","\uac19\uc740 HTTP \uc694\uccad\uc744 \ubb36\uc5b4\uc11c \ud655\uc778\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc774 \uc911\uc694\ud558\ub2e4.",(0,o.kt)("br",{parentName:"p"}),"\n","MDC(Mapped Diagnostic Context) \uc801\uc6a9"),(0,o.kt)("p",null,"\ud30c\uc77c\ub85c \uc9c1\uc811 \ub85c\uadf8\ub97c \ub0a8\uae30\ub294 \uacbd\uc6b0 \u2192 \uc77c\ubc18 \ub85c\uadf8\uc640 \uc5d0\ub7ec \ub85c\uadf8 \ud30c\uc77c\uc744 \uad6c\ubd84\ud574\uc11c \ub0a8\uaca8\uc57c \ud55c\ub2e4.",(0,o.kt)("br",{parentName:"p"}),"\n","\ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc800\uc7a5\ud558\ub294 \uacbd\uc6b0 \u2192 \uac80\uc0c9\uc774 \uc798 \ub418\ub3c4\ub85d \uad6c\ubd84\ud55c\ub2e4. "),(0,o.kt)("h3",{id:"\ubaa8\ub2c8\ud130\ub9c1"},"\ubaa8\ub2c8\ud130\ub9c1"),(0,o.kt)("p",null,"\uad00\ucc30\uc758 \uacbd\uc6b0 \uc804\uccb4 \u2192 \uc881\uac8c"),(0,o.kt)("h3",{id:"\uc54c\ub78c"},"\uc54c\ub78c"),(0,o.kt)("p",null,"\uc54c\ub78c\uc758 \uacbd\uc6b0 2\uac00\uc9c0 \uc885\ub958(\uacbd\uace0, \uc2ec\uac01)\ub85c \uad6c\ubd84\ud574\uc11c \uad00\ub9ac\ud55c\ub2e4."),(0,o.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,o.kt)("p",null,"\uc2a4\ud504\ub9c1 \ubd80\ud2b8 \ud575\uc2ec \uc6d0\ub9ac\uc640 \ud65c\uc6a9, \uae40\uc601\ud55c"))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/101cf32b.c05aa69b.js b/assets/js/101cf32b.c05aa69b.js deleted file mode 100644 index 57cfdda80..000000000 --- a/assets/js/101cf32b.c05aa69b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9239],{30880:e=>{e.exports=JSON.parse('{"permalink":"/page/45","page":45,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/44","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/101cf32b.cc00cd31.js b/assets/js/101cf32b.cc00cd31.js new file mode 100644 index 000000000..5066f58b3 --- /dev/null +++ b/assets/js/101cf32b.cc00cd31.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9239],{30880:e=>{e.exports=JSON.parse('{"permalink":"/page/45","page":45,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/44","nextPage":"/page/46","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/12cbeba7.55ec34b6.js b/assets/js/12cbeba7.0896b1b8.js similarity index 57% rename from assets/js/12cbeba7.55ec34b6.js rename to assets/js/12cbeba7.0896b1b8.js index 99a9e875d..8f74bb588 100644 --- a/assets/js/12cbeba7.55ec34b6.js +++ b/assets/js/12cbeba7.0896b1b8.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6508],{16134:e=>{e.exports=JSON.parse('{"permalink":"/page/29","page":29,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/28","nextPage":"/page/30","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6508],{16134:e=>{e.exports=JSON.parse('{"permalink":"/page/29","page":29,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/28","nextPage":"/page/30","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/1bb997fc.034c8bb9.js b/assets/js/1bb997fc.cb9e0f6d.js similarity index 57% rename from assets/js/1bb997fc.034c8bb9.js rename to assets/js/1bb997fc.cb9e0f6d.js index 693894711..4d168e5f5 100644 --- a/assets/js/1bb997fc.034c8bb9.js +++ b/assets/js/1bb997fc.cb9e0f6d.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9713],{66014:e=>{e.exports=JSON.parse('{"permalink":"/page/44","page":44,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/43","nextPage":"/page/45","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9713],{66014:e=>{e.exports=JSON.parse('{"permalink":"/page/44","page":44,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/43","nextPage":"/page/45","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/226700de.c27d727c.js b/assets/js/226700de.4a36650b.js similarity index 57% rename from assets/js/226700de.c27d727c.js rename to assets/js/226700de.4a36650b.js index 01586b48a..4e43bf748 100644 --- a/assets/js/226700de.c27d727c.js +++ b/assets/js/226700de.4a36650b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6035],{41961:e=>{e.exports=JSON.parse('{"permalink":"/page/25","page":25,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/24","nextPage":"/page/26","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6035],{41961:e=>{e.exports=JSON.parse('{"permalink":"/page/25","page":25,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/24","nextPage":"/page/26","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/270346fa.b38c6387.js b/assets/js/270346fa.f3b605a3.js similarity index 57% rename from assets/js/270346fa.b38c6387.js rename to assets/js/270346fa.f3b605a3.js index 00846a01f..7d550aab5 100644 --- a/assets/js/270346fa.b38c6387.js +++ b/assets/js/270346fa.f3b605a3.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7975],{89424:e=>{e.exports=JSON.parse('{"permalink":"/page/28","page":28,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/27","nextPage":"/page/29","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7975],{89424:e=>{e.exports=JSON.parse('{"permalink":"/page/28","page":28,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/27","nextPage":"/page/29","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/2832e534.f340f2ea.js b/assets/js/2832e534.9b8cccfb.js similarity index 57% rename from assets/js/2832e534.f340f2ea.js rename to assets/js/2832e534.9b8cccfb.js index 90a450837..60153dbc7 100644 --- a/assets/js/2832e534.f340f2ea.js +++ b/assets/js/2832e534.9b8cccfb.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2476],{69870:e=>{e.exports=JSON.parse('{"permalink":"/page/13","page":13,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/12","nextPage":"/page/14","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2476],{69870:e=>{e.exports=JSON.parse('{"permalink":"/page/13","page":13,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/12","nextPage":"/page/14","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/2e10a69c.4e824af5.js b/assets/js/2e10a69c.9fb45902.js similarity index 57% rename from assets/js/2e10a69c.4e824af5.js rename to assets/js/2e10a69c.9fb45902.js index 7f9ea66bb..c2acab06f 100644 --- a/assets/js/2e10a69c.4e824af5.js +++ b/assets/js/2e10a69c.9fb45902.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7581],{9981:e=>{e.exports=JSON.parse('{"permalink":"/page/38","page":38,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/37","nextPage":"/page/39","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7581],{9981:e=>{e.exports=JSON.parse('{"permalink":"/page/38","page":38,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/37","nextPage":"/page/39","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/302370be.a84b65cc.js b/assets/js/302370be.bdf8f944.js similarity index 98% rename from assets/js/302370be.a84b65cc.js rename to assets/js/302370be.bdf8f944.js index a823d5121..036fabf1f 100644 --- a/assets/js/302370be.a84b65cc.js +++ b/assets/js/302370be.bdf8f944.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[1883],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>f});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var p=n.createContext({}),s=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},c=function(e){var t=s(e.components);return n.createElement(p.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,l=e.originalType,p=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),u=s(r),f=a,m=u["".concat(p,".").concat(f)]||u[f]||d[f]||l;return r?n.createElement(m,i(i({ref:t},c),{},{components:r})):n.createElement(m,i({ref:t},c))}));function f(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=r.length,i=new Array(l);i[0]=u;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o.mdxType="string"==typeof e?e:a,i[1]=o;for(var s=2;s{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>d,frontMatter:()=>l,metadata:()=>o,toc:()=>s});var n=r(87462),a=(r(67294),r(3905));const l={title:"FIRST",slug:"/test/first",tags:["test"]},i=void 0,o={unversionedId:"\ud14c\uc2a4\ud2b8/FIRST",id:"\ud14c\uc2a4\ud2b8/FIRST",title:"FIRST",description:"FIRST\ub780?",source:"@site/docs/\ud14c\uc2a4\ud2b8/FIRST.mdx",sourceDirName:"\ud14c\uc2a4\ud2b8",slug:"/test/first",permalink:"/docs/test/first",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ud14c\uc2a4\ud2b8/FIRST.mdx",tags:[{label:"test",permalink:"/docs/tags/test"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"FIRST",slug:"/test/first",tags:["test"]},sidebar:"tutorialSidebar",previous:{title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc720\ud615",permalink:"/docs/performance/types"},next:{title:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8",permalink:"/docs/test/stairstep"}},p={},s=[{value:"FIRST\ub780?",id:"first\ub780",level:3},{value:"Fast(\ube60\ub974\uac8c)",id:"fast\ube60\ub974\uac8c",level:3},{value:"Independent(\ub3c5\ub9bd\uc801\uc73c\ub85c)",id:"independent\ub3c5\ub9bd\uc801\uc73c\ub85c",level:3},{value:"Repeatable(\ubc18\ubcf5 \uac00\ub2a5\ud55c)",id:"repeatable\ubc18\ubcf5-\uac00\ub2a5\ud55c",level:3},{value:"Self-Validating(\uc790\uac00 \uac80\uc99d\ud558\ub294)",id:"self-validating\uc790\uac00-\uac80\uc99d\ud558\ub294",level:3},{value:"Timely(\uc801\uc2dc\uc5d0)",id:"timely\uc801\uc2dc\uc5d0",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:2}],c={toc:s};function d(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"first\ub780"},"FIRST\ub780?"),(0,a.kt)("p",null,"Fast, Independent, Repeatable, Self-Validating, Timely\uc758 \uc57d\uc790\ub85c \uc88b\uc740 \ub2e8\uc704 \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub294\ub370 \ub3c4\uc6c0\uc774 \ub418\ub294 \uc6d0\uce59\ub4e4\uc774\ub2e4."),(0,a.kt)("h3",{id:"fast\ube60\ub974\uac8c"},"Fast(\ube60\ub974\uac8c)"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\uac00 \ube60\ub974\uac8c \ub3cc\uc544\uac00\uc9c0 \uc54a\uc73c\uba74, \ud14c\uc2a4\ud2b8\ub97c \ub9e4 \uc21c\uac04 \uc2e4\ud589\ud558\uc9c0 \uc54a\uc744 \uac83\uc774\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud14c\uc2a4\ud2b8\ub97c \uc790\uc8fc \uc2e4\ud589\ud558\uc9c0 \uc54a\uc73c\uba74, \ubc84\uadf8\ub97c \ub193\uce60 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub530\ub77c\uc11c \ube60\ub974\uac8c \ud14c\uc2a4\ud2b8\ub97c \ub3cc\ub824 \ud53c\ub4dc\ubc31 \ubc1b\ub294 \uc8fc\uae30\ub97c \uc9e7\uac8c \ub9cc\ub4dc\ub294 \uac83\uc774 \uc88b\ub2e4. "),(0,a.kt)("h3",{id:"independent\ub3c5\ub9bd\uc801\uc73c\ub85c"},"Independent(\ub3c5\ub9bd\uc801\uc73c\ub85c)"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\ub07c\ub9ac \uc11c\ub85c \uc758\uc874\ud558\uba74 \uc548\ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc11c\ub85c \uc758\uc874\ud558\uac8c \ub41c\ub2e4\uba74 \ud558\ub098\uc758 \ud14c\uc2a4\ud2b8\uac00 \uc2e4\ud328\ud560 \ub54c, \ub610 \ub2e4\ub978 \ud558\ub098\uc758 \ud14c\uc2a4\ud2b8\uac00 \uc2e4\ud328\ud560 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e4\ub978 \ud14c\uc2a4\ud2b8\uc5d0 \uc758\uc874\ud558\uc9c0 \uc54a\uace0, \ub3c5\ub9bd\uc801\uc73c\ub85c \uc2e4\ud589 \uac00\ub2a5\ud55c \ud14c\uc2a4\ud2b8\uac00 \uc88b\uc740 \ud14c\uc2a4\ud2b8\ub2e4. "),(0,a.kt)("h3",{id:"repeatable\ubc18\ubcf5-\uac00\ub2a5\ud55c"},"Repeatable(\ubc18\ubcf5 \uac00\ub2a5\ud55c)"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\ub294 \uc5b4\ub5a0\ud55c \ud658\uacbd\uc5d0\uc11c\ub77c\ub3c4 \ubc18\ubcf5 \uac00\ub2a5\ud574\uc57c \ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub610\ud55c \ubc18\ubcf5\uc801\uc73c\ub85c \uc218\ud589\ud574\ub3c4 \uac19\uc740 \uacb0\uacfc\ub97c \ub0bc \uc218 \uc788\uc5b4\uc57c \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"self-validating\uc790\uac00-\uac80\uc99d\ud558\ub294"},"Self-Validating(\uc790\uac00 \uac80\uc99d\ud558\ub294)"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\ub294 \uc131\uacf5 \ub610\ub294 \uc2e4\ud328\ub85c bool \uac12\uc73c\ub85c \uacb0\uacfc\ub97c \ub0b4\uc5b4 \uc790\uccb4\uc801\uc73c\ub85c \uac80\uc99d\ud574\uc57c \ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","JUnit\uacfc \uac19\uc740 \uc790\ub3d9\ud654\ub41c \ud14c\uc2a4\ud2b8 \ub3c4\uad6c\ub97c \uc0ac\uc6a9\ud574\uc57c \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"timely\uc801\uc2dc\uc5d0"},"Timely(\uc801\uc2dc\uc5d0)"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\ub294 \uc801\uc2dc\uc5d0 \uc791\uc131\ud574\uc57c \ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e8\uc704 \ud14c\uc2a4\ud2b8\ub294 \ud14c\uc2a4\ud2b8 \ud558\ub824\ub294 \uc2e4\uc81c \ucf54\ub4dc\ub97c \uad6c\ud604\ud558\uae30 \uc9c1\uc804\uc5d0 \uad6c\ud604\ud574\uc57c \ud55c\ub2e4.(TDD)",(0,a.kt)("br",{parentName:"p"}),"\n","\uc2e4\uc81c \ucf54\ub4dc\ub97c \uad6c\ud604\ud55c \ub2e4\uc74c\uc5d0 \ud14c\uc2a4\ud2b8 \ucf54\ub4dc\ub97c \uc791\uc131\ud55c\ub2e4\uba74, \ud14c\uc2a4\ud2b8 \ud558\uae30 \uc5b4\ub824\uc6b4 \ucf54\ub4dc\ub97c \uc791\uc131\ud588\ub2e4\ub294 \uac83\uc744 \ub4a4\ub2a6\uac8c \ubc1c\uacac\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h2",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://tddmanifesto.com/a-clean-test/"},"A clean test, TDD Manifasto"),(0,a.kt)("br",{parentName:"p"}),"\n","Clean Code 9\uc7a5 \ub2e8\uc704\ud14c\uc2a4\ud2b8, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4"))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[1883],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>f});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var p=n.createContext({}),s=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},c=function(e){var t=s(e.components);return n.createElement(p.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,l=e.originalType,p=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),u=s(r),f=a,m=u["".concat(p,".").concat(f)]||u[f]||d[f]||l;return r?n.createElement(m,i(i({ref:t},c),{},{components:r})):n.createElement(m,i({ref:t},c))}));function f(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=r.length,i=new Array(l);i[0]=u;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o.mdxType="string"==typeof e?e:a,i[1]=o;for(var s=2;s{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>d,frontMatter:()=>l,metadata:()=>o,toc:()=>s});var n=r(87462),a=(r(67294),r(3905));const l={title:"FIRST",slug:"/test/first",tags:["test"]},i=void 0,o={unversionedId:"\ud14c\uc2a4\ud2b8/FIRST",id:"\ud14c\uc2a4\ud2b8/FIRST",title:"FIRST",description:"FIRST\ub780?",source:"@site/docs/\ud14c\uc2a4\ud2b8/FIRST.mdx",sourceDirName:"\ud14c\uc2a4\ud2b8",slug:"/test/first",permalink:"/docs/test/first",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ud14c\uc2a4\ud2b8/FIRST.mdx",tags:[{label:"test",permalink:"/docs/tags/test"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"FIRST",slug:"/test/first",tags:["test"]},sidebar:"tutorialSidebar",previous:{title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc720\ud615",permalink:"/docs/performance/types"},next:{title:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8",permalink:"/docs/test/stairstep"}},p={},s=[{value:"FIRST\ub780?",id:"first\ub780",level:3},{value:"Fast(\ube60\ub974\uac8c)",id:"fast\ube60\ub974\uac8c",level:3},{value:"Independent(\ub3c5\ub9bd\uc801\uc73c\ub85c)",id:"independent\ub3c5\ub9bd\uc801\uc73c\ub85c",level:3},{value:"Repeatable(\ubc18\ubcf5 \uac00\ub2a5\ud55c)",id:"repeatable\ubc18\ubcf5-\uac00\ub2a5\ud55c",level:3},{value:"Self-Validating(\uc790\uac00 \uac80\uc99d\ud558\ub294)",id:"self-validating\uc790\uac00-\uac80\uc99d\ud558\ub294",level:3},{value:"Timely(\uc801\uc2dc\uc5d0)",id:"timely\uc801\uc2dc\uc5d0",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:2}],c={toc:s};function d(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"first\ub780"},"FIRST\ub780?"),(0,a.kt)("p",null,"Fast, Independent, Repeatable, Self-Validating, Timely\uc758 \uc57d\uc790\ub85c \uc88b\uc740 \ub2e8\uc704 \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub294\ub370 \ub3c4\uc6c0\uc774 \ub418\ub294 \uc6d0\uce59\ub4e4\uc774\ub2e4."),(0,a.kt)("h3",{id:"fast\ube60\ub974\uac8c"},"Fast(\ube60\ub974\uac8c)"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\uac00 \ube60\ub974\uac8c \ub3cc\uc544\uac00\uc9c0 \uc54a\uc73c\uba74, \ud14c\uc2a4\ud2b8\ub97c \ub9e4 \uc21c\uac04 \uc2e4\ud589\ud558\uc9c0 \uc54a\uc744 \uac83\uc774\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud14c\uc2a4\ud2b8\ub97c \uc790\uc8fc \uc2e4\ud589\ud558\uc9c0 \uc54a\uc73c\uba74, \ubc84\uadf8\ub97c \ub193\uce60 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub530\ub77c\uc11c \ube60\ub974\uac8c \ud14c\uc2a4\ud2b8\ub97c \ub3cc\ub824 \ud53c\ub4dc\ubc31 \ubc1b\ub294 \uc8fc\uae30\ub97c \uc9e7\uac8c \ub9cc\ub4dc\ub294 \uac83\uc774 \uc88b\ub2e4. "),(0,a.kt)("h3",{id:"independent\ub3c5\ub9bd\uc801\uc73c\ub85c"},"Independent(\ub3c5\ub9bd\uc801\uc73c\ub85c)"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\ub07c\ub9ac \uc11c\ub85c \uc758\uc874\ud558\uba74 \uc548\ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc11c\ub85c \uc758\uc874\ud558\uac8c \ub41c\ub2e4\uba74 \ud558\ub098\uc758 \ud14c\uc2a4\ud2b8\uac00 \uc2e4\ud328\ud560 \ub54c, \ub610 \ub2e4\ub978 \ud558\ub098\uc758 \ud14c\uc2a4\ud2b8\uac00 \uc2e4\ud328\ud560 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e4\ub978 \ud14c\uc2a4\ud2b8\uc5d0 \uc758\uc874\ud558\uc9c0 \uc54a\uace0, \ub3c5\ub9bd\uc801\uc73c\ub85c \uc2e4\ud589 \uac00\ub2a5\ud55c \ud14c\uc2a4\ud2b8\uac00 \uc88b\uc740 \ud14c\uc2a4\ud2b8\ub2e4. "),(0,a.kt)("h3",{id:"repeatable\ubc18\ubcf5-\uac00\ub2a5\ud55c"},"Repeatable(\ubc18\ubcf5 \uac00\ub2a5\ud55c)"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\ub294 \uc5b4\ub5a0\ud55c \ud658\uacbd\uc5d0\uc11c\ub77c\ub3c4 \ubc18\ubcf5 \uac00\ub2a5\ud574\uc57c \ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub610\ud55c \ubc18\ubcf5\uc801\uc73c\ub85c \uc218\ud589\ud574\ub3c4 \uac19\uc740 \uacb0\uacfc\ub97c \ub0bc \uc218 \uc788\uc5b4\uc57c \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"self-validating\uc790\uac00-\uac80\uc99d\ud558\ub294"},"Self-Validating(\uc790\uac00 \uac80\uc99d\ud558\ub294)"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\ub294 \uc131\uacf5 \ub610\ub294 \uc2e4\ud328\ub85c bool \uac12\uc73c\ub85c \uacb0\uacfc\ub97c \ub0b4\uc5b4 \uc790\uccb4\uc801\uc73c\ub85c \uac80\uc99d\ud574\uc57c \ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","JUnit\uacfc \uac19\uc740 \uc790\ub3d9\ud654\ub41c \ud14c\uc2a4\ud2b8 \ub3c4\uad6c\ub97c \uc0ac\uc6a9\ud574\uc57c \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"timely\uc801\uc2dc\uc5d0"},"Timely(\uc801\uc2dc\uc5d0)"),(0,a.kt)("p",null,"\ud14c\uc2a4\ud2b8\ub294 \uc801\uc2dc\uc5d0 \uc791\uc131\ud574\uc57c \ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e8\uc704 \ud14c\uc2a4\ud2b8\ub294 \ud14c\uc2a4\ud2b8 \ud558\ub824\ub294 \uc2e4\uc81c \ucf54\ub4dc\ub97c \uad6c\ud604\ud558\uae30 \uc9c1\uc804\uc5d0 \uad6c\ud604\ud574\uc57c \ud55c\ub2e4.(TDD)",(0,a.kt)("br",{parentName:"p"}),"\n","\uc2e4\uc81c \ucf54\ub4dc\ub97c \uad6c\ud604\ud55c \ub2e4\uc74c\uc5d0 \ud14c\uc2a4\ud2b8 \ucf54\ub4dc\ub97c \uc791\uc131\ud55c\ub2e4\uba74, \ud14c\uc2a4\ud2b8 \ud558\uae30 \uc5b4\ub824\uc6b4 \ucf54\ub4dc\ub97c \uc791\uc131\ud588\ub2e4\ub294 \uac83\uc744 \ub4a4\ub2a6\uac8c \ubc1c\uacac\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h2",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://tddmanifesto.com/a-clean-test/"},"A clean test, TDD Manifasto"),(0,a.kt)("br",{parentName:"p"}),"\n","Clean Code 9\uc7a5 \ub2e8\uc704\ud14c\uc2a4\ud2b8, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4"))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/32b2299c.6deb4095.js b/assets/js/32b2299c.56edc850.js similarity index 57% rename from assets/js/32b2299c.6deb4095.js rename to assets/js/32b2299c.56edc850.js index c413fffa0..79c7bc238 100644 --- a/assets/js/32b2299c.6deb4095.js +++ b/assets/js/32b2299c.56edc850.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[970],{5280:e=>{e.exports=JSON.parse('{"permalink":"/page/41","page":41,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/40","nextPage":"/page/42","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[970],{5280:e=>{e.exports=JSON.parse('{"permalink":"/page/41","page":41,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/40","nextPage":"/page/42","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/35293ec4.06faed4f.js b/assets/js/35293ec4.fd6496e4.js similarity index 57% rename from assets/js/35293ec4.06faed4f.js rename to assets/js/35293ec4.fd6496e4.js index 0ca66ad8e..bc68401c6 100644 --- a/assets/js/35293ec4.06faed4f.js +++ b/assets/js/35293ec4.fd6496e4.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7697],{14:e=>{e.exports=JSON.parse('{"permalink":"/page/20","page":20,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/19","nextPage":"/page/21","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7697],{14:e=>{e.exports=JSON.parse('{"permalink":"/page/20","page":20,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/19","nextPage":"/page/21","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/366ddb85.a0bcd890.js b/assets/js/366ddb85.905cf70f.js similarity index 99% rename from assets/js/366ddb85.a0bcd890.js rename to assets/js/366ddb85.905cf70f.js index 77dd22ff5..46533878e 100644 --- a/assets/js/366ddb85.a0bcd890.js +++ b/assets/js/366ddb85.905cf70f.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3691],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>m});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function p(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var i=n.createContext({}),c=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):p(p({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(i.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,i=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=c(r),m=a,f=d["".concat(i,".").concat(m)]||d[m]||s[m]||o;return r?n.createElement(f,p(p({ref:t},u),{},{components:r})):n.createElement(f,p({ref:t},u))}));function m(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,p=new Array(o);p[0]=d;var l={};for(var i in t)hasOwnProperty.call(t,i)&&(l[i]=t[i]);l.originalType=e,l.mdxType="string"==typeof e?e:a,p[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>i,contentTitle:()=>p,default:()=>s,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var n=r(87462),a=(r(67294),r(3905));const o={title:"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",slug:"/etc/develop-with-spring",tags:["etc"]},p=void 0,l={unversionedId:"\uae30\ud0c0/\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",id:"\uae30\ud0c0/\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",title:"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",description:"\uc5b4\ub5a4 \uae30\uc220\uc744 \ud1b5\ud574\uc11c\ub3c4 \uc131\uc7a5\ud560 \uc218 \uc788\ub2e4.",source:"@site/docs/\uae30\ud0c0/\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30.mdx",sourceDirName:"\uae30\ud0c0",slug:"/etc/develop-with-spring",permalink:"/docs/etc/develop-with-spring",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uae30\ud0c0/\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30.mdx",tags:[{label:"etc",permalink:"/docs/tags/etc"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",slug:"/etc/develop-with-spring",tags:["etc"]},sidebar:"tutorialSidebar",previous:{title:"\uac74\uac15\ud558\uac8c \ub098\uc544\uc9c0\uae30",permalink:"/docs/etc/healthful-growth"},next:{title:"\uacbd\ud5d8\uacfc \uc9c8\ubb38",permalink:"/docs/etc/experience-and-self-question"}},i={},c=[{value:"\uc2e4\uc804 \uc0ac\uc6a9",id:"\uc2e4\uc804-\uc0ac\uc6a9",level:3},{value:"\uc9c8\ubb38\uacfc \ud0d0\uad6c",id:"\uc9c8\ubb38\uacfc-\ud0d0\uad6c",level:3},{value:"\ud6c8\ub828\uacfc \uac1c\uc120",id:"\ud6c8\ub828\uacfc-\uac1c\uc120",level:3},{value:"\uacf5\uc720\uc640 \ub17c\uc7c1",id:"\uacf5\uc720\uc640-\ub17c\uc7c1",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:c};function s(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"\uc5b4\ub5a4 \uae30\uc220\uc744 \ud1b5\ud574\uc11c\ub3c4 \uc131\uc7a5\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\uc2e4\uc804-\uc0ac\uc6a9"},"\uc2e4\uc804 \uc0ac\uc6a9"),(0,a.kt)("p",null,"\ub2e8\uc21c \uae30\uc220\uc758 \uc0ac\uc6a9\uc73c\ub85c\ub294 \uc131\uc7a5\uc774 \uc5b4\ub835\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc2e4\uc804 \uc801\uc6a9\uc73c\ub85c \uc131\uc7a5\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\uc9c8\ubb38\uacfc-\ud0d0\uad6c"},"\uc9c8\ubb38\uacfc \ud0d0\uad6c"),(0,a.kt)("p",null,"\ub0b4\uac00 \uc0ac\uc6a9\ud558\ub294 \uae30\uc220\uc740 \uc65c \uc774\ub807\uac8c \ub9cc\ub4e4\uc5b4\uc84c\ub294\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\ub294 \uc65c \uc774\ub807\uac8c \uc124\uacc4\ud558\uace0, \ucf54\ub4dc\ub97c \uc791\uc131\ud574\uc57c \ud558\ub294\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\uc7a5\ub2e8\uc810\uc740 \ubb34\uc5c7\uc778\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e4\ub978 \ub300\uc548\uc740 \uc5c6\uc744\uae4c?",(0,a.kt)("br",{parentName:"p"}),"\n","\uc9c0\uae08\ub3c4 \uc88b\uae34\ud55c\ub370 \ub2e4\uc74c\uc5d0\ub294 \uac1c\uc120\ud560 \ubd80\ubd84\uc774 \uc788\uc744\uae4c? "),(0,a.kt)("h3",{id:"\ud6c8\ub828\uacfc-\uac1c\uc120"},"\ud6c8\ub828\uacfc \uac1c\uc120"),(0,a.kt)("p",null,"\ubc30\uc6b4 \uac83\uc744 \uc751\uc6a9\ud574\ubcf4\ub294 \ucf54\ub529\uc744 \uafb8\uc900\ud788",(0,a.kt)("br",{parentName:"p"}),"\n","\ud29c\ud1a0\ub9ac\uc5bc \uc608\uc81c\ub97c \ubc18\ubcf5\ud574\uc11c \uc791\uc131",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0c8\ub85c\uc6b4 \uae30\ub2a5\uc744 \ucd94\uac00\ud558\uace0 \uc124\uacc4 \uad6c\uc870\ub97c \ubcc0\uacbd\ud574\ubcf4\ub294 \uc2dc\ub3c4",(0,a.kt)("br",{parentName:"p"}),"\n","\uc2e4\ubb34\uc5d0\uc11c \uc0ac\uc6a9\ud558\uc9c0 \ubabb\ud588\ub358 \uae30\uc220 \ub3c4\uc785",(0,a.kt)("br",{parentName:"p"}),"\n","\uc5f0\uc2b5\uc6a9 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uad6c\uc0c1\ud558\uace0 \uc124\uacc4",(0,a.kt)("br",{parentName:"p"}),"\n","\ucd08\uae30 \uac1c\ubc1c \uc0dd\uc0b0\uc131, \ubcc0\uacbd \uc6a9\uc774\uc131 \ub4f1\uc744 \uad00\ucc30 "),(0,a.kt)("h3",{id:"\uacf5\uc720\uc640-\ub17c\uc7c1"},"\uacf5\uc720\uc640 \ub17c\uc7c1"),(0,a.kt)("p",null,"\ubb38\uc11c, \ubc1c\ud45c \uc790\ub8cc\ub85c \uc815\ub9ac",(0,a.kt)("br",{parentName:"p"}),"\n","\uc815\ub9ac\uc5d0 \uc2dc\uac04\uc744 \ub9ce\uc774 \ub4e4\uc774\uba74 \ud6a8\uc728\uc131\uc774 \ub5a8\uc5b4\uc9c0\uae30\uc5d0 \uc911\uc694\ud55c \ubd80\ubd84\ub9cc \uc815\ub9ac",(0,a.kt)("br",{parentName:"p"}),"\n","\uac04\ub2e8\ud55c \uc791\uc131, \uac80\uc0c9\uc774 \uac00\ub2a5\ud55c \ub3c4\uad6c \ud65c\uc6a9",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\ub9cc\uc758 \uc815\uc758\uc640 \uc124\uba85\uc744 \ub9cc\ub4e4\uc5b4\uac00\uae30 -> \ud55c \ubb38\uc7a5, \ud55c \ubb38\ub2e8, 5\ubd84\uac04 \uc124\uba85, \uc810\uc810 \ub298\ub824\uac00\uba70"),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30 - \ud1a0\ube44, INFCON 2023"))}s.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3691],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>m});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function p(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var i=n.createContext({}),c=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):p(p({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(i.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,i=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=c(r),m=a,f=d["".concat(i,".").concat(m)]||d[m]||s[m]||o;return r?n.createElement(f,p(p({ref:t},u),{},{components:r})):n.createElement(f,p({ref:t},u))}));function m(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,p=new Array(o);p[0]=d;var l={};for(var i in t)hasOwnProperty.call(t,i)&&(l[i]=t[i]);l.originalType=e,l.mdxType="string"==typeof e?e:a,p[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>i,contentTitle:()=>p,default:()=>s,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var n=r(87462),a=(r(67294),r(3905));const o={title:"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",slug:"/etc/develop-with-spring",tags:["etc"]},p=void 0,l={unversionedId:"\uae30\ud0c0/\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",id:"\uae30\ud0c0/\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",title:"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",description:"\uc5b4\ub5a4 \uae30\uc220\uc744 \ud1b5\ud574\uc11c\ub3c4 \uc131\uc7a5\ud560 \uc218 \uc788\ub2e4.",source:"@site/docs/\uae30\ud0c0/\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30.mdx",sourceDirName:"\uae30\ud0c0",slug:"/etc/develop-with-spring",permalink:"/docs/etc/develop-with-spring",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uae30\ud0c0/\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30.mdx",tags:[{label:"etc",permalink:"/docs/tags/etc"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",slug:"/etc/develop-with-spring",tags:["etc"]},sidebar:"tutorialSidebar",previous:{title:"\uac74\uac15\ud558\uac8c \ub098\uc544\uc9c0\uae30",permalink:"/docs/etc/healthful-growth"},next:{title:"\uacbd\ud5d8\uacfc \uc9c8\ubb38",permalink:"/docs/etc/experience-and-self-question"}},i={},c=[{value:"\uc2e4\uc804 \uc0ac\uc6a9",id:"\uc2e4\uc804-\uc0ac\uc6a9",level:3},{value:"\uc9c8\ubb38\uacfc \ud0d0\uad6c",id:"\uc9c8\ubb38\uacfc-\ud0d0\uad6c",level:3},{value:"\ud6c8\ub828\uacfc \uac1c\uc120",id:"\ud6c8\ub828\uacfc-\uac1c\uc120",level:3},{value:"\uacf5\uc720\uc640 \ub17c\uc7c1",id:"\uacf5\uc720\uc640-\ub17c\uc7c1",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:c};function s(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"\uc5b4\ub5a4 \uae30\uc220\uc744 \ud1b5\ud574\uc11c\ub3c4 \uc131\uc7a5\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\uc2e4\uc804-\uc0ac\uc6a9"},"\uc2e4\uc804 \uc0ac\uc6a9"),(0,a.kt)("p",null,"\ub2e8\uc21c \uae30\uc220\uc758 \uc0ac\uc6a9\uc73c\ub85c\ub294 \uc131\uc7a5\uc774 \uc5b4\ub835\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc2e4\uc804 \uc801\uc6a9\uc73c\ub85c \uc131\uc7a5\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\uc9c8\ubb38\uacfc-\ud0d0\uad6c"},"\uc9c8\ubb38\uacfc \ud0d0\uad6c"),(0,a.kt)("p",null,"\ub0b4\uac00 \uc0ac\uc6a9\ud558\ub294 \uae30\uc220\uc740 \uc65c \uc774\ub807\uac8c \ub9cc\ub4e4\uc5b4\uc84c\ub294\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\ub294 \uc65c \uc774\ub807\uac8c \uc124\uacc4\ud558\uace0, \ucf54\ub4dc\ub97c \uc791\uc131\ud574\uc57c \ud558\ub294\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\uc7a5\ub2e8\uc810\uc740 \ubb34\uc5c7\uc778\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e4\ub978 \ub300\uc548\uc740 \uc5c6\uc744\uae4c?",(0,a.kt)("br",{parentName:"p"}),"\n","\uc9c0\uae08\ub3c4 \uc88b\uae34\ud55c\ub370 \ub2e4\uc74c\uc5d0\ub294 \uac1c\uc120\ud560 \ubd80\ubd84\uc774 \uc788\uc744\uae4c? "),(0,a.kt)("h3",{id:"\ud6c8\ub828\uacfc-\uac1c\uc120"},"\ud6c8\ub828\uacfc \uac1c\uc120"),(0,a.kt)("p",null,"\ubc30\uc6b4 \uac83\uc744 \uc751\uc6a9\ud574\ubcf4\ub294 \ucf54\ub529\uc744 \uafb8\uc900\ud788",(0,a.kt)("br",{parentName:"p"}),"\n","\ud29c\ud1a0\ub9ac\uc5bc \uc608\uc81c\ub97c \ubc18\ubcf5\ud574\uc11c \uc791\uc131",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0c8\ub85c\uc6b4 \uae30\ub2a5\uc744 \ucd94\uac00\ud558\uace0 \uc124\uacc4 \uad6c\uc870\ub97c \ubcc0\uacbd\ud574\ubcf4\ub294 \uc2dc\ub3c4",(0,a.kt)("br",{parentName:"p"}),"\n","\uc2e4\ubb34\uc5d0\uc11c \uc0ac\uc6a9\ud558\uc9c0 \ubabb\ud588\ub358 \uae30\uc220 \ub3c4\uc785",(0,a.kt)("br",{parentName:"p"}),"\n","\uc5f0\uc2b5\uc6a9 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uad6c\uc0c1\ud558\uace0 \uc124\uacc4",(0,a.kt)("br",{parentName:"p"}),"\n","\ucd08\uae30 \uac1c\ubc1c \uc0dd\uc0b0\uc131, \ubcc0\uacbd \uc6a9\uc774\uc131 \ub4f1\uc744 \uad00\ucc30 "),(0,a.kt)("h3",{id:"\uacf5\uc720\uc640-\ub17c\uc7c1"},"\uacf5\uc720\uc640 \ub17c\uc7c1"),(0,a.kt)("p",null,"\ubb38\uc11c, \ubc1c\ud45c \uc790\ub8cc\ub85c \uc815\ub9ac",(0,a.kt)("br",{parentName:"p"}),"\n","\uc815\ub9ac\uc5d0 \uc2dc\uac04\uc744 \ub9ce\uc774 \ub4e4\uc774\uba74 \ud6a8\uc728\uc131\uc774 \ub5a8\uc5b4\uc9c0\uae30\uc5d0 \uc911\uc694\ud55c \ubd80\ubd84\ub9cc \uc815\ub9ac",(0,a.kt)("br",{parentName:"p"}),"\n","\uac04\ub2e8\ud55c \uc791\uc131, \uac80\uc0c9\uc774 \uac00\ub2a5\ud55c \ub3c4\uad6c \ud65c\uc6a9",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\ub9cc\uc758 \uc815\uc758\uc640 \uc124\uba85\uc744 \ub9cc\ub4e4\uc5b4\uac00\uae30 -> \ud55c \ubb38\uc7a5, \ud55c \ubb38\ub2e8, 5\ubd84\uac04 \uc124\uba85, \uc810\uc810 \ub298\ub824\uac00\uba70"),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30 - \ud1a0\ube44, INFCON 2023"))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/38d8699e.c4935117.js b/assets/js/38d8699e.5a2f1220.js similarity index 57% rename from assets/js/38d8699e.c4935117.js rename to assets/js/38d8699e.5a2f1220.js index 0aaf201c8..a31135b44 100644 --- a/assets/js/38d8699e.c4935117.js +++ b/assets/js/38d8699e.5a2f1220.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[471],{97481:e=>{e.exports=JSON.parse('{"permalink":"/page/15","page":15,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/14","nextPage":"/page/16","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[471],{97481:e=>{e.exports=JSON.parse('{"permalink":"/page/15","page":15,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/14","nextPage":"/page/16","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/3b0d95bc.c9f19c2d.js b/assets/js/3b0d95bc.bbfd78b6.js similarity index 98% rename from assets/js/3b0d95bc.c9f19c2d.js rename to assets/js/3b0d95bc.bbfd78b6.js index a959c3672..fda192d87 100644 --- a/assets/js/3b0d95bc.c9f19c2d.js +++ b/assets/js/3b0d95bc.bbfd78b6.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5140],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),u=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=u(e.components);return r.createElement(p.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,p=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),d=u(n),m=a,E=d["".concat(p,".").concat(m)]||d[m]||s[m]||l;return n?r.createElement(E,i(i({ref:t},c),{},{components:n})):r.createElement(E,i({ref:t},c))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,i=new Array(l);i[0]=d;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o.mdxType="string"==typeof e?e:a,i[1]=o;for(var u=2;u{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>s,frontMatter:()=>l,metadata:()=>o,toc:()=>u});var r=n(87462),a=(n(67294),n(3905));const l={title:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",slug:"/jpa/key",tags:["JPA"]},i=void 0,o={unversionedId:"JPA/\uae30\ubcf8_\ud0a4_\ub9e4\ud551",id:"JPA/\uae30\ubcf8_\ud0a4_\ub9e4\ud551",title:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",description:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",source:"@site/docs/JPA/\uae30\ubcf8_\ud0a4_\ub9e4\ud551.mdx",sourceDirName:"JPA",slug:"/jpa/key",permalink:"/docs/jpa/key",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/JPA/\uae30\ubcf8_\ud0a4_\ub9e4\ud551.mdx",tags:[{label:"JPA",permalink:"/docs/tags/jpa"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",slug:"/jpa/key",tags:["JPA"]},sidebar:"tutorialSidebar",next:{title:"\uad6c\uc870 \ubc0f \uba85\ub839\uc5b4",permalink:"/docs/nginx/command"}},p={},u=[{value:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",id:"\uae30\ubcf8-\ud0a4-\ub9e4\ud551",level:3},{value:"\uc9c1\uc811 \ud560\ub2f9",id:"\uc9c1\uc811-\ud560\ub2f9",level:3},{value:"IDENTITY",id:"identity",level:3},{value:"SEQUENCE",id:"sequence",level:3},{value:"TABLE",id:"table",level:3},{value:"AUTO",id:"auto",level:3},{value:"UUID",id:"uuid",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],c={toc:u};function s(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\uae30\ubcf8-\ud0a4-\ub9e4\ud551"},"\uae30\ubcf8 \ud0a4 \ub9e4\ud551"),(0,a.kt)("p",null,"\uae30\ubcf8 \ud0a4 \ub9e4\ud551 \uc804\ub7b5\uc5d0\ub294 \uc9c1\uc811 \ud560\ub2f9\ud558\ub294 \ubc29\ubc95\uc774 \uc788\uace0 \uc790\ub3d9 \uc0dd\uc131\ud574\uc11c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc774 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc790\ub3d9 \uc0dd\uc131\uc744 \uc120\ud0dd\ud55c\ub2e4\uba74 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0\uc11c \ud0a4\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \uc9c1\uc811 \ud560\ub2f9\ud558\uc9c0 \uc54a\uace0, \ub370\uc774\ud130\ubca0\uc774\uc2a4\uac00 \uc0dd\uc131\ud574\uc8fc\ub294 \uac12\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\uc9c1\uc811-\ud560\ub2f9"},"\uc9c1\uc811 \ud560\ub2f9"),(0,a.kt)("p",null,"\uae30\ubcf8 \ud0a4\ub97c \uc9c1\uc811 \ud560\ub2f9\ud558\ub294 \uacbd\uc6b0\uc5d0 ",(0,a.kt)("inlineCode",{parentName:"p"},"@Id"),"\ub9cc \uc0ac\uc6a9\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc790\ub3d9 \ud560\ub2f9\ud558\ub294 \uacbd\uc6b0 ",(0,a.kt)("inlineCode",{parentName:"p"},"@Id"),"\uc5d0 \ucd94\uac00\uc801\uc73c\ub85c ",(0,a.kt)("inlineCode",{parentName:"p"},"@GeneratedValue"),"\ub97c \uc0ac\uc6a9\ud574\uc57c \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"identity"},"IDENTITY"),(0,a.kt)("p",null,"\ub370\uc774\ud130\ubca0\uc774\uc2a4\uac00 \uc0dd\uc131\ud55c \ud0a4\ub97c \uc0ac\uc6a9\ud558\ub294 \uc804\ub7b5",(0,a.kt)("br",{parentName:"p"}),"\n","MySQL, PostgreSQL\uacfc \uac19\uc740 DB\uc5d0\uc11c \uc8fc\ub85c \uc0ac\uc6a9\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","Hibernate\ub294 JDBC3\uc5d0 \ucd94\uac00\ub41c ",(0,a.kt)("inlineCode",{parentName:"p"},"Statement.getGeneratedKeys()"),"\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc0dd\uc131\uacfc \ub3d9\uc2dc\uc5d0 \ud0a4 \uac12\uc744 \uc5bb\uc5b4 \uc62c \uc218 \uc788\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"@Getter\n@NoArgsConstructor(access = AccessLevel.PROTECTED)\n@Entity\npublic class Member {\n\n @Id\n @GeneratedValue(strategy = GenerationType.IDENTITY)\n private Long id;\n}\n")),(0,a.kt)("h3",{id:"sequence"},"SEQUENCE"),(0,a.kt)("p",null,"\uc2dc\ud000\uc2a4\ub294 \uc720\uc77c\ud55c \uac12\uc744 \uc21c\uc11c\ub300\ub85c \uc0dd\uc131\ud558\ub294 \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uc624\ube0c\uc81d\ud2b8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","IDENTITY\uc758 \uacbd\uc6b0 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud55c \ud6c4 \uc2dd\ubcc4\uc790\ub97c \uc870\ud68c\ud574\uc11c \uc5d4\ud2f0\ud2f0\uc758 \uc2dd\ubcc4\uc790\uc5d0 \ud560\ub2f9\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","SEQUENCE\uc758 \uacbd\uc6b0 ",(0,a.kt)("inlineCode",{parentName:"p"},"call next value for member_seq"),"\uc640 \uac19\uc774 \uc2dc\ud000\uc2a4\ub97c \uc0ac\uc6a9\ud574\uc11c \uc2dd\ubcc4\uc790\ub97c "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},'@Getter\n@NoArgsConstructor(access = AccessLevel.PROTECTED)\n@SequenceGenerator(\n name = "MEMBER_SEQ_GENERATOR",\n sequenceName = "MEMBER_SEQ",\n initialValue = 1,\n allocationSize = 1\n)\n@Entity\npublic class Member {\n\n @Id\n @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")\n private Long id;\n}\n')),(0,a.kt)("h3",{id:"table"},"TABLE"),(0,a.kt)("p",null,"\ud0a4 \uc0dd\uc131\uc6a9 \ud14c\uc774\ube14\uc744 \ubcc4\ub3c4\ub85c \ub450\uc5b4 \uc2dc\ud000\uc2a4\uc640 \uc720\uc0ac\ud558\uac8c \uc2dd\ubcc4\uc790 \ud0a4\ub97c \uc5bb\ub294 \uc804\ub7b5\uc774\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc870\ud68c\uc640 \uc5c5\ub370\uc774\ud2b8\ub97c \ud574\uc57c\ud55c\ub2e4\ub294 \ub2e8\uc810\uc774 \uc788\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},'@Getter\n@NoArgsConstructor(access = AccessLevel.PROTECTED)\n@TableGenerator(\n name = "MEMBER_SEQ_GENERATOR",\n table = "MEMBER_SEQUENCES",\n pkColumnValue = "MEMBER_SEQ",\n allocationSize = 1\n)\n@Entity\npublic class Member {\n\n @Id\n @GeneratedValue(strategy = GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR")\n private Long id;\n}\n')),(0,a.kt)("h3",{id:"auto"},"AUTO"),(0,a.kt)("p",null,"\ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \ub530\ub77c \uc704\uc5d0 \uc5b8\uae09\ub41c \uc804\ub7b5 \uc911 \ud558\ub098\ub97c \uc790\ub3d9\uc73c\ub85c \uc120\ud0dd\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","MySQL\uc758 \uacbd\uc6b0 IDENTITY Oracle\uc758 \uacbd\uc6b0 SEQUENCE\ub97c \uc120\ud0dd\ud55c\ub2e4. "),(0,a.kt)("h3",{id:"uuid"},"UUID"),(0,a.kt)("p",null,"JPA 3.1.0 UUID \uc0dd\uc131 \uc804\ub7b5\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","Hibernate 6.2\ubd80\ud130 JPA 3.1.0\uc744 \uc9c0\uc6d0\ud558\uae30 \ub54c\ubb38\uc5d0 \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1 \uc774\uc0c1\uc778 \uacbd\uc6b0 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"@Getter\n@NoArgsConstructor(access = AccessLevel.PROTECTED)\n@Entity\npublic class Member {\n\n @Id\n @GeneratedValue(strategy = GenerationType.UUID)\n @UuidGenerator(style = Style.RANDOM)\n private Long id;\n}\n")),(0,a.kt)("p",null,"UuidGenerator\ub97c \uc774\uc6a9\ud558\uc5ec UUID \uc0dd\uc131 \ubc29\uc2dd\ub3c4 \uc124\uc815\ud560 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0dd\uc131 \ubc29\uc2dd\uc740 3\uac00\uc9c0\uac00 \uc788\ub2e4."),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"RANDOM - \ub09c\uc218 \uae30\ubc18 UUID \uc0dd\uc131(uuid v4)"),(0,a.kt)("li",{parentName:"ul"},"TIME \u2013 \uc2dc\uac04 \uae30\ubc18 UUID \uc0dd\uc131(uuid v1)"),(0,a.kt)("li",{parentName:"ul"},"AUTO \u2013 \uae30\ubcf8 \uc635\uc158, RANDOM\uacfc \ub3d9\uc77c")),(0,a.kt)("p",null,"UUID\uc758 \uacbd\uc6b0 \ub9ce\uc740 \uc591\uc758 \uc800\uc7a5 \uacf5\uac04\uc744 \ud544\uc694\ub85c \ud558\uace0, \uc131\ub2a5 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud560 \uc218 \uc788\uae30\uc5d0 UUID\ub97c \uc0ac\uc6a9\ud574\uc57c \ud558\ub294 \uacbd\uc6b0 TSID\ub97c \uace0\ub824\ud560 \uc218 \uc788\uc744 \uac83 \uac19\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uc790\ubc14 ORM \ud45c\uc900 JPA \ud504\ub85c\uadf8\ub798\ubc0d, \uae40\uc601\ud55c p.131 ~ p.144",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://www.baeldung.com/java-hibernate-uuid-primary-key"},"Generate UUIDs as Primary Keys With Hibernate")))}s.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5140],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),u=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=u(e.components);return r.createElement(p.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,p=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),d=u(n),m=a,E=d["".concat(p,".").concat(m)]||d[m]||s[m]||l;return n?r.createElement(E,i(i({ref:t},c),{},{components:n})):r.createElement(E,i({ref:t},c))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,i=new Array(l);i[0]=d;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o.mdxType="string"==typeof e?e:a,i[1]=o;for(var u=2;u{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>s,frontMatter:()=>l,metadata:()=>o,toc:()=>u});var r=n(87462),a=(n(67294),n(3905));const l={title:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",slug:"/jpa/key",tags:["JPA"]},i=void 0,o={unversionedId:"JPA/\uae30\ubcf8_\ud0a4_\ub9e4\ud551",id:"JPA/\uae30\ubcf8_\ud0a4_\ub9e4\ud551",title:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",description:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",source:"@site/docs/JPA/\uae30\ubcf8_\ud0a4_\ub9e4\ud551.mdx",sourceDirName:"JPA",slug:"/jpa/key",permalink:"/docs/jpa/key",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/JPA/\uae30\ubcf8_\ud0a4_\ub9e4\ud551.mdx",tags:[{label:"JPA",permalink:"/docs/tags/jpa"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",slug:"/jpa/key",tags:["JPA"]},sidebar:"tutorialSidebar",next:{title:"\uad6c\uc870 \ubc0f \uba85\ub839\uc5b4",permalink:"/docs/nginx/command"}},p={},u=[{value:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",id:"\uae30\ubcf8-\ud0a4-\ub9e4\ud551",level:3},{value:"\uc9c1\uc811 \ud560\ub2f9",id:"\uc9c1\uc811-\ud560\ub2f9",level:3},{value:"IDENTITY",id:"identity",level:3},{value:"SEQUENCE",id:"sequence",level:3},{value:"TABLE",id:"table",level:3},{value:"AUTO",id:"auto",level:3},{value:"UUID",id:"uuid",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],c={toc:u};function s(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\uae30\ubcf8-\ud0a4-\ub9e4\ud551"},"\uae30\ubcf8 \ud0a4 \ub9e4\ud551"),(0,a.kt)("p",null,"\uae30\ubcf8 \ud0a4 \ub9e4\ud551 \uc804\ub7b5\uc5d0\ub294 \uc9c1\uc811 \ud560\ub2f9\ud558\ub294 \ubc29\ubc95\uc774 \uc788\uace0 \uc790\ub3d9 \uc0dd\uc131\ud574\uc11c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc774 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc790\ub3d9 \uc0dd\uc131\uc744 \uc120\ud0dd\ud55c\ub2e4\uba74 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0\uc11c \ud0a4\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \uc9c1\uc811 \ud560\ub2f9\ud558\uc9c0 \uc54a\uace0, \ub370\uc774\ud130\ubca0\uc774\uc2a4\uac00 \uc0dd\uc131\ud574\uc8fc\ub294 \uac12\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\uc9c1\uc811-\ud560\ub2f9"},"\uc9c1\uc811 \ud560\ub2f9"),(0,a.kt)("p",null,"\uae30\ubcf8 \ud0a4\ub97c \uc9c1\uc811 \ud560\ub2f9\ud558\ub294 \uacbd\uc6b0\uc5d0 ",(0,a.kt)("inlineCode",{parentName:"p"},"@Id"),"\ub9cc \uc0ac\uc6a9\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc790\ub3d9 \ud560\ub2f9\ud558\ub294 \uacbd\uc6b0 ",(0,a.kt)("inlineCode",{parentName:"p"},"@Id"),"\uc5d0 \ucd94\uac00\uc801\uc73c\ub85c ",(0,a.kt)("inlineCode",{parentName:"p"},"@GeneratedValue"),"\ub97c \uc0ac\uc6a9\ud574\uc57c \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"identity"},"IDENTITY"),(0,a.kt)("p",null,"\ub370\uc774\ud130\ubca0\uc774\uc2a4\uac00 \uc0dd\uc131\ud55c \ud0a4\ub97c \uc0ac\uc6a9\ud558\ub294 \uc804\ub7b5",(0,a.kt)("br",{parentName:"p"}),"\n","MySQL, PostgreSQL\uacfc \uac19\uc740 DB\uc5d0\uc11c \uc8fc\ub85c \uc0ac\uc6a9\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","Hibernate\ub294 JDBC3\uc5d0 \ucd94\uac00\ub41c ",(0,a.kt)("inlineCode",{parentName:"p"},"Statement.getGeneratedKeys()"),"\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc0dd\uc131\uacfc \ub3d9\uc2dc\uc5d0 \ud0a4 \uac12\uc744 \uc5bb\uc5b4 \uc62c \uc218 \uc788\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"@Getter\n@NoArgsConstructor(access = AccessLevel.PROTECTED)\n@Entity\npublic class Member {\n\n @Id\n @GeneratedValue(strategy = GenerationType.IDENTITY)\n private Long id;\n}\n")),(0,a.kt)("h3",{id:"sequence"},"SEQUENCE"),(0,a.kt)("p",null,"\uc2dc\ud000\uc2a4\ub294 \uc720\uc77c\ud55c \uac12\uc744 \uc21c\uc11c\ub300\ub85c \uc0dd\uc131\ud558\ub294 \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uc624\ube0c\uc81d\ud2b8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","IDENTITY\uc758 \uacbd\uc6b0 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud55c \ud6c4 \uc2dd\ubcc4\uc790\ub97c \uc870\ud68c\ud574\uc11c \uc5d4\ud2f0\ud2f0\uc758 \uc2dd\ubcc4\uc790\uc5d0 \ud560\ub2f9\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","SEQUENCE\uc758 \uacbd\uc6b0 ",(0,a.kt)("inlineCode",{parentName:"p"},"call next value for member_seq"),"\uc640 \uac19\uc774 \uc2dc\ud000\uc2a4\ub97c \uc0ac\uc6a9\ud574\uc11c \uc2dd\ubcc4\uc790\ub97c "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},'@Getter\n@NoArgsConstructor(access = AccessLevel.PROTECTED)\n@SequenceGenerator(\n name = "MEMBER_SEQ_GENERATOR",\n sequenceName = "MEMBER_SEQ",\n initialValue = 1,\n allocationSize = 1\n)\n@Entity\npublic class Member {\n\n @Id\n @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")\n private Long id;\n}\n')),(0,a.kt)("h3",{id:"table"},"TABLE"),(0,a.kt)("p",null,"\ud0a4 \uc0dd\uc131\uc6a9 \ud14c\uc774\ube14\uc744 \ubcc4\ub3c4\ub85c \ub450\uc5b4 \uc2dc\ud000\uc2a4\uc640 \uc720\uc0ac\ud558\uac8c \uc2dd\ubcc4\uc790 \ud0a4\ub97c \uc5bb\ub294 \uc804\ub7b5\uc774\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc870\ud68c\uc640 \uc5c5\ub370\uc774\ud2b8\ub97c \ud574\uc57c\ud55c\ub2e4\ub294 \ub2e8\uc810\uc774 \uc788\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},'@Getter\n@NoArgsConstructor(access = AccessLevel.PROTECTED)\n@TableGenerator(\n name = "MEMBER_SEQ_GENERATOR",\n table = "MEMBER_SEQUENCES",\n pkColumnValue = "MEMBER_SEQ",\n allocationSize = 1\n)\n@Entity\npublic class Member {\n\n @Id\n @GeneratedValue(strategy = GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR")\n private Long id;\n}\n')),(0,a.kt)("h3",{id:"auto"},"AUTO"),(0,a.kt)("p",null,"\ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \ub530\ub77c \uc704\uc5d0 \uc5b8\uae09\ub41c \uc804\ub7b5 \uc911 \ud558\ub098\ub97c \uc790\ub3d9\uc73c\ub85c \uc120\ud0dd\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","MySQL\uc758 \uacbd\uc6b0 IDENTITY Oracle\uc758 \uacbd\uc6b0 SEQUENCE\ub97c \uc120\ud0dd\ud55c\ub2e4. "),(0,a.kt)("h3",{id:"uuid"},"UUID"),(0,a.kt)("p",null,"JPA 3.1.0 UUID \uc0dd\uc131 \uc804\ub7b5\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","Hibernate 6.2\ubd80\ud130 JPA 3.1.0\uc744 \uc9c0\uc6d0\ud558\uae30 \ub54c\ubb38\uc5d0 \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1 \uc774\uc0c1\uc778 \uacbd\uc6b0 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"@Getter\n@NoArgsConstructor(access = AccessLevel.PROTECTED)\n@Entity\npublic class Member {\n\n @Id\n @GeneratedValue(strategy = GenerationType.UUID)\n @UuidGenerator(style = Style.RANDOM)\n private Long id;\n}\n")),(0,a.kt)("p",null,"UuidGenerator\ub97c \uc774\uc6a9\ud558\uc5ec UUID \uc0dd\uc131 \ubc29\uc2dd\ub3c4 \uc124\uc815\ud560 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0dd\uc131 \ubc29\uc2dd\uc740 3\uac00\uc9c0\uac00 \uc788\ub2e4."),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"RANDOM - \ub09c\uc218 \uae30\ubc18 UUID \uc0dd\uc131(uuid v4)"),(0,a.kt)("li",{parentName:"ul"},"TIME \u2013 \uc2dc\uac04 \uae30\ubc18 UUID \uc0dd\uc131(uuid v1)"),(0,a.kt)("li",{parentName:"ul"},"AUTO \u2013 \uae30\ubcf8 \uc635\uc158, RANDOM\uacfc \ub3d9\uc77c")),(0,a.kt)("p",null,"UUID\uc758 \uacbd\uc6b0 \ub9ce\uc740 \uc591\uc758 \uc800\uc7a5 \uacf5\uac04\uc744 \ud544\uc694\ub85c \ud558\uace0, \uc131\ub2a5 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud560 \uc218 \uc788\uae30\uc5d0 UUID\ub97c \uc0ac\uc6a9\ud574\uc57c \ud558\ub294 \uacbd\uc6b0 TSID\ub97c \uace0\ub824\ud560 \uc218 \uc788\uc744 \uac83 \uac19\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uc790\ubc14 ORM \ud45c\uc900 JPA \ud504\ub85c\uadf8\ub798\ubc0d, \uae40\uc601\ud55c p.131 ~ p.144",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://www.baeldung.com/java-hibernate-uuid-primary-key"},"Generate UUIDs as Primary Keys With Hibernate")))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3fc16fd0.69ad4ad7.js b/assets/js/3fc16fd0.5d70fe41.js similarity index 98% rename from assets/js/3fc16fd0.69ad4ad7.js rename to assets/js/3fc16fd0.5d70fe41.js index 9173f3605..66cfe236f 100644 --- a/assets/js/3fc16fd0.69ad4ad7.js +++ b/assets/js/3fc16fd0.5d70fe41.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3886],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>k});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),c=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},s=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,l=e.originalType,p=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),s=c(r),k=o,d=s["".concat(p,".").concat(k)]||s[k]||m[k]||l;return r?n.createElement(d,a(a({ref:t},u),{},{components:r})):n.createElement(d,a({ref:t},u))}));function k(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var l=r.length,a=new Array(l);a[0]=s;var i={};for(var p in t)hasOwnProperty.call(t,p)&&(i[p]=t[p]);i.originalType=e,i.mdxType="string"==typeof e?e:o,a[1]=i;for(var c=2;c{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>m,frontMatter:()=>l,metadata:()=>i,toc:()=>c});var n=r(87462),o=(r(67294),r(3905));const l={title:"\uc0c1\uc790 \ubc16\uc73c\ub85c \ud0c8\ucd9c\ud558\uae30",slug:"/book/getting-out-of-the-box",tags:["book"]},a=void 0,i={unversionedId:"\ub3c4\uc11c/\uc0c1\uc790_\ubc16\uc73c\ub85c_\ud0c8\ucd9c\ud558\uae30",id:"\ub3c4\uc11c/\uc0c1\uc790_\ubc16\uc73c\ub85c_\ud0c8\ucd9c\ud558\uae30",title:"\uc0c1\uc790 \ubc16\uc73c\ub85c \ud0c8\ucd9c\ud558\uae30",description:"\u26a0\ufe0f \uc790\uae30\ubc30\ubc18\uc758 \uc6d0\ub9ac",source:"@site/docs/\ub3c4\uc11c/\uc0c1\uc790_\ubc16\uc73c\ub85c_\ud0c8\ucd9c\ud558\uae30.mdx",sourceDirName:"\ub3c4\uc11c",slug:"/book/getting-out-of-the-box",permalink:"/docs/book/getting-out-of-the-box",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ub3c4\uc11c/\uc0c1\uc790_\ubc16\uc73c\ub85c_\ud0c8\ucd9c\ud558\uae30.mdx",tags:[{label:"book",permalink:"/docs/tags/book"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\uc0c1\uc790 \ubc16\uc73c\ub85c \ud0c8\ucd9c\ud558\uae30",slug:"/book/getting-out-of-the-box",tags:["book"]},sidebar:"tutorialSidebar",previous:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1",permalink:"/docs/network/load-balancing"},next:{title:"Swap \uba54\ubaa8\ub9ac \uc124\uc815",permalink:"/docs/linux/swap"}},p={},c=[{value:"\u26a0\ufe0f \uc790\uae30\ubc30\ubc18\uc758 \uc6d0\ub9ac",id:"\ufe0f-\uc790\uae30\ubc30\ubc18\uc758-\uc6d0\ub9ac",level:3},{value:"\u274c \uc0c1\uc790 \uc548\uc5d0\uc11c \uc18c\uc6a9\uc5c6\ub294 \uc77c",id:"-\uc0c1\uc790-\uc548\uc5d0\uc11c-\uc18c\uc6a9\uc5c6\ub294-\uc77c",level:3},{value:"\ud83d\udcd6 \ud559\uc2b5\uc790\ub8cc",id:"-\ud559\uc2b5\uc790\ub8cc",level:3},{value:"\ud83c\udfc3 \uc2e4\ucc9c\ud558\uae30",id:"-\uc2e4\ucc9c\ud558\uae30",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:c};function m(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h3",{id:"\ufe0f-\uc790\uae30\ubc30\ubc18\uc758-\uc6d0\ub9ac"},"\u26a0\ufe0f \uc790\uae30\ubc30\ubc18\uc758 \uc6d0\ub9ac"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\uc744 \uc704\ud574 \ub0b4\uac00 \ubb34\uc5c7\uc778\uac00 \ud574\uc57c\ub9cc \ud55c\ub2e4\ub294 \uc0dd\uac01\uacfc \ub290\ub08c\uc5d0 \ub300\ud574 \ubc18\ud558\ub294 \ud589\uc704\ub97c \uc790\uae30\ubc30\ubc18\uc774\ub77c\uace0 \ud55c\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\ub0b4\uac00 \uc790\uae30\ubc30\ubc18\ud560 \ub54c, \ub098\ub294 \uc790\uae30\ubc30\ubc18\uc744 \uc815\ub2f9\ud654\uc2dc\ud0a4\ub294 \ubc29\uc2dd\uc73c\ub85c \uc138\uc0c1\uc744 \ubcf4\uae30 \uc2dc\uc791\ud55c\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\uc790\uc2e0\uc744 \uc815\ub2f9\ud654\uc2dc\ud0a4\ub294 \ubc29\uc2dd\uc73c\ub85c \uc138\uc0c1\uc744 \ubcfc \ub54c, \uc0ac\uc2e4\uc744 \ubcf4\ub294 \ub098\uc758 \uc2dc\uac01\uc740 \uc65c\uace1\ub41c\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\uc790\uae30\ubc30\ubc18\ud560 \ub54c, \ub098\ub294 \uc0c1\uc790 \uc548\uc5d0 \ub4e4\uc5b4\uac00\uac8c \ub41c\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\uc2dc\uac04\uc774 \uc9c0\ub098\uba74\uc11c \uc5b4\ub5a4 \uc0c1\uc790\ub4e4\uc740 \ub098\uc758 \ud2b9\uc131\uc774 \ub418\uace0 \uc77c\uc0c1\uc801\uc73c\ub85c \ub098\ub294 \uadf8 \uc0c1\uc790\ub4e4\uc744 \uc9c0\ub2c8\uace0 \ub2e4\ub2cc\ub2e4."),(0,o.kt)("li",{parentName:"ol"},"\ub0b4\uac00 \uc0c1\uc790 \uc548\uc5d0 \uc788\uc74c\uc73c\ub85c \uc778\ud558\uc5ec, \ub098\ub294 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\ub3c4 \uc0c1\uc790 \uc548\uc5d0 \ub4e4\uc5b4\uac00\ub3c4\ub85d \uc774\ub04c\uac8c \ub41c\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\uc0c1\uc790 \uc548\uc5d0\uc11c \uc6b0\ub9ac\ub294 \uc11c\ub85c \uc798\ubabb \ub300\ud558\ub294 \uac83\uc744 \ubd80\ucd94\uae30\uace0 \uc0c1\ud638 \uc815\ub2f9\ud654\ub97c \uc5bb\uac8c \ub41c\ub2e4. \uc6b0\ub9ac\ub294 \uc11c\ub85c\uc5d0\uac8c \uc0c1\uc790 \uc548\uc5d0 \uba38\ubb3c\uae30 \uc704\ud55c \uc774\uc720\ub97c \uc8fc\ub3c4\ub85d \uacf5\ubaa8\ud55c\ub2e4. ")),(0,o.kt)("h3",{id:"-\uc0c1\uc790-\uc548\uc5d0\uc11c-\uc18c\uc6a9\uc5c6\ub294-\uc77c"},"\u274c \uc0c1\uc790 \uc548\uc5d0\uc11c \uc18c\uc6a9\uc5c6\ub294 \uc77c"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc744 \ubcc0\ud654\uc2dc\ud0a4\ub824\uace0 \ud558\ub294 \uac83"),(0,o.kt)("li",{parentName:"ol"},'\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc5d0\uac8c "\ub9de\ucdb0\uc8fc\uae30" \uc704\ud574 \ucd5c\uc120\uc744 \ub2e4\ud558\uae30'),(0,o.kt)("li",{parentName:"ol"},"\ub450\uace0 \ub5a0\ub098\uae30"),(0,o.kt)("li",{parentName:"ol"},"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158"),(0,o.kt)("li",{parentName:"ol"},"\uc0c8\ub85c\uc6b4 \uae30\uc220\uc774\ub098 \ud14c\ud06c\ub2c9 \ud65c\uc6a9\ud558\uae30"),(0,o.kt)("li",{parentName:"ol"},"\ub098\uc758 \ud589\ub3d9\uc744 \ubcc0\ud654\uc2dc\ud0a4\ub294 \uac83")),(0,o.kt)("h3",{id:"-\ud559\uc2b5\uc790\ub8cc"},"\ud83d\udcd6 \ud559\uc2b5\uc790\ub8cc"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},'\uc790\uae30\ubc30\ubc18\uc740 \uc790\uae30\uae30\ub9cc\uacfc "\uc0c1\uc790"\uc548\uc73c\ub85c \uc774\ub048\ub2e4. '),(0,o.kt)("li",{parentName:"ol"},"\uc0c1\uc790 \uc548\uc5d0 \uc788\uc744 \ub54c, \ub2f9\uc2e0\uc740 \uacb0\uacfc(\uc131\uacfc)\uc5d0 \uc9d1\uc911\ud560 \uc218 \uc5c6\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\ub2f9\uc2e0\uc758 \uc601\ud5a5\ub825\uacfc \uc131\uacf5\uc758 \ud06c\uae30\ub294 \uc5bc\ub9c8\ub098 \uc0c1\uc790 \ubc16\uc5d0 \uc874\uc7ac\ud558\ub290\ub0d0\uc5d0 \ub2ec\ub824\uc788\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc5d0\uac8c \uc800\ud56d\ud558\ub294 \uac83\uc744 \uadf8\ub9cc\ub458 \ub54c \ub2f9\uc2e0\uc740 \uc0c1\uc790 \ubc16\uc5d0 \uc788\uac8c \ub41c\ub2e4. ")),(0,o.kt)("h3",{id:"-\uc2e4\ucc9c\ud558\uae30"},"\ud83c\udfc3 \uc2e4\ucc9c\ud558\uae30"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"\uc644\ubcbd\ud574\uc9c0\ub824\uace0 \ub178\ub825\ud558\uc9c0 \ub9d0\uace0, \uc9c0\uae08\ubcf4\ub2e4 \ub354 \uc88b\uc544\uc9c0\ub824\uace0 \ub178\ub825\ud558\ub77c. "),(0,o.kt)("li",{parentName:"ol"},"\uc544\uc9c1 \ud559\uc2b5\ub0b4\uc6a9\uc744 \uc54c\uc9c0 \ubabb\ud558\ub294 \uc0ac\ub78c\ub4e4\uc5d0\uac8c '\uc0c1\uc790'\ub098 \uae30\ud0c0 \ub2e8\uc5b4\ub4e4\uc744 \uc0ac\uc6a9\ud558\uc9c0 \ub9c8\ub77c. \ub2e4\ub9cc \ub2f9\uc2e0 \uc790\uc2e0\uc758 \uc0b6\uc5d0\uc11c \uadf8 \uc6d0\ub9ac\ub4e4\uc744 \uc801\uc6a9\ud558\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc758 \uc0c1\uc790\ub97c \ucc3e\uc9c0 \ub9d0\uace0, \uba3c\uc800 \ub2f9\uc2e0 \uc790\uc2e0\uc758 \uc0c1\uc790\ub97c \ucc3e\uc544\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc774 \uc0c1\uc790 \uc548\uc5d0 \uc788\ub2e4\uace0 \ud790\ub09c\ud558\uc9c0 \ub9d0\uace0, \ub2f9\uc2e0\uc774 \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub3c4\ub85d \ub178\ub825\ud558\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2f9\uc2e0\uc774 \uc0c1\uc790 \uc548\uc5d0 \uc788\ub2e4\ub294 \uac83\uc744 \ubc1c\uacac\ud588\uc744 \ub54c \uc790\uc2e0\uc5d0 \ub300\ud574 \ud3ec\uae30\ud558\uc9c0 \ub9c8\ub77c. \uacc4\uc18d \ub178\ub825\ud558\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2f9\uc2e0\uc774 \uc0c1\uc790 \uc548\uc5d0 \uc788\ub2e4\ub294 \uac83\uc744 \ubd80\uc778\ud558\uc9c0 \ub9c8\ub77c. \uc0ac\uacfc\ud558\uace0, \uacc4\uc18d\ud574\uc11c \uc804\uc9c4\ud558\ub77c. \ubbf8\ub798\uc5d0 \ub2e4\ub978 \uc0ac\ub78c\uc5d0\uac8c \ub354 \ub3c4\uc6c0\uc774 \ub418\ub3c4\ub85d \ub178\ub825\ud558\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc774 \uc798\ubabb\ud558\uace0 \uc788\ub294 \uac83\uc5d0 \ucd08\uc810\uc744 \ub9de\ucd94\uc9c0 \ub9c8\ub77c. \uadf8\ub4e4\uc744 \ub3d5\uae30 \uc704\ud574 \ub2f9\uc2e0\uc774 \uc62c\ubc14\ub974\uac8c \ud589\ud560 \uc218 \uc788\ub294 \uc77c\uc5d0 \ucd08\uc810\uc744 \ub9de\ucdb0\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc774 \ub2f9\uc2e0\uc744 \ub3d5\uace0 \uc788\ub294\uc9c0\uc5d0 \ub300\ud574 \uc5fc\ub824\ud558\uc9c0 \ub9c8\ub77c. \ub2f9\uc2e0\uc774 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc744 \ub3d5\uace0 \uc788\ub294\uc9c0\uc5d0 \ub300\ud574 \uac71\uc815\ud558\ub77c. ")),(0,o.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,o.kt)("p",null,"\uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c, \uc544\ube48\uc800\uc5f0\uad6c\uc18c"))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3886],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>k});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),c=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},s=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,l=e.originalType,p=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),s=c(r),k=o,d=s["".concat(p,".").concat(k)]||s[k]||m[k]||l;return r?n.createElement(d,a(a({ref:t},u),{},{components:r})):n.createElement(d,a({ref:t},u))}));function k(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var l=r.length,a=new Array(l);a[0]=s;var i={};for(var p in t)hasOwnProperty.call(t,p)&&(i[p]=t[p]);i.originalType=e,i.mdxType="string"==typeof e?e:o,a[1]=i;for(var c=2;c{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>m,frontMatter:()=>l,metadata:()=>i,toc:()=>c});var n=r(87462),o=(r(67294),r(3905));const l={title:"\uc0c1\uc790 \ubc16\uc73c\ub85c \ud0c8\ucd9c\ud558\uae30",slug:"/book/getting-out-of-the-box",tags:["book"]},a=void 0,i={unversionedId:"\ub3c4\uc11c/\uc0c1\uc790_\ubc16\uc73c\ub85c_\ud0c8\ucd9c\ud558\uae30",id:"\ub3c4\uc11c/\uc0c1\uc790_\ubc16\uc73c\ub85c_\ud0c8\ucd9c\ud558\uae30",title:"\uc0c1\uc790 \ubc16\uc73c\ub85c \ud0c8\ucd9c\ud558\uae30",description:"\u26a0\ufe0f \uc790\uae30\ubc30\ubc18\uc758 \uc6d0\ub9ac",source:"@site/docs/\ub3c4\uc11c/\uc0c1\uc790_\ubc16\uc73c\ub85c_\ud0c8\ucd9c\ud558\uae30.mdx",sourceDirName:"\ub3c4\uc11c",slug:"/book/getting-out-of-the-box",permalink:"/docs/book/getting-out-of-the-box",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ub3c4\uc11c/\uc0c1\uc790_\ubc16\uc73c\ub85c_\ud0c8\ucd9c\ud558\uae30.mdx",tags:[{label:"book",permalink:"/docs/tags/book"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\uc0c1\uc790 \ubc16\uc73c\ub85c \ud0c8\ucd9c\ud558\uae30",slug:"/book/getting-out-of-the-box",tags:["book"]},sidebar:"tutorialSidebar",previous:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1",permalink:"/docs/network/load-balancing"},next:{title:"Swap \uba54\ubaa8\ub9ac \uc124\uc815",permalink:"/docs/linux/swap"}},p={},c=[{value:"\u26a0\ufe0f \uc790\uae30\ubc30\ubc18\uc758 \uc6d0\ub9ac",id:"\ufe0f-\uc790\uae30\ubc30\ubc18\uc758-\uc6d0\ub9ac",level:3},{value:"\u274c \uc0c1\uc790 \uc548\uc5d0\uc11c \uc18c\uc6a9\uc5c6\ub294 \uc77c",id:"-\uc0c1\uc790-\uc548\uc5d0\uc11c-\uc18c\uc6a9\uc5c6\ub294-\uc77c",level:3},{value:"\ud83d\udcd6 \ud559\uc2b5\uc790\ub8cc",id:"-\ud559\uc2b5\uc790\ub8cc",level:3},{value:"\ud83c\udfc3 \uc2e4\ucc9c\ud558\uae30",id:"-\uc2e4\ucc9c\ud558\uae30",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:c};function m(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h3",{id:"\ufe0f-\uc790\uae30\ubc30\ubc18\uc758-\uc6d0\ub9ac"},"\u26a0\ufe0f \uc790\uae30\ubc30\ubc18\uc758 \uc6d0\ub9ac"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\uc744 \uc704\ud574 \ub0b4\uac00 \ubb34\uc5c7\uc778\uac00 \ud574\uc57c\ub9cc \ud55c\ub2e4\ub294 \uc0dd\uac01\uacfc \ub290\ub08c\uc5d0 \ub300\ud574 \ubc18\ud558\ub294 \ud589\uc704\ub97c \uc790\uae30\ubc30\ubc18\uc774\ub77c\uace0 \ud55c\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\ub0b4\uac00 \uc790\uae30\ubc30\ubc18\ud560 \ub54c, \ub098\ub294 \uc790\uae30\ubc30\ubc18\uc744 \uc815\ub2f9\ud654\uc2dc\ud0a4\ub294 \ubc29\uc2dd\uc73c\ub85c \uc138\uc0c1\uc744 \ubcf4\uae30 \uc2dc\uc791\ud55c\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\uc790\uc2e0\uc744 \uc815\ub2f9\ud654\uc2dc\ud0a4\ub294 \ubc29\uc2dd\uc73c\ub85c \uc138\uc0c1\uc744 \ubcfc \ub54c, \uc0ac\uc2e4\uc744 \ubcf4\ub294 \ub098\uc758 \uc2dc\uac01\uc740 \uc65c\uace1\ub41c\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\uc790\uae30\ubc30\ubc18\ud560 \ub54c, \ub098\ub294 \uc0c1\uc790 \uc548\uc5d0 \ub4e4\uc5b4\uac00\uac8c \ub41c\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\uc2dc\uac04\uc774 \uc9c0\ub098\uba74\uc11c \uc5b4\ub5a4 \uc0c1\uc790\ub4e4\uc740 \ub098\uc758 \ud2b9\uc131\uc774 \ub418\uace0 \uc77c\uc0c1\uc801\uc73c\ub85c \ub098\ub294 \uadf8 \uc0c1\uc790\ub4e4\uc744 \uc9c0\ub2c8\uace0 \ub2e4\ub2cc\ub2e4."),(0,o.kt)("li",{parentName:"ol"},"\ub0b4\uac00 \uc0c1\uc790 \uc548\uc5d0 \uc788\uc74c\uc73c\ub85c \uc778\ud558\uc5ec, \ub098\ub294 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\ub3c4 \uc0c1\uc790 \uc548\uc5d0 \ub4e4\uc5b4\uac00\ub3c4\ub85d \uc774\ub04c\uac8c \ub41c\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\uc0c1\uc790 \uc548\uc5d0\uc11c \uc6b0\ub9ac\ub294 \uc11c\ub85c \uc798\ubabb \ub300\ud558\ub294 \uac83\uc744 \ubd80\ucd94\uae30\uace0 \uc0c1\ud638 \uc815\ub2f9\ud654\ub97c \uc5bb\uac8c \ub41c\ub2e4. \uc6b0\ub9ac\ub294 \uc11c\ub85c\uc5d0\uac8c \uc0c1\uc790 \uc548\uc5d0 \uba38\ubb3c\uae30 \uc704\ud55c \uc774\uc720\ub97c \uc8fc\ub3c4\ub85d \uacf5\ubaa8\ud55c\ub2e4. ")),(0,o.kt)("h3",{id:"-\uc0c1\uc790-\uc548\uc5d0\uc11c-\uc18c\uc6a9\uc5c6\ub294-\uc77c"},"\u274c \uc0c1\uc790 \uc548\uc5d0\uc11c \uc18c\uc6a9\uc5c6\ub294 \uc77c"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc744 \ubcc0\ud654\uc2dc\ud0a4\ub824\uace0 \ud558\ub294 \uac83"),(0,o.kt)("li",{parentName:"ol"},'\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc5d0\uac8c "\ub9de\ucdb0\uc8fc\uae30" \uc704\ud574 \ucd5c\uc120\uc744 \ub2e4\ud558\uae30'),(0,o.kt)("li",{parentName:"ol"},"\ub450\uace0 \ub5a0\ub098\uae30"),(0,o.kt)("li",{parentName:"ol"},"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158"),(0,o.kt)("li",{parentName:"ol"},"\uc0c8\ub85c\uc6b4 \uae30\uc220\uc774\ub098 \ud14c\ud06c\ub2c9 \ud65c\uc6a9\ud558\uae30"),(0,o.kt)("li",{parentName:"ol"},"\ub098\uc758 \ud589\ub3d9\uc744 \ubcc0\ud654\uc2dc\ud0a4\ub294 \uac83")),(0,o.kt)("h3",{id:"-\ud559\uc2b5\uc790\ub8cc"},"\ud83d\udcd6 \ud559\uc2b5\uc790\ub8cc"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},'\uc790\uae30\ubc30\ubc18\uc740 \uc790\uae30\uae30\ub9cc\uacfc "\uc0c1\uc790"\uc548\uc73c\ub85c \uc774\ub048\ub2e4. '),(0,o.kt)("li",{parentName:"ol"},"\uc0c1\uc790 \uc548\uc5d0 \uc788\uc744 \ub54c, \ub2f9\uc2e0\uc740 \uacb0\uacfc(\uc131\uacfc)\uc5d0 \uc9d1\uc911\ud560 \uc218 \uc5c6\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\ub2f9\uc2e0\uc758 \uc601\ud5a5\ub825\uacfc \uc131\uacf5\uc758 \ud06c\uae30\ub294 \uc5bc\ub9c8\ub098 \uc0c1\uc790 \ubc16\uc5d0 \uc874\uc7ac\ud558\ub290\ub0d0\uc5d0 \ub2ec\ub824\uc788\ub2e4. "),(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc5d0\uac8c \uc800\ud56d\ud558\ub294 \uac83\uc744 \uadf8\ub9cc\ub458 \ub54c \ub2f9\uc2e0\uc740 \uc0c1\uc790 \ubc16\uc5d0 \uc788\uac8c \ub41c\ub2e4. ")),(0,o.kt)("h3",{id:"-\uc2e4\ucc9c\ud558\uae30"},"\ud83c\udfc3 \uc2e4\ucc9c\ud558\uae30"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"\uc644\ubcbd\ud574\uc9c0\ub824\uace0 \ub178\ub825\ud558\uc9c0 \ub9d0\uace0, \uc9c0\uae08\ubcf4\ub2e4 \ub354 \uc88b\uc544\uc9c0\ub824\uace0 \ub178\ub825\ud558\ub77c. "),(0,o.kt)("li",{parentName:"ol"},"\uc544\uc9c1 \ud559\uc2b5\ub0b4\uc6a9\uc744 \uc54c\uc9c0 \ubabb\ud558\ub294 \uc0ac\ub78c\ub4e4\uc5d0\uac8c '\uc0c1\uc790'\ub098 \uae30\ud0c0 \ub2e8\uc5b4\ub4e4\uc744 \uc0ac\uc6a9\ud558\uc9c0 \ub9c8\ub77c. \ub2e4\ub9cc \ub2f9\uc2e0 \uc790\uc2e0\uc758 \uc0b6\uc5d0\uc11c \uadf8 \uc6d0\ub9ac\ub4e4\uc744 \uc801\uc6a9\ud558\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc758 \uc0c1\uc790\ub97c \ucc3e\uc9c0 \ub9d0\uace0, \uba3c\uc800 \ub2f9\uc2e0 \uc790\uc2e0\uc758 \uc0c1\uc790\ub97c \ucc3e\uc544\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc774 \uc0c1\uc790 \uc548\uc5d0 \uc788\ub2e4\uace0 \ud790\ub09c\ud558\uc9c0 \ub9d0\uace0, \ub2f9\uc2e0\uc774 \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub3c4\ub85d \ub178\ub825\ud558\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2f9\uc2e0\uc774 \uc0c1\uc790 \uc548\uc5d0 \uc788\ub2e4\ub294 \uac83\uc744 \ubc1c\uacac\ud588\uc744 \ub54c \uc790\uc2e0\uc5d0 \ub300\ud574 \ud3ec\uae30\ud558\uc9c0 \ub9c8\ub77c. \uacc4\uc18d \ub178\ub825\ud558\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2f9\uc2e0\uc774 \uc0c1\uc790 \uc548\uc5d0 \uc788\ub2e4\ub294 \uac83\uc744 \ubd80\uc778\ud558\uc9c0 \ub9c8\ub77c. \uc0ac\uacfc\ud558\uace0, \uacc4\uc18d\ud574\uc11c \uc804\uc9c4\ud558\ub77c. \ubbf8\ub798\uc5d0 \ub2e4\ub978 \uc0ac\ub78c\uc5d0\uac8c \ub354 \ub3c4\uc6c0\uc774 \ub418\ub3c4\ub85d \ub178\ub825\ud558\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc774 \uc798\ubabb\ud558\uace0 \uc788\ub294 \uac83\uc5d0 \ucd08\uc810\uc744 \ub9de\ucd94\uc9c0 \ub9c8\ub77c. \uadf8\ub4e4\uc744 \ub3d5\uae30 \uc704\ud574 \ub2f9\uc2e0\uc774 \uc62c\ubc14\ub974\uac8c \ud589\ud560 \uc218 \uc788\ub294 \uc77c\uc5d0 \ucd08\uc810\uc744 \ub9de\ucdb0\ub77c."),(0,o.kt)("li",{parentName:"ol"},"\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc774 \ub2f9\uc2e0\uc744 \ub3d5\uace0 \uc788\ub294\uc9c0\uc5d0 \ub300\ud574 \uc5fc\ub824\ud558\uc9c0 \ub9c8\ub77c. \ub2f9\uc2e0\uc774 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc744 \ub3d5\uace0 \uc788\ub294\uc9c0\uc5d0 \ub300\ud574 \uac71\uc815\ud558\ub77c. ")),(0,o.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,o.kt)("p",null,"\uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c, \uc544\ube48\uc800\uc5f0\uad6c\uc18c"))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4515250b.7c65c757.js b/assets/js/4515250b.7c65c757.js new file mode 100644 index 000000000..8d273f90d --- /dev/null +++ b/assets/js/4515250b.7c65c757.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9304],{88767:e=>{e.exports=JSON.parse('{"permalink":"/tags/exception","page":1,"postsPerPage":1,"totalPages":1,"totalCount":1,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/459bd227.9c343d08.js b/assets/js/459bd227.d754b82a.js similarity index 98% rename from assets/js/459bd227.9c343d08.js rename to assets/js/459bd227.d754b82a.js index 299355448..7dbb89602 100644 --- a/assets/js/459bd227.9c343d08.js +++ b/assets/js/459bd227.d754b82a.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9094],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>d});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function p(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var i=n.createContext({}),c=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):p(p({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(i.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,l=e.originalType,i=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),m=c(r),d=a,b=m["".concat(i,".").concat(d)]||m[d]||s[d]||l;return r?n.createElement(b,p(p({ref:t},u),{},{components:r})):n.createElement(b,p({ref:t},u))}));function d(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=r.length,p=new Array(l);p[0]=m;var o={};for(var i in t)hasOwnProperty.call(t,i)&&(o[i]=t[i]);o.originalType=e,o.mdxType="string"==typeof e?e:a,p[1]=o;for(var c=2;c{r.r(t),r.d(t,{assets:()=>i,contentTitle:()=>p,default:()=>s,frontMatter:()=>l,metadata:()=>o,toc:()=>c});var n=r(87462),a=(r(67294),r(3905));const l={title:"\uac74\uac15\ud558\uac8c \ub098\uc544\uc9c0\uae30",slug:"/etc/healthful-growth",tags:["etc"]},p=void 0,o={unversionedId:"\uae30\ud0c0/\uac74\uac15\ud558\uac8c_\ub098\uc544\uc9c0\uae30",id:"\uae30\ud0c0/\uac74\uac15\ud558\uac8c_\ub098\uc544\uc9c0\uae30",title:"\uac74\uac15\ud558\uac8c \ub098\uc544\uc9c0\uae30",description:"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 2023\ub144 4\uc6d4 19\uc77c",source:"@site/docs/\uae30\ud0c0/\uac74\uac15\ud558\uac8c_\ub098\uc544\uc9c0\uae30.mdx",sourceDirName:"\uae30\ud0c0",slug:"/etc/healthful-growth",permalink:"/docs/etc/healthful-growth",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uae30\ud0c0/\uac74\uac15\ud558\uac8c_\ub098\uc544\uc9c0\uae30.mdx",tags:[{label:"etc",permalink:"/docs/tags/etc"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\uac74\uac15\ud558\uac8c \ub098\uc544\uc9c0\uae30",slug:"/etc/healthful-growth",tags:["etc"]},sidebar:"tutorialSidebar",previous:{title:"\ubb38\uc11c",permalink:"/docs/"},next:{title:"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",permalink:"/docs/etc/develop-with-spring"}},i={},c=[{value:"\uc790\uc874\uac10 \uae30\ub465 \ub9cc\ub4e4\uae30",id:"\uc790\uc874\uac10-\uae30\ub465-\ub9cc\ub4e4\uae30",level:3},{value:"\ub098\ub9cc\uc758 \ud559\uc2b5 \ubc29\ubc95 \ucc3e\uae30",id:"\ub098\ub9cc\uc758-\ud559\uc2b5-\ubc29\ubc95-\ucc3e\uae30",level:3},{value:"\uc0c8\ub85c\uc6b4 \ud658\uacbd\uc744 \uc798 \ubc30\uc6b0\ub294 \ubc29\ubc95",id:"\uc0c8\ub85c\uc6b4-\ud658\uacbd\uc744-\uc798-\ubc30\uc6b0\ub294-\ubc29\ubc95",level:3},{value:"\ud559\uc2b5 \uc8fc\uc81c",id:"\ud559\uc2b5-\uc8fc\uc81c",level:3},{value:"\uc0b0\ub9cc\ud568 \uad00\ub9ac\ud558\uae30",id:"\uc0b0\ub9cc\ud568-\uad00\ub9ac\ud558\uae30",level:3},{value:"\uac70\uc778\uc5d0 \uc5b4\uae68\uc704\uc5d0 \uc62c\ub77c\ud0c0\uae30",id:"\uac70\uc778\uc5d0-\uc5b4\uae68\uc704\uc5d0-\uc62c\ub77c\ud0c0\uae30",level:3},{value:"\ubcf4\uc0c1",id:"\ubcf4\uc0c1",level:3},{value:"\ub0a8\uc744 \uc124\ub4dd\ud558\ub294 \ubc29\ubc95 \ubc30\uc6b0\uae30",id:"\ub0a8\uc744-\uc124\ub4dd\ud558\ub294-\ubc29\ubc95-\ubc30\uc6b0\uae30",level:3}],u={toc:c};function s(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 2023\ub144 4\uc6d4 19\uc77c",(0,a.kt)("br",{parentName:"p"}),"\n","\uc774\ub3d9\uc6b1\ub2d8 \ud2b9\uac15"),(0,a.kt)("h3",{id:"\uc790\uc874\uac10-\uae30\ub465-\ub9cc\ub4e4\uae30"},"\uc790\uc874\uac10 \uae30\ub465 \ub9cc\ub4e4\uae30"),(0,a.kt)("p",null,"\ub6f0\uc5b4\ub09c \ub3d9\ub8cc, \uc0c8\ub85c\uc6b4 \ud658\uacbd \uadf8\ub9ac\uace0 \ud504\ub85c\uc81d\ud2b8\ub97c \uc2e4\ud328\ud558\uba74\uc11c \uc790\uc874\uac10\uc774 \ub5a8\uc5b4\uc9c8 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc790\uc874\uac10\uc774 \ubb34\ub108\uc9c0\uc9c0 \uc54a\ub3c4\ub85d \ub098\ub97c \uc9c0\ud0f1\ud560 \uc218 \uc788\ub294 \uae30\ub465\uc774 \ud544\uc694\ud558\ub2e4. (\ud55c \uac1c\uac00 \uc544\ub2cc \uc5ec\ub7ec \uac1c) "),(0,a.kt)("h3",{id:"\ub098\ub9cc\uc758-\ud559\uc2b5-\ubc29\ubc95-\ucc3e\uae30"},"\ub098\ub9cc\uc758 \ud559\uc2b5 \ubc29\ubc95 \ucc3e\uae30"),(0,a.kt)("p",null,"\ud68c\uc0ac \uc77c\uc744 \uc9c0\uc18d\uc801\uc73c\ub85c \ud55c\ub2e4\uba74 \ud68c\uc0ac \uc77c\uc758 \uc219\ub828\uc790\uac00 \ub418\uc9c0\ub9cc, \uac1c\ubc1c \uc804\ubb38\uac00\uac00 \ub418\uc9c0 \uc54a\ub294\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc8fc\ub2c8\uc5b4 \uc77c\ub54c\ub294 \uc131\uacfc\uac00 \uc544\ub2cc \ud559\uc2b5\uc73c\ub85c!",(0,a.kt)("br",{parentName:"p"}),"\n","\uc9c0\uc18d\uc801\uc73c\ub85c \uc131\uc7a5\ud560 \uc218 \uc788\ub294 \uc0ac\ub78c\uc778\uc9c0? \uace0\ubbfc\ud558\uae30"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\ub3d9\uc6b1\ub2d8\uc774 \uadf8\ub3d9\uc548 \uc2dc\ub3c4\ud55c \ubc29\ubc95")),(0,a.kt)("p",null,"\uc0ac\uc774\ub4dc \ud504\ub85c\uc81d\ud2b8 \uc9c4\ud589\ud558\uae30 \u2192 A-Z \uad6c\ud604 \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\ucc45 \uc2a4\ud130\ub514 \u2192 \ub192\uc740 \uc644\uc8fc\uc728, \ud558\uc9c0\ub9cc \ub0b4\uac00 \ubc1c\ud45c\ud55c \uc8fc\uc81c\ub9cc \uae30\uc5b5\uc5d0 \ub0a8\ub294\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uac15\uc758 \uc900\ube44 \u2192 100% \ub0b4\uc6a9 \uc2b5\ub4dd, \ub0ae\uc740 \uc2dc\uac04 \uac00\uc131\ube44, \uac15\uc758 \uc678\uc801\uc778 \ubd80\uac00\uc791\uc5c5",(0,a.kt)("br",{parentName:"p"}),"\n","\ube14\ub85c\uadf8 \u2192 \uc628\ub77c\uc778 \ubaa8\ub450\uac00 \ub9ac\ubdf0\uc5b4, \ub3d9\ub8cc\uc640 \uacf5\uc720 \uac00\ub2a5, \ud53c\ub4dc\ubc31\uc758 \ubd80\ub044\ub7ec\uc6c0 \ud83d\ude33 "),(0,a.kt)("p",null,"\uc2e4\ud328\ud558\uac70\ub098, \uc798\ubabb \uc801\uc5b4\ub3c4 \ub0a8\ub4e4\uc758 \uc2dc\uc120\ubcf4\ub2e8 \ub098 \uc790\uc2e0\uc758 \uc131\uc7a5\uc774 \uc911\uc694\ud558\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uac00\ub2a5\ud558\uba74 \uc778\uc99d\ub418\uace0, \uc815\uc81c\ub41c \uc790\ub8cc\ub85c \uc2b5\ub4dd\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\uc5d0\uac8c \ub9de\ub294 \uac00\uc7a5 \ud6a8\uc728\uc774 \uc88b\uc740 \ud559\uc2b5 \ubc29\ubc95\uc73c\ub85c \ucc3e\uace0, \uc218\uc2dc\ub85c \uc810\uac80\ud558\uc5ec \ub354 \uc88b\uc740 \ubc29\ubc95\uc744 \ucc3e\uace0 \uc2dc\ub3c4\ud55c\ub2e4. "),(0,a.kt)("h3",{id:"\uc0c8\ub85c\uc6b4-\ud658\uacbd\uc744-\uc798-\ubc30\uc6b0\ub294-\ubc29\ubc95"},"\uc0c8\ub85c\uc6b4 \ud658\uacbd\uc744 \uc798 \ubc30\uc6b0\ub294 \ubc29\ubc95"),(0,a.kt)("p",null,"\uc2dc\uac04 > \ub3c8\uc774\uae30 \ub54c\ubb38\uc5d0 \uc2dc\uac04\uc744 \ub3c8\uc73c\ub85c \uad6c\ub9e4\ud558\uc790.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc2e0\ub8b0\ud560\ub9cc\ud55c \ubd84\uaed8 \uc9c8\ubb38 \ub610\ub294 \ucf54\ub4dc \ub9ac\ubdf0\uac00 \uac00\ub2a5\ud55c \uac15\uc758\ub97c \uad6c\ub9e4\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc5b4\ub5a4 \ud559\uc2b5 \ubc29\uc2dd, \uc5b8\uc81c \uc9d1\uc911\uc774 \uc798 \ub418\ub294\uc9c0, \uc5b4\ub5a4 \ud658\uacbd\uc5d0\uc11c \uc9d1\uc911\uc774 \uc798 \ub418\ub294\uc9c0 \ud655\uc778\ud55c\ub2e4. "),(0,a.kt)("h3",{id:"\ud559\uc2b5-\uc8fc\uc81c"},"\ud559\uc2b5 \uc8fc\uc81c"),(0,a.kt)("p",null,"\ud68c\uc0ac \uc5c5\ubb34\uc5d0\uc11c \ub9cc\ub09c \ubb38\uc81c\ub97c \uc5f0\uad6c, \uc815\ub9ac, \ud574\uacb0\ud574\uc11c \ucee4\ubba4\ub2c8\ud2f0\uc5d0 \uacf5\uc720\ud558\uace0 \ud53c\ub4dc\ubc31\uc744 \ubc1b\ub294\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc8fc\ubcc0 \ub3d9\ub8cc\ub4e4\uc5d0\uac8c \uc778\uc815\ubc1b\ub294\uac8c \uc6b0\uc120\uc774\ub2e4. "),(0,a.kt)("h3",{id:"\uc0b0\ub9cc\ud568-\uad00\ub9ac\ud558\uae30"},"\uc0b0\ub9cc\ud568 \uad00\ub9ac\ud558\uae30"),(0,a.kt)("p",null,"\ucee8\ud14d\uc2a4\ud2b8 \uc2a4\uc704\uce6d\uc774 \uc790\uc8fc \uc77c\uc5b4\ub098\uba74 \uc0b0\ub9cc\ud574\uc9c0\uae30 \ub54c\ubb38\uc5d0 \uc9d1\uc911\ud560 \uc218 \uc788\ub294 \ud658\uacbd\uc744 \uac00\uc838\uc57c \ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","ex) \uc9d1\uc911\uc774 \uc798 \ub418\ub294 \ud658\uacbd \uad6c\uc131\ud558\uae30, \ucd9c\uadfc \uc804 1~2\uc2dc\uac04 \uc9d1\uc911\ud558\uace0 \ucd9c\uadfc\ud558\uae30, \uc810\uc2ec \uc800\ub141 \uc0b0\ucc45\ud558\uae30, \uc8fc 2~3\ud68c \uc6b4\ub3d9\ud558\uae30"),(0,a.kt)("h3",{id:"\uac70\uc778\uc5d0-\uc5b4\uae68\uc704\uc5d0-\uc62c\ub77c\ud0c0\uae30"},"\uac70\uc778\uc5d0 \uc5b4\uae68\uc704\uc5d0 \uc62c\ub77c\ud0c0\uae30"),(0,a.kt)("p",null,"\ub6f0\uc5b4\ub09c \uc0ac\ub78c \uc606\uc5d0\uc11c \ubc30\uc6cc\uc57c \ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","C\ub808\ubca8, \ud14c\ud06c \ub9ac\ub4dc\uc640 \uac19\uc774 \uc77c\ud560 \uc218 \uc788\ub294 \uac83\uc740 \ud070 \uae30\ud68c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \ubc29\ubc95, \uc2e0\ub8b0 \uc790\uc0b0\uc744 \ud655\ubcf4\ud558\ub294 \ubc29\ubc95, \ubb38\ud654\ub97c \ub9cc\ub4e4\uc5b4\uac00\ub294 \ubc29\ubc95, \uacb0\uc815\uc758 \uae30\uc900\uacfc \uac19\uc740 \ubd80\ubd84\uc744 \ud559\uc2b5\ud560 \uc218 \uc788\ub2e4."),(0,a.kt)("h3",{id:"\ubcf4\uc0c1"},"\ubcf4\uc0c1"),(0,a.kt)("p",null,"\uc2dc\ub828 \ub4a4\uc5d0\ub294 \ud56d\uc0c1 \ubcf4\ubb3c\uc774 \uae30\ub2e4\ub9ac\uace0 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ubcf4\uc0c1\uc744 \ud1b5\ud574 \uafb8\uc900\ud568\uc744 \uc720\uc9c0\ud560 \uc218 \uc788\ub3c4\ub85d \ub9cc\ub4e4\uc5b4\ub77c. "),(0,a.kt)("h3",{id:"\ub0a8\uc744-\uc124\ub4dd\ud558\ub294-\ubc29\ubc95-\ubc30\uc6b0\uae30"},"\ub0a8\uc744 \uc124\ub4dd\ud558\ub294 \ubc29\ubc95 \ubc30\uc6b0\uae30"),(0,a.kt)("p",null,"\ud300\uc6d0\ub4e4\uc774 \ub9e4\ubc88 \ub0b4 \uc758\uacac\uc744 \ubc18\ub300\ud55c\ub2e4\uba74 \uc644\ubcbd\ud55c \ub17c\ub9ac\uac00 \uc911\uc694\ud55c\uac8c \uc544\ub2c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc5b4\ub5bb\uac8c \ud558\uba74 \uc2e0\ub8b0 \uc790\uc0b0\uc744 \ud655\ubcf4\ud560 \uc218 \uc788\ub294\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\ucee4\ubba4\ub2c8\ucf00\uc774\uc158, \ud611\uc5c5, \uc18c\ud504\ud2b8 \uc2a4\ud0ac\uc5d0\uc11c \ubd80\uc871\ud568\uc774 \uc788\uc73c\uba74 \uc548\ub41c\ub2e4."))}s.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9094],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>d});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function p(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var i=n.createContext({}),c=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):p(p({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(i.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,l=e.originalType,i=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),m=c(r),d=a,b=m["".concat(i,".").concat(d)]||m[d]||s[d]||l;return r?n.createElement(b,p(p({ref:t},u),{},{components:r})):n.createElement(b,p({ref:t},u))}));function d(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=r.length,p=new Array(l);p[0]=m;var o={};for(var i in t)hasOwnProperty.call(t,i)&&(o[i]=t[i]);o.originalType=e,o.mdxType="string"==typeof e?e:a,p[1]=o;for(var c=2;c{r.r(t),r.d(t,{assets:()=>i,contentTitle:()=>p,default:()=>s,frontMatter:()=>l,metadata:()=>o,toc:()=>c});var n=r(87462),a=(r(67294),r(3905));const l={title:"\uac74\uac15\ud558\uac8c \ub098\uc544\uc9c0\uae30",slug:"/etc/healthful-growth",tags:["etc"]},p=void 0,o={unversionedId:"\uae30\ud0c0/\uac74\uac15\ud558\uac8c_\ub098\uc544\uc9c0\uae30",id:"\uae30\ud0c0/\uac74\uac15\ud558\uac8c_\ub098\uc544\uc9c0\uae30",title:"\uac74\uac15\ud558\uac8c \ub098\uc544\uc9c0\uae30",description:"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 2023\ub144 4\uc6d4 19\uc77c",source:"@site/docs/\uae30\ud0c0/\uac74\uac15\ud558\uac8c_\ub098\uc544\uc9c0\uae30.mdx",sourceDirName:"\uae30\ud0c0",slug:"/etc/healthful-growth",permalink:"/docs/etc/healthful-growth",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uae30\ud0c0/\uac74\uac15\ud558\uac8c_\ub098\uc544\uc9c0\uae30.mdx",tags:[{label:"etc",permalink:"/docs/tags/etc"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\uac74\uac15\ud558\uac8c \ub098\uc544\uc9c0\uae30",slug:"/etc/healthful-growth",tags:["etc"]},sidebar:"tutorialSidebar",previous:{title:"\ubb38\uc11c",permalink:"/docs/"},next:{title:"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",permalink:"/docs/etc/develop-with-spring"}},i={},c=[{value:"\uc790\uc874\uac10 \uae30\ub465 \ub9cc\ub4e4\uae30",id:"\uc790\uc874\uac10-\uae30\ub465-\ub9cc\ub4e4\uae30",level:3},{value:"\ub098\ub9cc\uc758 \ud559\uc2b5 \ubc29\ubc95 \ucc3e\uae30",id:"\ub098\ub9cc\uc758-\ud559\uc2b5-\ubc29\ubc95-\ucc3e\uae30",level:3},{value:"\uc0c8\ub85c\uc6b4 \ud658\uacbd\uc744 \uc798 \ubc30\uc6b0\ub294 \ubc29\ubc95",id:"\uc0c8\ub85c\uc6b4-\ud658\uacbd\uc744-\uc798-\ubc30\uc6b0\ub294-\ubc29\ubc95",level:3},{value:"\ud559\uc2b5 \uc8fc\uc81c",id:"\ud559\uc2b5-\uc8fc\uc81c",level:3},{value:"\uc0b0\ub9cc\ud568 \uad00\ub9ac\ud558\uae30",id:"\uc0b0\ub9cc\ud568-\uad00\ub9ac\ud558\uae30",level:3},{value:"\uac70\uc778\uc5d0 \uc5b4\uae68\uc704\uc5d0 \uc62c\ub77c\ud0c0\uae30",id:"\uac70\uc778\uc5d0-\uc5b4\uae68\uc704\uc5d0-\uc62c\ub77c\ud0c0\uae30",level:3},{value:"\ubcf4\uc0c1",id:"\ubcf4\uc0c1",level:3},{value:"\ub0a8\uc744 \uc124\ub4dd\ud558\ub294 \ubc29\ubc95 \ubc30\uc6b0\uae30",id:"\ub0a8\uc744-\uc124\ub4dd\ud558\ub294-\ubc29\ubc95-\ubc30\uc6b0\uae30",level:3}],u={toc:c};function s(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 2023\ub144 4\uc6d4 19\uc77c",(0,a.kt)("br",{parentName:"p"}),"\n","\uc774\ub3d9\uc6b1\ub2d8 \ud2b9\uac15"),(0,a.kt)("h3",{id:"\uc790\uc874\uac10-\uae30\ub465-\ub9cc\ub4e4\uae30"},"\uc790\uc874\uac10 \uae30\ub465 \ub9cc\ub4e4\uae30"),(0,a.kt)("p",null,"\ub6f0\uc5b4\ub09c \ub3d9\ub8cc, \uc0c8\ub85c\uc6b4 \ud658\uacbd \uadf8\ub9ac\uace0 \ud504\ub85c\uc81d\ud2b8\ub97c \uc2e4\ud328\ud558\uba74\uc11c \uc790\uc874\uac10\uc774 \ub5a8\uc5b4\uc9c8 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc790\uc874\uac10\uc774 \ubb34\ub108\uc9c0\uc9c0 \uc54a\ub3c4\ub85d \ub098\ub97c \uc9c0\ud0f1\ud560 \uc218 \uc788\ub294 \uae30\ub465\uc774 \ud544\uc694\ud558\ub2e4. (\ud55c \uac1c\uac00 \uc544\ub2cc \uc5ec\ub7ec \uac1c) "),(0,a.kt)("h3",{id:"\ub098\ub9cc\uc758-\ud559\uc2b5-\ubc29\ubc95-\ucc3e\uae30"},"\ub098\ub9cc\uc758 \ud559\uc2b5 \ubc29\ubc95 \ucc3e\uae30"),(0,a.kt)("p",null,"\ud68c\uc0ac \uc77c\uc744 \uc9c0\uc18d\uc801\uc73c\ub85c \ud55c\ub2e4\uba74 \ud68c\uc0ac \uc77c\uc758 \uc219\ub828\uc790\uac00 \ub418\uc9c0\ub9cc, \uac1c\ubc1c \uc804\ubb38\uac00\uac00 \ub418\uc9c0 \uc54a\ub294\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc8fc\ub2c8\uc5b4 \uc77c\ub54c\ub294 \uc131\uacfc\uac00 \uc544\ub2cc \ud559\uc2b5\uc73c\ub85c!",(0,a.kt)("br",{parentName:"p"}),"\n","\uc9c0\uc18d\uc801\uc73c\ub85c \uc131\uc7a5\ud560 \uc218 \uc788\ub294 \uc0ac\ub78c\uc778\uc9c0? \uace0\ubbfc\ud558\uae30"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\ub3d9\uc6b1\ub2d8\uc774 \uadf8\ub3d9\uc548 \uc2dc\ub3c4\ud55c \ubc29\ubc95")),(0,a.kt)("p",null,"\uc0ac\uc774\ub4dc \ud504\ub85c\uc81d\ud2b8 \uc9c4\ud589\ud558\uae30 \u2192 A-Z \uad6c\ud604 \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\ucc45 \uc2a4\ud130\ub514 \u2192 \ub192\uc740 \uc644\uc8fc\uc728, \ud558\uc9c0\ub9cc \ub0b4\uac00 \ubc1c\ud45c\ud55c \uc8fc\uc81c\ub9cc \uae30\uc5b5\uc5d0 \ub0a8\ub294\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uac15\uc758 \uc900\ube44 \u2192 100% \ub0b4\uc6a9 \uc2b5\ub4dd, \ub0ae\uc740 \uc2dc\uac04 \uac00\uc131\ube44, \uac15\uc758 \uc678\uc801\uc778 \ubd80\uac00\uc791\uc5c5",(0,a.kt)("br",{parentName:"p"}),"\n","\ube14\ub85c\uadf8 \u2192 \uc628\ub77c\uc778 \ubaa8\ub450\uac00 \ub9ac\ubdf0\uc5b4, \ub3d9\ub8cc\uc640 \uacf5\uc720 \uac00\ub2a5, \ud53c\ub4dc\ubc31\uc758 \ubd80\ub044\ub7ec\uc6c0 \ud83d\ude33 "),(0,a.kt)("p",null,"\uc2e4\ud328\ud558\uac70\ub098, \uc798\ubabb \uc801\uc5b4\ub3c4 \ub0a8\ub4e4\uc758 \uc2dc\uc120\ubcf4\ub2e8 \ub098 \uc790\uc2e0\uc758 \uc131\uc7a5\uc774 \uc911\uc694\ud558\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uac00\ub2a5\ud558\uba74 \uc778\uc99d\ub418\uace0, \uc815\uc81c\ub41c \uc790\ub8cc\ub85c \uc2b5\ub4dd\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\uc5d0\uac8c \ub9de\ub294 \uac00\uc7a5 \ud6a8\uc728\uc774 \uc88b\uc740 \ud559\uc2b5 \ubc29\ubc95\uc73c\ub85c \ucc3e\uace0, \uc218\uc2dc\ub85c \uc810\uac80\ud558\uc5ec \ub354 \uc88b\uc740 \ubc29\ubc95\uc744 \ucc3e\uace0 \uc2dc\ub3c4\ud55c\ub2e4. "),(0,a.kt)("h3",{id:"\uc0c8\ub85c\uc6b4-\ud658\uacbd\uc744-\uc798-\ubc30\uc6b0\ub294-\ubc29\ubc95"},"\uc0c8\ub85c\uc6b4 \ud658\uacbd\uc744 \uc798 \ubc30\uc6b0\ub294 \ubc29\ubc95"),(0,a.kt)("p",null,"\uc2dc\uac04 > \ub3c8\uc774\uae30 \ub54c\ubb38\uc5d0 \uc2dc\uac04\uc744 \ub3c8\uc73c\ub85c \uad6c\ub9e4\ud558\uc790.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc2e0\ub8b0\ud560\ub9cc\ud55c \ubd84\uaed8 \uc9c8\ubb38 \ub610\ub294 \ucf54\ub4dc \ub9ac\ubdf0\uac00 \uac00\ub2a5\ud55c \uac15\uc758\ub97c \uad6c\ub9e4\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc5b4\ub5a4 \ud559\uc2b5 \ubc29\uc2dd, \uc5b8\uc81c \uc9d1\uc911\uc774 \uc798 \ub418\ub294\uc9c0, \uc5b4\ub5a4 \ud658\uacbd\uc5d0\uc11c \uc9d1\uc911\uc774 \uc798 \ub418\ub294\uc9c0 \ud655\uc778\ud55c\ub2e4. "),(0,a.kt)("h3",{id:"\ud559\uc2b5-\uc8fc\uc81c"},"\ud559\uc2b5 \uc8fc\uc81c"),(0,a.kt)("p",null,"\ud68c\uc0ac \uc5c5\ubb34\uc5d0\uc11c \ub9cc\ub09c \ubb38\uc81c\ub97c \uc5f0\uad6c, \uc815\ub9ac, \ud574\uacb0\ud574\uc11c \ucee4\ubba4\ub2c8\ud2f0\uc5d0 \uacf5\uc720\ud558\uace0 \ud53c\ub4dc\ubc31\uc744 \ubc1b\ub294\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc8fc\ubcc0 \ub3d9\ub8cc\ub4e4\uc5d0\uac8c \uc778\uc815\ubc1b\ub294\uac8c \uc6b0\uc120\uc774\ub2e4. "),(0,a.kt)("h3",{id:"\uc0b0\ub9cc\ud568-\uad00\ub9ac\ud558\uae30"},"\uc0b0\ub9cc\ud568 \uad00\ub9ac\ud558\uae30"),(0,a.kt)("p",null,"\ucee8\ud14d\uc2a4\ud2b8 \uc2a4\uc704\uce6d\uc774 \uc790\uc8fc \uc77c\uc5b4\ub098\uba74 \uc0b0\ub9cc\ud574\uc9c0\uae30 \ub54c\ubb38\uc5d0 \uc9d1\uc911\ud560 \uc218 \uc788\ub294 \ud658\uacbd\uc744 \uac00\uc838\uc57c \ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","ex) \uc9d1\uc911\uc774 \uc798 \ub418\ub294 \ud658\uacbd \uad6c\uc131\ud558\uae30, \ucd9c\uadfc \uc804 1~2\uc2dc\uac04 \uc9d1\uc911\ud558\uace0 \ucd9c\uadfc\ud558\uae30, \uc810\uc2ec \uc800\ub141 \uc0b0\ucc45\ud558\uae30, \uc8fc 2~3\ud68c \uc6b4\ub3d9\ud558\uae30"),(0,a.kt)("h3",{id:"\uac70\uc778\uc5d0-\uc5b4\uae68\uc704\uc5d0-\uc62c\ub77c\ud0c0\uae30"},"\uac70\uc778\uc5d0 \uc5b4\uae68\uc704\uc5d0 \uc62c\ub77c\ud0c0\uae30"),(0,a.kt)("p",null,"\ub6f0\uc5b4\ub09c \uc0ac\ub78c \uc606\uc5d0\uc11c \ubc30\uc6cc\uc57c \ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","C\ub808\ubca8, \ud14c\ud06c \ub9ac\ub4dc\uc640 \uac19\uc774 \uc77c\ud560 \uc218 \uc788\ub294 \uac83\uc740 \ud070 \uae30\ud68c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \ubc29\ubc95, \uc2e0\ub8b0 \uc790\uc0b0\uc744 \ud655\ubcf4\ud558\ub294 \ubc29\ubc95, \ubb38\ud654\ub97c \ub9cc\ub4e4\uc5b4\uac00\ub294 \ubc29\ubc95, \uacb0\uc815\uc758 \uae30\uc900\uacfc \uac19\uc740 \ubd80\ubd84\uc744 \ud559\uc2b5\ud560 \uc218 \uc788\ub2e4."),(0,a.kt)("h3",{id:"\ubcf4\uc0c1"},"\ubcf4\uc0c1"),(0,a.kt)("p",null,"\uc2dc\ub828 \ub4a4\uc5d0\ub294 \ud56d\uc0c1 \ubcf4\ubb3c\uc774 \uae30\ub2e4\ub9ac\uace0 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ubcf4\uc0c1\uc744 \ud1b5\ud574 \uafb8\uc900\ud568\uc744 \uc720\uc9c0\ud560 \uc218 \uc788\ub3c4\ub85d \ub9cc\ub4e4\uc5b4\ub77c. "),(0,a.kt)("h3",{id:"\ub0a8\uc744-\uc124\ub4dd\ud558\ub294-\ubc29\ubc95-\ubc30\uc6b0\uae30"},"\ub0a8\uc744 \uc124\ub4dd\ud558\ub294 \ubc29\ubc95 \ubc30\uc6b0\uae30"),(0,a.kt)("p",null,"\ud300\uc6d0\ub4e4\uc774 \ub9e4\ubc88 \ub0b4 \uc758\uacac\uc744 \ubc18\ub300\ud55c\ub2e4\uba74 \uc644\ubcbd\ud55c \ub17c\ub9ac\uac00 \uc911\uc694\ud55c\uac8c \uc544\ub2c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc5b4\ub5bb\uac8c \ud558\uba74 \uc2e0\ub8b0 \uc790\uc0b0\uc744 \ud655\ubcf4\ud560 \uc218 \uc788\ub294\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\ucee4\ubba4\ub2c8\ucf00\uc774\uc158, \ud611\uc5c5, \uc18c\ud504\ud2b8 \uc2a4\ud0ac\uc5d0\uc11c \ubd80\uc871\ud568\uc774 \uc788\uc73c\uba74 \uc548\ub41c\ub2e4."))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/48bd1d32.ff33e518.js b/assets/js/48bd1d32.00513cdb.js similarity index 98% rename from assets/js/48bd1d32.ff33e518.js rename to assets/js/48bd1d32.00513cdb.js index 673c4ef5c..85218fe44 100644 --- a/assets/js/48bd1d32.ff33e518.js +++ b/assets/js/48bd1d32.00513cdb.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9563],{3905:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>d});var n=r(67294);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},p=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=c(r),d=i,f=m["".concat(s,".").concat(d)]||m[d]||u[d]||a;return r?n.createElement(f,o(o({ref:t},p),{},{components:r})):n.createElement(f,o({ref:t},p))}));function d(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=r.length,o=new Array(a);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var n=r(87462),i=(r(67294),r(3905));const a={title:"TDD heuristics",slug:"/test/heuristics",tags:["test"]},o=void 0,l={unversionedId:"\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\uc8fc\ub3c4_\uac1c\ubc1c_\uaddc\uce59",id:"\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\uc8fc\ub3c4_\uac1c\ubc1c_\uaddc\uce59",title:"TDD heuristics",description:"TDD heuristics",source:"@site/docs/\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\uc8fc\ub3c4_\uac1c\ubc1c_\uaddc\uce59.mdx",sourceDirName:"\ud14c\uc2a4\ud2b8",slug:"/test/heuristics",permalink:"/docs/test/heuristics",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\uc8fc\ub3c4_\uac1c\ubc1c_\uaddc\uce59.mdx",tags:[{label:"test",permalink:"/docs/tags/test"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"TDD heuristics",slug:"/test/heuristics",tags:["test"]},sidebar:"tutorialSidebar",previous:{title:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8",permalink:"/docs/test/stairstep"},next:{title:"\ud14c\uc2a4\ud2b8 \ucf54\ub4dc\uac00 \uc8fc\ub294 \ud61c\ud0dd",permalink:"/docs/test/benefit"}},s={},c=[{value:"TDD heuristics",id:"tdd-heuristics",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],p={toc:c};function u(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h3",{id:"tdd-heuristics"},"TDD heuristics"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"\uc5ec\ub7ec\ubd84\uc774 \uc791\uc131\ud558\uace0 \uc2f6\uc740 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\ub3c4\ub85d \ub9cc\ub4dc\ub294 \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\uc2e4\ud328\uc2dc\ucf1c\ub77c. \ud1b5\uacfc\uc2dc\ucf1c\ub77c. \uadf8\ub9ac\uace0 \uc815\ub9ac\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ucd5c\uc0c1\uc758 \uacb0\uacfc\ub97c \ucd94\uad6c\ud558\uc9c0 \ub9d0\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\uc2e4\ud328\ud558\ub294 \uac00\uc7a5 \uac04\ub2e8\ud558\uace0, \uac00\uc7a5 \uad6c\uccb4\uc801\uc774\uba70, \uac00\uc7a5 \ud1f4\ud654\ud55c \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\uac00\ub2a5\ud558\uba74 \uc77c\ubc18\ud654\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ucf54\ub4dc\uac00 \ud2c0\ub838\ub2e4\uace0 \ub290\uaef4\uc9c0\uba74 \uc7a0\uc2dc \uba48\ucdb0\uc11c \uc124\uacc4\ub97c \uace0\uccd0\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ub354 \ubcf5\uc7a1\ud55c \ub2e4\uc74c \uacbd\uc6b0\ub85c \ub118\uc5b4\uac00\uae30 \uc804, \uc9c0\uae08 \ub2e4\ub8e8\uace0 \uc788\ub294 \ub354 \ub2e8\uc21c\ud55c \uacbd\uc6b0\ub97c \ubaa8\uc870\ub9ac \ud14c\uc2a4\ud2b8\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud604\uc7ac \ud14c\uc2a4\ud2b8\ub97c \ud1b5\uacfc\uc2dc\ud0a4\uae30 \uc704\ud574 \ub108\ubb34 \ub9ce\uc740 \uad6c\ud604\uc744 \ud574\uc57c \ud55c\ub2e4\uba74, \ud14c\uc2a4\ud2b8\ub97c \uc9c0\uc6b0\uace0 \ub354 \uc27d\uac8c \ud1b5\uacfc\ud560 \uc218 \uc788\ub294 \ub354 \ub2e8\uc21c\ud55c \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud14c\uc2a4\ud2b8 \uacf5\uac04(test space)\uc744 \uc804\ubd80 \ud3ec\uad04\ud558\ub294 \uc2e0\uc911\ud558\uace0 \uc810\uc9c4\uc801\uc778 \ud328\ud134\uc744 \ub530\ub974\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud544\uc694 \uc5c6\ub294 \uac83\uc744 \uc5ec\ub7ec\ubd84\uc758 \ud14c\uc2a4\ud2b8\uc5d0 \ub123\uc9c0 \ub9d0\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud14c\uc2a4\ud2b8\uc5d0 \uc2e4\uc81c \uc11c\ube44\uc2a4 \ub370\uc774\ud130\ub97c \uc0ac\uc6a9\ud558\uc9c0 \ub9d0\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud14c\uc2a4\ud2b8 \uad6c\uc870\ub97c \uc81c\ud488 \ucf54\ub4dc \uad6c\uc870\ub85c\ubd80\ud130 \ubd84\ub9ac\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud14c\uc2a4\ud2b8\uac00 \uad6c\uccb4\uc801(specific)\uc774 \ub420\uc218\ub85d \ucf54\ub4dc\ub294 \uc77c\ubc18\uc801(generic)\uc774 \ub41c\ub2e4."),(0,i.kt)("li",{parentName:"ol"},"\ubcc0\ud658\uc744 \uc801\uc6a9\ud55c \uacb0\uacfc \ucd5c\uc801\uc774 \uc544\ub2cc \ud574\ub2f5\uc5d0 \ub3c4\ub2ec\ud588\ub2e4\uba74 \ub2e4\ub978 \ubcc0\ud658\uc744 \uc2dc\ub3c4\ud574\ubcf4\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ub514\ubc84\uac70 \uc0ac\uc6a9\uc744 \ud53c\ud558\ub77c")),(0,i.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,i.kt)("p",null,"\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc7a5\uc778 \uc815\uc2e0 \uc774\uc57c\uae30, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4 p.44 ~ p.209"))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9563],{3905:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>d});var n=r(67294);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},p=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=c(r),d=i,f=m["".concat(s,".").concat(d)]||m[d]||u[d]||a;return r?n.createElement(f,o(o({ref:t},p),{},{components:r})):n.createElement(f,o({ref:t},p))}));function d(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=r.length,o=new Array(a);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var n=r(87462),i=(r(67294),r(3905));const a={title:"TDD heuristics",slug:"/test/heuristics",tags:["test"]},o=void 0,l={unversionedId:"\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\uc8fc\ub3c4_\uac1c\ubc1c_\uaddc\uce59",id:"\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\uc8fc\ub3c4_\uac1c\ubc1c_\uaddc\uce59",title:"TDD heuristics",description:"TDD heuristics",source:"@site/docs/\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\uc8fc\ub3c4_\uac1c\ubc1c_\uaddc\uce59.mdx",sourceDirName:"\ud14c\uc2a4\ud2b8",slug:"/test/heuristics",permalink:"/docs/test/heuristics",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\uc8fc\ub3c4_\uac1c\ubc1c_\uaddc\uce59.mdx",tags:[{label:"test",permalink:"/docs/tags/test"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"TDD heuristics",slug:"/test/heuristics",tags:["test"]},sidebar:"tutorialSidebar",previous:{title:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8",permalink:"/docs/test/stairstep"},next:{title:"\ud14c\uc2a4\ud2b8 \ucf54\ub4dc\uac00 \uc8fc\ub294 \ud61c\ud0dd",permalink:"/docs/test/benefit"}},s={},c=[{value:"TDD heuristics",id:"tdd-heuristics",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],p={toc:c};function u(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h3",{id:"tdd-heuristics"},"TDD heuristics"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"\uc5ec\ub7ec\ubd84\uc774 \uc791\uc131\ud558\uace0 \uc2f6\uc740 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\ub3c4\ub85d \ub9cc\ub4dc\ub294 \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\uc2e4\ud328\uc2dc\ucf1c\ub77c. \ud1b5\uacfc\uc2dc\ucf1c\ub77c. \uadf8\ub9ac\uace0 \uc815\ub9ac\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ucd5c\uc0c1\uc758 \uacb0\uacfc\ub97c \ucd94\uad6c\ud558\uc9c0 \ub9d0\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\uc2e4\ud328\ud558\ub294 \uac00\uc7a5 \uac04\ub2e8\ud558\uace0, \uac00\uc7a5 \uad6c\uccb4\uc801\uc774\uba70, \uac00\uc7a5 \ud1f4\ud654\ud55c \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\uac00\ub2a5\ud558\uba74 \uc77c\ubc18\ud654\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ucf54\ub4dc\uac00 \ud2c0\ub838\ub2e4\uace0 \ub290\uaef4\uc9c0\uba74 \uc7a0\uc2dc \uba48\ucdb0\uc11c \uc124\uacc4\ub97c \uace0\uccd0\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ub354 \ubcf5\uc7a1\ud55c \ub2e4\uc74c \uacbd\uc6b0\ub85c \ub118\uc5b4\uac00\uae30 \uc804, \uc9c0\uae08 \ub2e4\ub8e8\uace0 \uc788\ub294 \ub354 \ub2e8\uc21c\ud55c \uacbd\uc6b0\ub97c \ubaa8\uc870\ub9ac \ud14c\uc2a4\ud2b8\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud604\uc7ac \ud14c\uc2a4\ud2b8\ub97c \ud1b5\uacfc\uc2dc\ud0a4\uae30 \uc704\ud574 \ub108\ubb34 \ub9ce\uc740 \uad6c\ud604\uc744 \ud574\uc57c \ud55c\ub2e4\uba74, \ud14c\uc2a4\ud2b8\ub97c \uc9c0\uc6b0\uace0 \ub354 \uc27d\uac8c \ud1b5\uacfc\ud560 \uc218 \uc788\ub294 \ub354 \ub2e8\uc21c\ud55c \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud14c\uc2a4\ud2b8 \uacf5\uac04(test space)\uc744 \uc804\ubd80 \ud3ec\uad04\ud558\ub294 \uc2e0\uc911\ud558\uace0 \uc810\uc9c4\uc801\uc778 \ud328\ud134\uc744 \ub530\ub974\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud544\uc694 \uc5c6\ub294 \uac83\uc744 \uc5ec\ub7ec\ubd84\uc758 \ud14c\uc2a4\ud2b8\uc5d0 \ub123\uc9c0 \ub9d0\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud14c\uc2a4\ud2b8\uc5d0 \uc2e4\uc81c \uc11c\ube44\uc2a4 \ub370\uc774\ud130\ub97c \uc0ac\uc6a9\ud558\uc9c0 \ub9d0\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud14c\uc2a4\ud2b8 \uad6c\uc870\ub97c \uc81c\ud488 \ucf54\ub4dc \uad6c\uc870\ub85c\ubd80\ud130 \ubd84\ub9ac\ud558\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ud14c\uc2a4\ud2b8\uac00 \uad6c\uccb4\uc801(specific)\uc774 \ub420\uc218\ub85d \ucf54\ub4dc\ub294 \uc77c\ubc18\uc801(generic)\uc774 \ub41c\ub2e4."),(0,i.kt)("li",{parentName:"ol"},"\ubcc0\ud658\uc744 \uc801\uc6a9\ud55c \uacb0\uacfc \ucd5c\uc801\uc774 \uc544\ub2cc \ud574\ub2f5\uc5d0 \ub3c4\ub2ec\ud588\ub2e4\uba74 \ub2e4\ub978 \ubcc0\ud658\uc744 \uc2dc\ub3c4\ud574\ubcf4\ub77c."),(0,i.kt)("li",{parentName:"ol"},"\ub514\ubc84\uac70 \uc0ac\uc6a9\uc744 \ud53c\ud558\ub77c")),(0,i.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,i.kt)("p",null,"\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc7a5\uc778 \uc815\uc2e0 \uc774\uc57c\uae30, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4 p.44 ~ p.209"))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/494882d1.c40a80ba.js b/assets/js/494882d1.73f8d425.js similarity index 57% rename from assets/js/494882d1.c40a80ba.js rename to assets/js/494882d1.73f8d425.js index c230d67f5..f0559307e 100644 --- a/assets/js/494882d1.c40a80ba.js +++ b/assets/js/494882d1.73f8d425.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[4471],{2098:e=>{e.exports=JSON.parse('{"permalink":"/page/37","page":37,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/36","nextPage":"/page/38","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[4471],{2098:e=>{e.exports=JSON.parse('{"permalink":"/page/37","page":37,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/36","nextPage":"/page/38","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/4959fc42.c6328d60.js b/assets/js/4959fc42.6cdbaa8f.js similarity index 57% rename from assets/js/4959fc42.c6328d60.js rename to assets/js/4959fc42.6cdbaa8f.js index eefbec2a0..e0f24f779 100644 --- a/assets/js/4959fc42.c6328d60.js +++ b/assets/js/4959fc42.6cdbaa8f.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[240],{80897:e=>{e.exports=JSON.parse('{"permalink":"/page/14","page":14,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/13","nextPage":"/page/15","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[240],{80897:e=>{e.exports=JSON.parse('{"permalink":"/page/14","page":14,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/13","nextPage":"/page/15","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/53e4509a.c8f25e5c.js b/assets/js/53e4509a.c8f25e5c.js new file mode 100644 index 000000000..7b825835e --- /dev/null +++ b/assets/js/53e4509a.c8f25e5c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[4722],{32259:e=>{e.exports=JSON.parse('{"permalink":"/page/46","page":46,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/45","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/54cb095e.20ac0662.js b/assets/js/54cb095e.a3fc1b31.js similarity index 57% rename from assets/js/54cb095e.20ac0662.js rename to assets/js/54cb095e.a3fc1b31.js index 9bbaaacdf..91514cc9f 100644 --- a/assets/js/54cb095e.20ac0662.js +++ b/assets/js/54cb095e.a3fc1b31.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7009],{95159:e=>{e.exports=JSON.parse('{"permalink":"/page/26","page":26,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/25","nextPage":"/page/27","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7009],{95159:e=>{e.exports=JSON.parse('{"permalink":"/page/26","page":26,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/25","nextPage":"/page/27","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/5f81b25c.fcb5e683.js b/assets/js/5f81b25c.8a3fc417.js similarity index 57% rename from assets/js/5f81b25c.fcb5e683.js rename to assets/js/5f81b25c.8a3fc417.js index c649c902d..f17176c64 100644 --- a/assets/js/5f81b25c.fcb5e683.js +++ b/assets/js/5f81b25c.8a3fc417.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[4889],{29492:e=>{e.exports=JSON.parse('{"permalink":"/page/27","page":27,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/26","nextPage":"/page/28","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[4889],{29492:e=>{e.exports=JSON.parse('{"permalink":"/page/27","page":27,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/26","nextPage":"/page/28","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/6093f82b.e76b4eb3.js b/assets/js/6093f82b.729560f0.js similarity index 57% rename from assets/js/6093f82b.e76b4eb3.js rename to assets/js/6093f82b.729560f0.js index 874c2a969..467138163 100644 --- a/assets/js/6093f82b.e76b4eb3.js +++ b/assets/js/6093f82b.729560f0.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6017],{30708:e=>{e.exports=JSON.parse('{"permalink":"/page/9","page":9,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/8","nextPage":"/page/10","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6017],{30708:e=>{e.exports=JSON.parse('{"permalink":"/page/9","page":9,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/8","nextPage":"/page/10","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/635a92d5.aca340d5.js b/assets/js/635a92d5.eeaf5e2b.js similarity index 57% rename from assets/js/635a92d5.aca340d5.js rename to assets/js/635a92d5.eeaf5e2b.js index cfcdfe276..5e9a604d0 100644 --- a/assets/js/635a92d5.aca340d5.js +++ b/assets/js/635a92d5.eeaf5e2b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7891],{72126:e=>{e.exports=JSON.parse('{"permalink":"/page/24","page":24,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/23","nextPage":"/page/25","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7891],{72126:e=>{e.exports=JSON.parse('{"permalink":"/page/24","page":24,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/23","nextPage":"/page/25","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/64868a43.fd9f94bb.js b/assets/js/64868a43.2f5e5632.js similarity index 57% rename from assets/js/64868a43.fd9f94bb.js rename to assets/js/64868a43.2f5e5632.js index 3bbd3feab..4dfbdb14c 100644 --- a/assets/js/64868a43.fd9f94bb.js +++ b/assets/js/64868a43.2f5e5632.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[1501],{33159:e=>{e.exports=JSON.parse('{"permalink":"/page/39","page":39,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/38","nextPage":"/page/40","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[1501],{33159:e=>{e.exports=JSON.parse('{"permalink":"/page/39","page":39,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/38","nextPage":"/page/40","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/69c28c32.535e44e8.js b/assets/js/69c28c32.4b72ca56.js similarity index 57% rename from assets/js/69c28c32.535e44e8.js rename to assets/js/69c28c32.4b72ca56.js index ba5f701d1..c82b5938f 100644 --- a/assets/js/69c28c32.535e44e8.js +++ b/assets/js/69c28c32.4b72ca56.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[1065],{99263:e=>{e.exports=JSON.parse('{"permalink":"/page/36","page":36,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/35","nextPage":"/page/37","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[1065],{99263:e=>{e.exports=JSON.parse('{"permalink":"/page/36","page":36,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/35","nextPage":"/page/37","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/6d4355d3.3a49ae35.js b/assets/js/6d4355d3.3a49ae35.js new file mode 100644 index 000000000..adef4f370 --- /dev/null +++ b/assets/js/6d4355d3.3a49ae35.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3122],{37438:s=>{s.exports=JSON.parse('{"label":"async","permalink":"/tags/async","allTagsPath":"/tags","count":2}')}}]); \ No newline at end of file diff --git a/assets/js/6dd1c948.4d90960d.js b/assets/js/6dd1c948.5efd0dca.js similarity index 57% rename from assets/js/6dd1c948.4d90960d.js rename to assets/js/6dd1c948.5efd0dca.js index 4abcf2355..eb158b572 100644 --- a/assets/js/6dd1c948.4d90960d.js +++ b/assets/js/6dd1c948.5efd0dca.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7064],{76376:e=>{e.exports=JSON.parse('{"permalink":"/page/34","page":34,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/33","nextPage":"/page/35","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7064],{76376:e=>{e.exports=JSON.parse('{"permalink":"/page/34","page":34,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/33","nextPage":"/page/35","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/70275fcd.2c549c8b.js b/assets/js/70275fcd.437d81af.js similarity index 57% rename from assets/js/70275fcd.2c549c8b.js rename to assets/js/70275fcd.437d81af.js index 763e51c48..cdda57433 100644 --- a/assets/js/70275fcd.2c549c8b.js +++ b/assets/js/70275fcd.437d81af.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7412],{81191:e=>{e.exports=JSON.parse('{"permalink":"/page/42","page":42,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/41","nextPage":"/page/43","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7412],{81191:e=>{e.exports=JSON.parse('{"permalink":"/page/42","page":42,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/41","nextPage":"/page/43","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/754fb852.5bbaace5.js b/assets/js/754fb852.26719bd8.js similarity index 57% rename from assets/js/754fb852.5bbaace5.js rename to assets/js/754fb852.26719bd8.js index d407f6c16..5c0ee1ad9 100644 --- a/assets/js/754fb852.5bbaace5.js +++ b/assets/js/754fb852.26719bd8.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[988],{38242:e=>{e.exports=JSON.parse('{"permalink":"/page/32","page":32,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/31","nextPage":"/page/33","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[988],{38242:e=>{e.exports=JSON.parse('{"permalink":"/page/32","page":32,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/31","nextPage":"/page/33","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/7762a24e.cee52218.js b/assets/js/7762a24e.a15ab1a0.js similarity index 57% rename from assets/js/7762a24e.cee52218.js rename to assets/js/7762a24e.a15ab1a0.js index 7ff9bd627..144439a1c 100644 --- a/assets/js/7762a24e.cee52218.js +++ b/assets/js/7762a24e.a15ab1a0.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2753],{55095:e=>{e.exports=JSON.parse('{"permalink":"/page/4","page":4,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/3","nextPage":"/page/5","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2753],{55095:e=>{e.exports=JSON.parse('{"permalink":"/page/4","page":4,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/3","nextPage":"/page/5","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/7af1d52f.47b6475a.js b/assets/js/7af1d52f.c77778a9.js similarity index 57% rename from assets/js/7af1d52f.47b6475a.js rename to assets/js/7af1d52f.c77778a9.js index 6f8740315..1c9e38cd4 100644 --- a/assets/js/7af1d52f.47b6475a.js +++ b/assets/js/7af1d52f.c77778a9.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2334],{59565:e=>{e.exports=JSON.parse('{"permalink":"/page/6","page":6,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/5","nextPage":"/page/7","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2334],{59565:e=>{e.exports=JSON.parse('{"permalink":"/page/6","page":6,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/5","nextPage":"/page/7","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/80960b4b.abe732c9.js b/assets/js/80960b4b.4272e931.js similarity index 57% rename from assets/js/80960b4b.abe732c9.js rename to assets/js/80960b4b.4272e931.js index 419079326..b10536512 100644 --- a/assets/js/80960b4b.abe732c9.js +++ b/assets/js/80960b4b.4272e931.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7599],{28386:e=>{e.exports=JSON.parse('{"permalink":"/page/21","page":21,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/20","nextPage":"/page/22","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7599],{28386:e=>{e.exports=JSON.parse('{"permalink":"/page/21","page":21,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/20","nextPage":"/page/22","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/814f3328.11618e69.js b/assets/js/814f3328.11618e69.js new file mode 100644 index 000000000..64d71caca --- /dev/null +++ b/assets/js/814f3328.11618e69.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2535],{45641:e=>{e.exports=JSON.parse('{"title":"Recent posts","items":[{"title":"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac","permalink":"/async-exception"},{"title":"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0","permalink":"/tomcat-retrospective"},{"title":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958","permalink":"/performance-test-type"},{"title":"DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30","permalink":"/db-replication"},{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 3 \ud68c\uace0","permalink":"/woowacourse-level3-retrospective"}]}')}}]); \ No newline at end of file diff --git a/assets/js/814f3328.b3fcd2fa.js b/assets/js/814f3328.b3fcd2fa.js deleted file mode 100644 index 980903fda..000000000 --- a/assets/js/814f3328.b3fcd2fa.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2535],{45641:e=>{e.exports=JSON.parse('{"title":"Recent posts","items":[{"title":"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0","permalink":"/tomcat-retrospective"},{"title":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958","permalink":"/performance-test-type"},{"title":"DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30","permalink":"/db-replication"},{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 3 \ud68c\uace0","permalink":"/woowacourse-level3-retrospective"},{"title":"CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131","permalink":"/cloudwatch"}]}')}}]); \ No newline at end of file diff --git a/assets/js/87070fc3.fa990a2e.js b/assets/js/87070fc3.0f052f4b.js similarity index 99% rename from assets/js/87070fc3.fa990a2e.js rename to assets/js/87070fc3.0f052f4b.js index db3771a9d..f90809d21 100644 --- a/assets/js/87070fc3.fa990a2e.js +++ b/assets/js/87070fc3.0f052f4b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6124],{3905:(t,e,r)=>{r.d(e,{Zo:()=>s,kt:()=>d});var n=r(67294);function a(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function l(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}function o(t){for(var e=1;e=0||(a[r]=t[r]);return a}(t,e);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(t);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(t,r)&&(a[r]=t[r])}return a}var i=n.createContext({}),c=function(t){var e=n.useContext(i),r=e;return t&&(r="function"==typeof t?t(e):o(o({},e),t)),r},s=function(t){var e=c(t.components);return n.createElement(i.Provider,{value:e},t.children)},u={inlineCode:"code",wrapper:function(t){var e=t.children;return n.createElement(n.Fragment,{},e)}},m=n.forwardRef((function(t,e){var r=t.components,a=t.mdxType,l=t.originalType,i=t.parentName,s=p(t,["components","mdxType","originalType","parentName"]),m=c(r),d=a,f=m["".concat(i,".").concat(d)]||m[d]||u[d]||l;return r?n.createElement(f,o(o({ref:e},s),{},{components:r})):n.createElement(f,o({ref:e},s))}));function d(t,e){var r=arguments,a=e&&e.mdxType;if("string"==typeof t||a){var l=r.length,o=new Array(l);o[0]=m;var p={};for(var i in e)hasOwnProperty.call(e,i)&&(p[i]=e[i]);p.originalType=t,p.mdxType="string"==typeof t?t:a,o[1]=p;for(var c=2;c{r.r(e),r.d(e,{assets:()=>i,contentTitle:()=>o,default:()=>u,frontMatter:()=>l,metadata:()=>p,toc:()=>c});var n=r(87462),a=(r(67294),r(3905));const l={title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc720\ud615",slug:"/performance/types",tags:["performance","test"]},o=void 0,p={unversionedId:"\uc131\ub2a5/\uc131\ub2a5 \ud14c\uc2a4\ud2b8",id:"\uc131\ub2a5/\uc131\ub2a5 \ud14c\uc2a4\ud2b8",title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc720\ud615",description:"| \ud14c\uc2a4\ud2b8 | \uc124\uba85 |",source:"@site/docs/\uc131\ub2a5/\uc131\ub2a5 \ud14c\uc2a4\ud2b8.mdx",sourceDirName:"\uc131\ub2a5",slug:"/performance/types",permalink:"/docs/performance/types",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uc131\ub2a5/\uc131\ub2a5 \ud14c\uc2a4\ud2b8.mdx",tags:[{label:"performance",permalink:"/docs/tags/performance"},{label:"test",permalink:"/docs/tags/test"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc720\ud615",slug:"/performance/types",tags:["performance","test"]},sidebar:"tutorialSidebar",previous:{title:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",permalink:"/docs/performance/throughput-latency"},next:{title:"FIRST",permalink:"/docs/test/first"}},i={},c=[{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],s={toc:c};function u(t){let{components:e,...r}=t;return(0,a.kt)("wrapper",(0,n.Z)({},s,r,{components:e,mdxType:"MDXLayout"}),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"\ud14c\uc2a4\ud2b8"),(0,a.kt)("th",{parentName:"tr",align:null},"\uc124\uba85"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\uc9c0\uc5f0 \ud14c\uc2a4\ud2b8(latency test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\uc694\uccad\uc5d0 \ub300\ud55c \uc751\ub2f5 \uc2dc\uac04\uc744 \uce21\uc815")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\ucc98\ub9ac\uc728 \ud14c\uc2a4\ud2b8(throughput test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\uc2dc\uc2a4\ud15c \uc131\ub2a5\uc774 \uae09\ub77d\ud558\uae30 \uc9c1\uc804, \ucd5c\ub300 \ucc98\ub9ac\uc728 \uc218\uce58\ub97c \uce21\uc815")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\ubd80\ud558 \ud14c\uc2a4\ud2b8(load test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\ube44\uc988\ub2c8\uc2a4 \uc774\ubca4\ud2b8\ub97c \ub300\ube44\ud574 \ud2b8\ub798\ud53d\uc744 \uacac\ub51c \uc218 \uc788\ub294\uc9c0 \ud655\uc778")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\uc2a4\ud2b8\ub808\uc2a4 \ud14c\uc2a4\ud2b8(stress test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\uc2dc\uc2a4\ud15c\uc758 \ud55c\uacc4\uc810\uc744 \ud655\uc778")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\ub0b4\uad6c \ud14c\uc2a4\ud2b8(endurance test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\uc7a5\uc2dc\uac04 \uc2e4\ud589\ud560 \uacbd\uc6b0 \uc131\ub2a5 \uc774\uc0c1\uc774 \ubc1c\uc0dd\ud558\ub294\uc9c0 \ud655\uc778")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\uc6a9\ub7c9 \uacc4\ud68d \ud14c\uc2a4\ud2b8(capacity planning test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\ub9ac\uc18c\uc2a4\ub97c \ucd94\uac00\ud55c \ub9cc\ud07c \uc2dc\uc2a4\ud15c\uc774 \ud655\uc7a5\ub418\ub294\uc9c0 \ud655\uc778")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\uc800\ud558 \ud14c\uc2a4\ud2b8(degradation test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\uc2dc\uc2a4\ud15c\uc774 \ubd80\ubd84\uc801\uc73c\ub85c \uc2e4\ud328\ud560 \uacbd\uc6b0 \uc5b4\ub5a4 \uc77c\uc774 \ubc1c\uc0dd\ud558\ub294\uc9c0 \ud655\uc778(\uc7a5\uc560 \ubcf5\uad6c \ubc0f \ud68c\ubcf5 \ub4f1)")))),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uc790\ubc14 \ucd5c\uc801\ud654 - \ubca4\uc800\ubbfc J. \uc5d0\ubc88\uc2a4, \uc81c\uc784\uc2a4 \uace0\ud504, \ud06c\ub9ac\uc2a4 \ub274\ub79c\ub4dc"))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6124],{3905:(t,e,r)=>{r.d(e,{Zo:()=>s,kt:()=>d});var n=r(67294);function a(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function l(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}function o(t){for(var e=1;e=0||(a[r]=t[r]);return a}(t,e);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(t);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(t,r)&&(a[r]=t[r])}return a}var i=n.createContext({}),c=function(t){var e=n.useContext(i),r=e;return t&&(r="function"==typeof t?t(e):o(o({},e),t)),r},s=function(t){var e=c(t.components);return n.createElement(i.Provider,{value:e},t.children)},u={inlineCode:"code",wrapper:function(t){var e=t.children;return n.createElement(n.Fragment,{},e)}},m=n.forwardRef((function(t,e){var r=t.components,a=t.mdxType,l=t.originalType,i=t.parentName,s=p(t,["components","mdxType","originalType","parentName"]),m=c(r),d=a,f=m["".concat(i,".").concat(d)]||m[d]||u[d]||l;return r?n.createElement(f,o(o({ref:e},s),{},{components:r})):n.createElement(f,o({ref:e},s))}));function d(t,e){var r=arguments,a=e&&e.mdxType;if("string"==typeof t||a){var l=r.length,o=new Array(l);o[0]=m;var p={};for(var i in e)hasOwnProperty.call(e,i)&&(p[i]=e[i]);p.originalType=t,p.mdxType="string"==typeof t?t:a,o[1]=p;for(var c=2;c{r.r(e),r.d(e,{assets:()=>i,contentTitle:()=>o,default:()=>u,frontMatter:()=>l,metadata:()=>p,toc:()=>c});var n=r(87462),a=(r(67294),r(3905));const l={title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc720\ud615",slug:"/performance/types",tags:["performance","test"]},o=void 0,p={unversionedId:"\uc131\ub2a5/\uc131\ub2a5 \ud14c\uc2a4\ud2b8",id:"\uc131\ub2a5/\uc131\ub2a5 \ud14c\uc2a4\ud2b8",title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc720\ud615",description:"| \ud14c\uc2a4\ud2b8 | \uc124\uba85 |",source:"@site/docs/\uc131\ub2a5/\uc131\ub2a5 \ud14c\uc2a4\ud2b8.mdx",sourceDirName:"\uc131\ub2a5",slug:"/performance/types",permalink:"/docs/performance/types",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uc131\ub2a5/\uc131\ub2a5 \ud14c\uc2a4\ud2b8.mdx",tags:[{label:"performance",permalink:"/docs/tags/performance"},{label:"test",permalink:"/docs/tags/test"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc720\ud615",slug:"/performance/types",tags:["performance","test"]},sidebar:"tutorialSidebar",previous:{title:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",permalink:"/docs/performance/throughput-latency"},next:{title:"FIRST",permalink:"/docs/test/first"}},i={},c=[{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],s={toc:c};function u(t){let{components:e,...r}=t;return(0,a.kt)("wrapper",(0,n.Z)({},s,r,{components:e,mdxType:"MDXLayout"}),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"\ud14c\uc2a4\ud2b8"),(0,a.kt)("th",{parentName:"tr",align:null},"\uc124\uba85"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\uc9c0\uc5f0 \ud14c\uc2a4\ud2b8(latency test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\uc694\uccad\uc5d0 \ub300\ud55c \uc751\ub2f5 \uc2dc\uac04\uc744 \uce21\uc815")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\ucc98\ub9ac\uc728 \ud14c\uc2a4\ud2b8(throughput test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\uc2dc\uc2a4\ud15c \uc131\ub2a5\uc774 \uae09\ub77d\ud558\uae30 \uc9c1\uc804, \ucd5c\ub300 \ucc98\ub9ac\uc728 \uc218\uce58\ub97c \uce21\uc815")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\ubd80\ud558 \ud14c\uc2a4\ud2b8(load test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\ube44\uc988\ub2c8\uc2a4 \uc774\ubca4\ud2b8\ub97c \ub300\ube44\ud574 \ud2b8\ub798\ud53d\uc744 \uacac\ub51c \uc218 \uc788\ub294\uc9c0 \ud655\uc778")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\uc2a4\ud2b8\ub808\uc2a4 \ud14c\uc2a4\ud2b8(stress test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\uc2dc\uc2a4\ud15c\uc758 \ud55c\uacc4\uc810\uc744 \ud655\uc778")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\ub0b4\uad6c \ud14c\uc2a4\ud2b8(endurance test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\uc7a5\uc2dc\uac04 \uc2e4\ud589\ud560 \uacbd\uc6b0 \uc131\ub2a5 \uc774\uc0c1\uc774 \ubc1c\uc0dd\ud558\ub294\uc9c0 \ud655\uc778")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\uc6a9\ub7c9 \uacc4\ud68d \ud14c\uc2a4\ud2b8(capacity planning test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\ub9ac\uc18c\uc2a4\ub97c \ucd94\uac00\ud55c \ub9cc\ud07c \uc2dc\uc2a4\ud15c\uc774 \ud655\uc7a5\ub418\ub294\uc9c0 \ud655\uc778")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"\uc800\ud558 \ud14c\uc2a4\ud2b8(degradation test)"),(0,a.kt)("td",{parentName:"tr",align:null},"\uc2dc\uc2a4\ud15c\uc774 \ubd80\ubd84\uc801\uc73c\ub85c \uc2e4\ud328\ud560 \uacbd\uc6b0 \uc5b4\ub5a4 \uc77c\uc774 \ubc1c\uc0dd\ud558\ub294\uc9c0 \ud655\uc778(\uc7a5\uc560 \ubcf5\uad6c \ubc0f \ud68c\ubcf5 \ub4f1)")))),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uc790\ubc14 \ucd5c\uc801\ud654 - \ubca4\uc800\ubbfc J. \uc5d0\ubc88\uc2a4, \uc81c\uc784\uc2a4 \uace0\ud504, \ud06c\ub9ac\uc2a4 \ub274\ub79c\ub4dc"))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/871c1e5a.cf85e1ed.js b/assets/js/871c1e5a.87654b34.js similarity index 57% rename from assets/js/871c1e5a.cf85e1ed.js rename to assets/js/871c1e5a.87654b34.js index c334ebd38..d116c5b6b 100644 --- a/assets/js/871c1e5a.cf85e1ed.js +++ b/assets/js/871c1e5a.87654b34.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5966],{71247:e=>{e.exports=JSON.parse('{"permalink":"/page/23","page":23,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/22","nextPage":"/page/24","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5966],{71247:e=>{e.exports=JSON.parse('{"permalink":"/page/23","page":23,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/22","nextPage":"/page/24","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/8d05b77c.23797b44.js b/assets/js/8d05b77c.241f55bf.js similarity index 57% rename from assets/js/8d05b77c.23797b44.js rename to assets/js/8d05b77c.241f55bf.js index db6acf79a..50526391e 100644 --- a/assets/js/8d05b77c.23797b44.js +++ b/assets/js/8d05b77c.241f55bf.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[4149],{22801:e=>{e.exports=JSON.parse('{"permalink":"/page/5","page":5,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/4","nextPage":"/page/6","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[4149],{22801:e=>{e.exports=JSON.parse('{"permalink":"/page/5","page":5,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/4","nextPage":"/page/6","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/8fbd512b.27bf5da5.js b/assets/js/8fbd512b.23ff3cd4.js similarity index 68% rename from assets/js/8fbd512b.27bf5da5.js rename to assets/js/8fbd512b.23ff3cd4.js index 376f44b0b..7c2ad6f83 100644 --- a/assets/js/8fbd512b.27bf5da5.js +++ b/assets/js/8fbd512b.23ff3cd4.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5873],{15:s=>{s.exports=JSON.parse('{"label":"async","permalink":"/tags/async","allTagsPath":"/tags","count":1}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5873],{15:s=>{s.exports=JSON.parse('{"label":"async","permalink":"/tags/async","allTagsPath":"/tags","count":2}')}}]); \ No newline at end of file diff --git a/assets/js/905ecccc.06ce4047.js b/assets/js/905ecccc.06ce4047.js deleted file mode 100644 index 8510ba29a..000000000 --- a/assets/js/905ecccc.06ce4047.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2939],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>d});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var u=r.createContext({}),c=function(e){var t=r.useContext(u),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=c(e.components);return r.createElement(u.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,u=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),m=c(n),d=a,g=m["".concat(u,".").concat(d)]||m[d]||p[d]||o;return n?r.createElement(g,i(i({ref:t},s),{},{components:n})):r.createElement(g,i({ref:t},s))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var u in t)hasOwnProperty.call(t,u)&&(l[u]=t[u]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var c=2;c{n.d(t,{Z:()=>i});var r=n(67294),a=n(86010);const o="tabItem_Ymn6";function i(e){let{children:t,hidden:n,className:i}=e;return r.createElement("div",{role:"tabpanel",className:(0,a.Z)(o,i),hidden:n},t)}},74866:(e,t,n)=>{n.d(t,{Z:()=>S});var r=n(87462),a=n(67294),o=n(86010),i=n(12466),l=n(16550),u=n(91980),c=n(67392),s=n(50012);function p(e){return function(e){return a.Children.map(e,(e=>{if((0,a.isValidElement)(e)&&"value"in e.props)return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))}(e).map((e=>{let{props:{value:t,label:n,attributes:r,default:a}}=e;return{value:t,label:n,attributes:r,default:a}}))}function m(e){const{values:t,children:n}=e;return(0,a.useMemo)((()=>{const e=t??p(n);return function(e){const t=(0,c.l)(e,((e,t)=>e.value===t.value));if(t.length>0)throw new Error(`Docusaurus error: Duplicate values "${t.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[t,n])}function d(e){let{value:t,tabValues:n}=e;return n.some((e=>e.value===t))}function g(e){let{queryString:t=!1,groupId:n}=e;const r=(0,l.k6)(),o=function(e){let{queryString:t=!1,groupId:n}=e;if("string"==typeof t)return t;if(!1===t)return null;if(!0===t&&!n)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:t,groupId:n});return[(0,u._X)(o),(0,a.useCallback)((e=>{if(!o)return;const t=new URLSearchParams(r.location.search);t.set(o,e),r.replace({...r.location,search:t.toString()})}),[o,r])]}function k(e){const{defaultValue:t,queryString:n=!1,groupId:r}=e,o=m(e),[i,l]=(0,a.useState)((()=>function(e){let{defaultValue:t,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the component requires at least one children component");if(t){if(!d({value:t,tabValues:n}))throw new Error(`Docusaurus error: The has a defaultValue "${t}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return t}const r=n.find((e=>e.default))??n[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:t,tabValues:o}))),[u,c]=g({queryString:n,groupId:r}),[p,k]=function(e){let{groupId:t}=e;const n=function(e){return e?`docusaurus.tab.${e}`:null}(t),[r,o]=(0,s.Nk)(n);return[r,(0,a.useCallback)((e=>{n&&o.set(e)}),[n,o])]}({groupId:r}),f=(()=>{const e=u??p;return d({value:e,tabValues:o})?e:null})();(0,a.useEffect)((()=>{f&&l(f)}),[f]);return{selectedValue:i,selectValue:(0,a.useCallback)((e=>{if(!d({value:e,tabValues:o}))throw new Error(`Can't select invalid tab value=${e}`);l(e),c(e),k(e)}),[c,k,o]),tabValues:o}}var f=n(72389);const b="tabList__CuJ",h="tabItem_LNqP";function N(e){let{className:t,block:n,selectedValue:l,selectValue:u,tabValues:c}=e;const s=[],{blockElementScrollPositionUntilNextRender:p}=(0,i.o5)(),m=e=>{const t=e.currentTarget,n=s.indexOf(t),r=c[n].value;r!==l&&(p(t),u(r))},d=e=>{let t=null;switch(e.key){case"Enter":m(e);break;case"ArrowRight":{const n=s.indexOf(e.currentTarget)+1;t=s[n]??s[0];break}case"ArrowLeft":{const n=s.indexOf(e.currentTarget)-1;t=s[n]??s[s.length-1];break}}t?.focus()};return a.createElement("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,o.Z)("tabs",{"tabs--block":n},t)},c.map((e=>{let{value:t,label:n,attributes:i}=e;return a.createElement("li",(0,r.Z)({role:"tab",tabIndex:l===t?0:-1,"aria-selected":l===t,key:t,ref:e=>s.push(e),onKeyDown:d,onClick:m},i,{className:(0,o.Z)("tabs__item",h,i?.className,{"tabs__item--active":l===t})}),n??t)})))}function v(e){let{lazy:t,children:n,selectedValue:r}=e;if(t){const e=n.find((e=>e.props.value===r));return e?(0,a.cloneElement)(e,{className:"margin-top--md"}):null}return a.createElement("div",{className:"margin-top--md"},n.map(((e,t)=>(0,a.cloneElement)(e,{key:t,hidden:e.props.value!==r}))))}function C(e){const t=k(e);return a.createElement("div",{className:(0,o.Z)("tabs-container",b)},a.createElement(N,(0,r.Z)({},e,t)),a.createElement(v,(0,r.Z)({},e,t)))}function S(e){const t=(0,f.Z)();return a.createElement(C,(0,r.Z)({key:String(t)},e))}},1057:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>u,default:()=>d,frontMatter:()=>l,metadata:()=>c,toc:()=>p});var r=n(87462),a=(n(67294),n(3905)),o=n(74866),i=n(85162);const l={title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",slug:"tomcat-retrospective",tags:["Woowahan Techcourse","Retrospective"]},u=void 0,c={permalink:"/tomcat-retrospective",editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx",source:"@site/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx",title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",description:"\ud1b0\ucea3 \uad6c\ud604",date:"2023-09-11T00:00:00.000Z",formattedDate:"2023\ub144 9\uc6d4 11\uc77c",tags:[{label:"Woowahan Techcourse",permalink:"/tags/woowahan-techcourse"},{label:"Retrospective",permalink:"/tags/retrospective"}],readingTime:12.27,hasTruncateMarker:!1,authors:[],frontMatter:{title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",slug:"tomcat-retrospective",tags:["Woowahan Techcourse","Retrospective"]},nextItem:{title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958",permalink:"/performance-test-type"}},s={authorsImageUrls:[]},p=[{value:"\ud1b0\ucea3 \uad6c\ud604",id:"\ud1b0\ucea3-\uad6c\ud604",level:3},{value:"\ub2e4\uc774\uc5b4\uadf8\ub7a8",id:"\ub2e4\uc774\uc5b4\uadf8\ub7a8",level:3},{value:"\ucf54\ub4dc \ub9ac\ubdf0",id:"\ucf54\ub4dc-\ub9ac\ubdf0",level:3},{value:"SessionConfig",id:"sessionconfig",level:3},{value:"HTTP \uc218\uc5c5",id:"http-\uc218\uc5c5",level:3},{value:"Thread \uc218\uc5c5",id:"thread-\uc218\uc5c5",level:3},{value:"\ub9c8\uce58\uba70",id:"\ub9c8\uce58\uba70",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],m={toc:p};function d(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ud1b0\ucea3-\uad6c\ud604"},"\ud1b0\ucea3 \uad6c\ud604"),(0,a.kt)("p",null,"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\ub97c \uc9c0\uc6d0\ud560 \ub54c \uac1d\uccb4\uc9c0\ud5a5\uacfc \uad00\ub828\ub41c \ubbf8\uc158\ub3c4 \uae30\ub300\ub97c \ub9ce\uc774 \ud588\uc9c0\ub9cc \ub808\ubca8 4\uc5d0 \uc9c4\ud589\ud558\ub294 \ubbf8\uc158\uc774 \uc815\ub9d0 \ud558\uace0 \uc2f6\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uadf8\ub798\uc11c \ubbf8\uc158\uc744 \ud560 \uc218 \uc788\uc744\uae4c\ub77c\ub294 \uac71\uc815 \ubc18, \ubbf8\uc158\uc5d0 \ub300\ud55c \uae30\ub300 \ubc18\uc73c\ub85c \ubd80\ud47c \ub9c8\uc74c\uc744 \uac00\uc9c0\uace0 \ubbf8\uc158\uc744 \uc2dc\uc791\ud588\ub358 \uac83 \uac19\ub2e4. "),(0,a.kt)("p",null,"\uc774\ubc88 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc801\uc808\ud558\uac8c \ucd94\uc0c1\ud654\ud558\uace0, \ubbf8\uc158\uc758 \ubcf8\uc9c8\uc744 \uc774\ud574\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158\uc740 ",(0,a.kt)("a",{parentName:"p",href:"https://datatracker.ietf.org/doc/html/rfc2616/"},"RFC 2616"),"\uc5d0 \uba85\uc2dc\ub41c \uc2a4\ud399(\uc644\ubcbd\ud558\uc9c0 \uc54a\uc9c0\ub9cc \ubbf8\uc158\uc5d0\uc11c \uc8fc\uc5b4\uc9c4 \uc694\uad6c\uc0ac\ud56d\ub9cc \ub9cc\uc871\ud558\ub3c4\ub85d)\uc73c\ub85c \uc694\uccad\uc744 \ubc1b\uc544 \ucc98\ub9ac \ud6c4 \ubc18\ud658\ud558\ub294\ub370 \uc9d1\uc911\ud588\ub2e4. "),(0,a.kt)("h3",{id:"\ub2e4\uc774\uc5b4\uadf8\ub7a8"},"\ub2e4\uc774\uc5b4\uadf8\ub7a8"),(0,a.kt)("p",null,"Catalina\ub294 Tomcat\uc758 \uc11c\ube14\ub9bf \ucee8\ud14c\uc774\ub108, Coyote\ub294 HTTP 1.1 \uc6f9 \uc11c\ubc84\ub97c \uc9c0\uc6d0\ud558\ub294 \uad6c\uc131 \uc694\uc18c\ub77c\uace0 \uc0dd\uac01\ud558\uace0 \uc544\ub798\uc640 \uac19\uc774 \uad6c\uc131\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0ac\uc2e4 \ub0b4\ubd80 \uad6c\uc870\ub97c \uae4a\uac8c \uacf5\ubd80\ud560 \uc2dc\uac04\uc744 \uac00\uc9c0\uc9c0 \ubabb\ud574\uc11c \uac01 \uad6c\uc131 \uc694\uc18c\uac00 \uc65c \ud574\ub2f9 \uc704\uce58\uc5d0 \uc788\ub294\uc9c0 \uc644\ubcbd\ud558\uac8c \uc124\uba85\ud558\uc9c0\ub294 \ubabb\ud558\uc9c0\ub9cc \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc774\uac74 \uc5ec\uae30\uc5d0 \uc788\uc73c\uba74 \uc88b\uc744 \uac83 \uac19\uc740\ub370? \ub77c\ub294 \uc0dd\uac01\uc774 \ub4e4\uba74 \uc801\uc808\ud55c \ud328\ud0a4\uc9c0\uc5d0 \uc704\uce58\uc2dc\ud0a4\ub294 \ubc29\ud5a5\uc73c\ub85c \uc9c4\ud589\uc744 \ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub610\ud55c \uc801\uc808\ud558\uac8c \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc758\uc874\uc131 \ubc29\ud5a5\uc744 \ub2e8\ubc29\ud5a5\uc73c\ub85c \ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. "),(0,a.kt)("mermaid",{value:"graph LR\n\tsubgraph coyote\n\t\tHP[Http11Processor] --\x3e A\n\t\tHP --\x3e HttpRequestParser\n\t\tHP --\x3e HttpResponseGenerator\n\t\tA[Adapter]\n end\n\n subgraph catalina\n\t\tRA[RequestAdapter] -.-> A\n\t\tAC[AbstractController] -.-> C[Controller]\n\t\tStaticController -.-> AC\n\t\tSM[SessionManger] -.-> Manager\n\t\tTC[Tomcat] --\x3e RA\n\t\tRA --\x3e C\n\t\tRA --\x3e Manager\n\t\tRA --\x3e RM\n\t\tRM[RequestMapper] --\x3e C\n end\n\n subgraph jwp\n\t\tLC[LoginController] -.-> AC\n\t\tApplication --\x3e TC\n end\n"}),(0,a.kt)("h3",{id:"\ucf54\ub4dc-\ub9ac\ubdf0"},"\ucf54\ub4dc \ub9ac\ubdf0"),(0,a.kt)("p",null,"\ud06c\ub8e8 \uc911 \ud55c \uba85\uc774 \ub098\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\uace0, \ub0b4\uac00 \ub2e4\ub978 \ud06c\ub8e8\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\ub294 \ud615\ud0dc\ub85c \uc9c4\ud589\uc774 \ub418\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\uc758 \ub9ac\ubdf0\uc5b4\ub294 \ub514\ub178, \ub9ac\ubdf0\uc774\ub294 \ud544\ub9bd\uc774\uc5c8\ub2e4. "),(0,a.kt)("p",null,"\ub514\ub178(\ub9e4\uc758 \ub208\uc774 \uc544\ub2cc \uacf5\ub8e1\uc758 \ub208?)\uac00 \ub9e4\uc6b0 \uaf3c\uaf3c\ud558\uac8c \ucf54\ub4dc \ub9ac\ubdf0\ub97c \ud574\uc8fc\uc5b4\uc11c \uc870\uae08 \ub354 \ub098\uc740 \ucf54\ub4dc\ub97c \uc791\uc131\ud560 \uc218 \uc788\uc5c8\uace0, \ud544\ub9bd\uc758 \ucf54\ub4dc\uc5d0\uc11c\ub294 \uaf3c\uaf3c\ud558\uac8c \uc608\uc678\ucc98\ub9ac \ud558\ub294 \ubd80\ubd84\uc744 \ubc30\uc6b8 \uc218 \uc788\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud55c \uac00\uc9c0 \uc544\uc26c\uc6b4 \uc810\uc740 \ud544\ub9bd\uc5d0\uac8c \uc791\uc131\ud55c \ub098\uc758 \ucf54\uba58\ud2b8\ub4e4\uc774 \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uacbd\ud5d8 \uae30\ubc18\uc73c\ub85c \uc791\uc131\ud55c \ub0b4\uc6a9\uc774 \ub9ce\uc544 \uadfc\uac70\uac00 \uc870\uae08 \ubd80\uc871\ud588\uace0, \uc815\ub9ac\ub418\uc9c0 \uc54a\uc740 \ubd80\ubd84\uc774 \ub9ce\uc558\ub358 \uac83 \uac19\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e4\uc74c \ubbf8\uc158\ubd80\ud130 \ub9ac\ubdf0\ud560 \ub54c \uc870\uae08 \ub354 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ub354 \uc88b\uc740 \ub0b4\uc6a9\uc744 \ud06c\ub8e8\ub4e4\uacfc \uacf5\uc720\ud560 \uc218 \uc788\ub3c4\ub85d \ub178\ub825\ud574\uc57c\uaca0\ub2e4. "),(0,a.kt)("h3",{id:"sessionconfig"},"SessionConfig"),(0,a.kt)("p",null,"\ubbf8\uc158\uc744 \uc9c4\ud589 \uc911 catalina \ud328\ud0a4\uc9c0\uc758 Session \uad00\ub828 \ubd80\ubd84\uc744 \ubcf4\uba74\uc11c \uc911\ubcf5 \ub85c\uc9c1\uc744 \uac1c\uc120\ud574 \ubcfc \uc218 \uc788\uc744 \uac83 \uac19\uc544 ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/apache/tomcat/pull/660"},"\ucee8\ud2b8\ub9ac\ubdf0\ud2b8"),"\ub97c \uc2dc\ub3c4\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc138\uc158 \ucfe0\ud0a4\uc758 \uc774\ub984\uc744 \uac00\uc838\uc624\ub294 Util \ud074\ub798\uc2a4\uc758 \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub294\ub370 \uae30\ubcf8 \uac12\uc740 JSESSIONID \uc9c0\ub9cc \uc124\uc815\uc5d0 \ub530\ub77c\uc11c \uc138\uc158 \ucfe0\ud0a4\uba85\uc744 \ub2e4\ub974\uac8c \uc0ac\uc6a9\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \ud574\ub2f9 \ub85c\uc9c1\uc774 \uc788\ub294 \uac83\uc73c\ub85c \uc0dd\uac01\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uae30\uc874\uc758 \ucf54\ub4dc\ub294 \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \ucf54\ub4dc\uc758 \ud750\ub984\uc774 \uc77c\uce58\ud558\uc9c0 \uc54a\uc544\uc11c \uc57d\uac04 \uc774\ud574\ud558\uae30 \uc5b4\ub824\uc6e0\ub2e4. "),(0,a.kt)("p",null,"\ucd08\uae30\uc5d0 \uc694\uccad\ud588\ub358 PR\uc740 \uae30\uc874\uc758 \ucf54\ub4dc\ubcf4\ub2e4 \uc804\uccb4\uc801\uc73c\ub85c \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, context\uac00 null\uc778 \uacbd\uc6b0 \ubc14\ub85c \uae30\ubcf8 \uac12\uc744 \ubc18\ud658\ud568\uc73c\ub85c\uc368 \uc131\ub2a5 \uac1c\uc120\uc758 \ud6a8\uacfc\uac00 \uc788\uc744 \uac70\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uba54\uc778\ud14c\uc774\ub108\uc778 Mark Thomas \ud615\uc774 \ud574\ub2f9 \ub85c\uc9c1\uc758 \uacbd\uc6b0 \ucef4\ud30c\uc77c\ub7ec\uac00 \ud574\ub2f9 \ubd80\ubd84\uc744 \ucd5c\uc801\ud654 \ud560 \uc218 \uc788\uc744 \uac70\ub77c\uace0 \uae30\ub300\ud55c\ub2e4\uace0 \ud588\uace0, \uac00\ub3c5\uc131\uc744 \uac1c\uc120\uc2dc\ucf1c\ubcf4\ub77c\uace0 \uc870\uc5b8\ud574\uc8fc\uc168\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ucef4\ud30c\uc77c\ub7ec \ucd5c\uc801\ud654\ub294 \uace0\ub824\ud574\ubcf4\uc9c0 \ubabb\ud55c \ubd80\ubd84\uc778\ub370, \uc55e\uc73c\ub85c \ud559\uc2b5\ud574\uc57c \ud560 \ubd80\ubd84\uc774 \uc0b0\ub354\ubbf8\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. "),(0,a.kt)("p",null,"\ub0a8\uaca8\uc900 \ucf54\uba58\ud2b8\uc5d0 \ub530\ub77c \ucd5c\uc885\uc801\uc73c\ub85c\ub294 \uc911\ubcf5\ub41c \ucf54\ub4dc\ub97c \uc904\uc774\ub294 \ubc29\ud5a5\uc73c\ub85c \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uacb0\uacfc\uc801\uc73c\ub85c \uae30\uc874 \ub85c\uc9c1 \ub300\ube44 \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \uc720\uc0ac\ud55c \ud750\ub984\uc758 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\uc5ec \uc88b\uc740 \ubc29\ud5a5\uc73c\ub85c \ub9ac\ud329\ud130\ub9c1\uc744 \ud588\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. "),(0,a.kt)(o.Z,{mdxType:"Tabs"},(0,a.kt)(i.Z,{value:"\uae30\uc874",label:"\uae30\uc874",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n\n String result = getConfiguredSessionCookieName(context);\n\n if (result == null) {\n result = DEFAULT_SESSION_COOKIE_NAME;\n }\n\n return result;\n}\n\npublic static String getSessionUriParamName(Context context) {\n\n String result = getConfiguredSessionCookieName(context);\n\n if (result == null) {\n result = DEFAULT_SESSION_PARAMETER_NAME;\n }\n\n return result;\n}\n\nprivate static String getConfiguredSessionCookieName(Context context) {\n\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n if (context != null) {\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc =\n context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n }\n\n return null;\n}\n"))),(0,a.kt)(i.Z,{value:"PR \uc694\uccad",label:"PR \uc694\uccad",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n if (context == null) {\n return DEFAULT_SESSION_COOKIE_NAME;\n }\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\n}\n\npublic static String getSessionUriParamName(Context context) {\n if (context == null) {\n return DEFAULT_SESSION_PARAMETER_NAME;\n }\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\n}\n\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n return defaultName;\n}\n"))),(0,a.kt)(i.Z,{value:"\ucd5c\uc885",label:"\ucd5c\uc885",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\n}\n\npublic static String getSessionUriParamName(Context context) {\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\n}\n\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n if (context != null) {\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n }\n return defaultName;\n}\n")))),(0,a.kt)("h3",{id:"http-\uc218\uc5c5"},"HTTP \uc218\uc5c5"),(0,a.kt)("p",null,"\ubbf8\uc158 \uc911\uac04\uc5d0 \uc9c4\ud589\ub418\uc5c8\ub358 HTTP \uc218\uc5c5\uc5d0\ub294 HTTP\ub97c \uc801\uc808\ud558\uac8c \ud65c\uc6a9\ud558\ub294 \ubd80\ubd84\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud56d\uc0c1 \uc131\ub2a5 \uac1c\uc120\uc744 \uc704\ud574 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ub2e8\uc5d0\uc11c \ucd5c\uc801\ud654\ud574\ubcf4\ub824\uace0 \ub178\ub825\uc744 \ud588\uc9c0\ub9cc, \ub354 \uc801\uc740 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ud6a8\uc728\uc801\uc73c\ub85c \uc131\ub2a5\uc744 \uac1c\uc120\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc5d0 \ub300\ud574 \uc54c \uc218 \uc788\uc5c8\ub358 \uc218\uc5c5\uc774\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","HTTP \uc555\ucd95, HTTP \uce90\uc2f1, \ub9ac\uc18c\uc2a4 \ucd5c\uc801\ud654 \uae30\ubc95\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4. "),(0,a.kt)("p",null,"\uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \ub2e4\uc74c \uc635\uc158\uc744 \uc124\uc815\ud558\uc5ec http\uc758 \uc1a1\uc218\uc2e0\uc758 \uc555\ucd95\uc744 \uc9c4\ud589\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yml"},"server:\n compression:\n enabled: true\n")),(0,a.kt)("p",null,"\uc218\uc5c5 \uc911 \ud574\ub2f9 \uc555\ucd95 \uc131\ub2a5\uc774 \uc88b\ub2e4\uba74 \uc65c \uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \uae30\ubcf8 \uac12\uc73c\ub85c \uc124\uc815\uc744 \ud558\uc9c0 \uc54a\uc558\ub294\uc9c0 \uad81\uae08\ud574\uc84c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uad81\uae08\uc99d\uc744 \ud574\uc18c\ud558\uc9c0 \ubabb\ud588\ub294\ub370 \ub9d0\ub791\uc774 \uc7a1\ub2f4 \ucc44\ub110\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc740 ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/spring-projects/spring-boot/issues/21369"},"issue"),"\ub97c \ucc3e\uc544\uc8fc\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub0b4\uc6a9\uc744 \uc694\uc57d\ud574 \ubcf4\uc790\uba74 WAS \ubcc4\ub85c \uc555\ucd95\uc744 \ud558\uae30 \uc704\ud574 \uc124\uc815\ud574\uc57c \ud558\ub294 \uac83\uc774 \ub2e4\ub974\uace0, \ubb34\uc870\uac74 \uc555\ucd95\uc744 \ud558\ub294 \uac83\uc774 \ucd5c\uc801\uc758 \uacbd\uc6b0\uac00 \uc544\ub2d0 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \uae30\ubcf8\uac12\uc73c\ub85c \uc124\uc815\ud558\uc9c0 \uc54a\ub294 \uac83 \uac19\ub2e4. "),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"If you're developing a public-facing application then it's probably likely gzip compression would be worthwhile. If, however, you're a microservice application and you're in a dataceter, you may well prefer to reduce CPU load because you know you'll only be talking to other microservices and you have a reliable gigabit network.")),(0,a.kt)("p",null,"Phil Webb \ud615\ub2d8\uc758 \ub9d0\uc5d0 \ub530\ub974\uba74 \uc77c\ubc18\uc801\uc778 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uac1c\ubc1c\ud558\ub294 \uacbd\uc6b0 gzip \uc555\ucd95\uc744 \uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\uc9c0\ub9cc, MSA \ud658\uacbd + \ub370\uc774\ud130 \uc13c\ud130\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \uc624\uc9c1 \ub2e4\ub978 MSA \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uacfc \ud1b5\uc2e0\ud558\uace0, \uace0\uc131\ub2a5 \ub124\ud2b8\uc6cc\ud06c\uac00 \uc788\uae30 \ub54c\ubb38\uc5d0 CPU \ubd80\ud558\ub97c \uc904\uc774\ub294 \uac83\uc774 \uc6b0\uc120\uc2dc \ub420 \uc218\ub3c4 \uc788\ub2e4\ub294 \uac83\uc774\uc5c8\ub2e4. "),(0,a.kt)("p",null,"\uc774\uc678\uc5d0\ub3c4 \uc758\ub3c4\ud558\uc9c0 \uc54a\uc740 \uce90\uc2f1\uc744 \ub9c9\uae30 \uc704\ud574 \ud734\ub9ac\uc2a4\ud2f1 \uce90\uc2f1\uc744 \uc81c\uac70\ud558\uac70\ub098, \uac1c\uc778 \uc815\ubcf4 \uc720\ucd9c\uc744 \ub9c9\uae30 \uc704\ud574 \uc751\ub2f5 \ud5e4\ub354\uc5d0 private\uc744 \uc124\uc815, ETag\ub3c4 \ud559\uc2b5\ud588\ub2e4. "),(0,a.kt)("admonition",{title:"ETag",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"ETag HTTP \uc751\ub2f5 \ud5e4\ub354\ub294 \ud2b9\uc815 \ubc84\uc804\uc758 \ub9ac\uc18c\uc2a4\ub97c \uc2dd\ubcc4\ud558\ub294 \uc2dd\ubcc4\uc790\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc6f9 \uc11c\ubc84\uac00 \ub0b4\uc6a9\uc744 \ud655\uc778\ud558\uace0 \ubcc0\ud558\uc9c0 \uc54a\uc558\uc73c\uba74, \uc6f9 \uc11c\ubc84\ub85c full \uc694\uccad\uc744 \ubcf4\ub0b4\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0, \uce90\uc2dc\uac00 \ub354 \ud6a8\uc728\uc801\uc774\uac8c \ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","MDN")),(0,a.kt)("h3",{id:"thread-\uc218\uc5c5"},"Thread \uc218\uc5c5"),(0,a.kt)("p",null,"\uc2a4\ub808\ub4dc\uc5d0 \ub300\ud55c \uc218\uc5c5\uc744 \ub4e4\uc5c8\uc9c0\ub9cc, \ubcf5\uc7a1\ud55c \ub0b4\uc6a9\ub3c4 \uc6cc\ub099 \ub9ce\uc558\uae30 \ub54c\ubb38\uc5d0 \uc124\uba85\ud558\ub77c\uace0 \ud558\uba74 \uc798 \ubabb\ud560 \uac83 \uac19\uc740 \ub290\ub08c\uc774 \ub4e0\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud604\uc7ac \ud504\ub85c\uc81d\ud2b8, \ubbf8\uc158, \ud14c\ucf54\ud1a1 \uc900\ube44\ub97c \ubcd1\ud589\ud574\uc57c \ud574\uc11c \uc138\ubd80\uc801\uc778 \ub0b4\uc6a9\uc740 \uc2dc\uac04 \ub0a0 \ub54c \ubcf5\uc2b5\ud558\ub824\uace0 \ud55c\ub2e4. "),(0,a.kt)("p",null,"\uc2a4\ub808\ub4dc\ub97c \uc774\ud574\ud558\uace0, WAS\uc5d0 \uc2a4\ub808\ub4dc \uc124\uc815 \uad00\ub828\ud55c \uc2e4\uc2b5\uc774 \uc788\uc5c8\ub294\ub370 \ud14c\uc624\uc640 \uac19\uc774 1\uc2dc\uac04 \uc815\ub3c4 \ud398\uc5b4\ub85c Thread \uc2e4\uc2b5\uc744 \uc9c4\ud589\ud574 \ubcf4\uc558\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud559\uc2b5\ud55c \ub0b4\uc6a9\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. "),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"threads.max"),": Tomcat\uc758 \ucd5c\ub300 \uc2a4\ub808\ub4dc \uac1c\uc218",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"max-connections"),": Tomcat\uc774 \uc720\uc9c0\ud560 \uc218 \uc788\ub294 \ucd5c\ub300 \ucee4\ub125\uc158 \uac1c\uc218",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"accept-count"),": \ucd5c\ub300 \uc5f0\uacb0 \uc218\uc5d0 \ub3c4\ub2ec\ud588\uc744 \ub54c \uc5f0\uacb0 \uc694\uccad\uc5d0 \ub300\ud574 \uc6b4\uc601 \uccb4\uc81c\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \ub300\uae30\uc5f4\uc758 \ucd5c\ub300 \uae38\uc774. \ud574\ub2f9 Queue\uc5d0 \uc694\uccad\uc774 \uc313\uc774\ub294 \uac83\uc740 Tomcat\uc774 \ub354 \uc774\uc0c1 \uc694\uccad\uc744 \ubc1b\uc744 \uc218 \uc5c6\ub2e4\ub294 \ub73b\uc774\ub2e4. accpet-count queue\uc5d0\ub3c4 \uc694\uccad\uc774 \uac00\ub4dd\ucc28\uba74 \uadf8 \uc774\ud6c4\uc5d0 \uc624\ub294 \uc694\uccad\uc740 \uac70\ubd80\ub41c\ub2e4. "),(0,a.kt)("mermaid",{value:'graph LR\n C("Client") -- request --\x3e ACQ("\uc6b4\uc601\uccb4\uc81c\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue \n size = accept-count") --\x3e TCQ("Tomcat Connector\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue\n size = max-connections") --\x3e TP("Thread Pool\n size = threads.max")'}),(0,a.kt)("h3",{id:"\ub9c8\uce58\uba70"},"\ub9c8\uce58\uba70"),(0,a.kt)("p",null,"\uc2dc\uac04\uc740 \ub108\ubb34 \ube60\ub974\uac8c \uac00\uace0 \ud560 \uc77c\uc740 \ub9ce\uc740 \uac83 \uac19\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc6b0\uc120\uc21c\uc704\ub97c \uc798 \uc815\ud558\uace0 \ud559\uc2b5\uc744 \uc9c4\ud589\ud574\uc57c\uaca0\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud604\uc7ac \ub370\uc774\ud130 \ub2e4\ub8e8\ub294 \ubd80\ubd84(DB)\uc5d0 \ub300\ud55c \ud559\uc2b5\uc774 \ub9ce\uc774 \ubd80\uc871\ud55c \uac83 \uac19\ub2e4. \ud574\ub2f9 \ubd80\ubd84\uc740 \ud14c\ucf54\ud1a1\uc774 \ub05d\ub098\ub294\ub300\ub85c \ucc44\uc6cc\uc57c\uaca0\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://datatracker.ietf.org/doc/html/rfc2616/"},"RFC 2616"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/ETag"},"ETag, mdn"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://tomcat.apache.org/tomcat-8.5-doc/config/http.html"},"Apache Tomcat 8 Configuration Reference"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://bcho.tistory.com/788"},"Apache Tomcat Tuning, Terry Cho"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://dev-ws.tistory.com/96"},"maxThreads, maxConnections, acceptCount\ub85c Tomcat \ud29c\ub2dd\ud558\uae30")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/905ecccc.806a92d0.js b/assets/js/905ecccc.806a92d0.js new file mode 100644 index 000000000..94096a9ef --- /dev/null +++ b/assets/js/905ecccc.806a92d0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2939],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>d});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var u=r.createContext({}),c=function(e){var t=r.useContext(u),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=c(e.components);return r.createElement(u.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,u=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),m=c(n),d=a,g=m["".concat(u,".").concat(d)]||m[d]||p[d]||o;return n?r.createElement(g,i(i({ref:t},s),{},{components:n})):r.createElement(g,i({ref:t},s))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var u in t)hasOwnProperty.call(t,u)&&(l[u]=t[u]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var c=2;c{n.d(t,{Z:()=>i});var r=n(67294),a=n(86010);const o="tabItem_Ymn6";function i(e){let{children:t,hidden:n,className:i}=e;return r.createElement("div",{role:"tabpanel",className:(0,a.Z)(o,i),hidden:n},t)}},74866:(e,t,n)=>{n.d(t,{Z:()=>S});var r=n(87462),a=n(67294),o=n(86010),i=n(12466),l=n(16550),u=n(91980),c=n(67392),s=n(50012);function p(e){return function(e){return a.Children.map(e,(e=>{if((0,a.isValidElement)(e)&&"value"in e.props)return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))}(e).map((e=>{let{props:{value:t,label:n,attributes:r,default:a}}=e;return{value:t,label:n,attributes:r,default:a}}))}function m(e){const{values:t,children:n}=e;return(0,a.useMemo)((()=>{const e=t??p(n);return function(e){const t=(0,c.l)(e,((e,t)=>e.value===t.value));if(t.length>0)throw new Error(`Docusaurus error: Duplicate values "${t.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[t,n])}function d(e){let{value:t,tabValues:n}=e;return n.some((e=>e.value===t))}function g(e){let{queryString:t=!1,groupId:n}=e;const r=(0,l.k6)(),o=function(e){let{queryString:t=!1,groupId:n}=e;if("string"==typeof t)return t;if(!1===t)return null;if(!0===t&&!n)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:t,groupId:n});return[(0,u._X)(o),(0,a.useCallback)((e=>{if(!o)return;const t=new URLSearchParams(r.location.search);t.set(o,e),r.replace({...r.location,search:t.toString()})}),[o,r])]}function k(e){const{defaultValue:t,queryString:n=!1,groupId:r}=e,o=m(e),[i,l]=(0,a.useState)((()=>function(e){let{defaultValue:t,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the component requires at least one children component");if(t){if(!d({value:t,tabValues:n}))throw new Error(`Docusaurus error: The has a defaultValue "${t}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return t}const r=n.find((e=>e.default))??n[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:t,tabValues:o}))),[u,c]=g({queryString:n,groupId:r}),[p,k]=function(e){let{groupId:t}=e;const n=function(e){return e?`docusaurus.tab.${e}`:null}(t),[r,o]=(0,s.Nk)(n);return[r,(0,a.useCallback)((e=>{n&&o.set(e)}),[n,o])]}({groupId:r}),f=(()=>{const e=u??p;return d({value:e,tabValues:o})?e:null})();(0,a.useEffect)((()=>{f&&l(f)}),[f]);return{selectedValue:i,selectValue:(0,a.useCallback)((e=>{if(!d({value:e,tabValues:o}))throw new Error(`Can't select invalid tab value=${e}`);l(e),c(e),k(e)}),[c,k,o]),tabValues:o}}var f=n(72389);const b="tabList__CuJ",h="tabItem_LNqP";function N(e){let{className:t,block:n,selectedValue:l,selectValue:u,tabValues:c}=e;const s=[],{blockElementScrollPositionUntilNextRender:p}=(0,i.o5)(),m=e=>{const t=e.currentTarget,n=s.indexOf(t),r=c[n].value;r!==l&&(p(t),u(r))},d=e=>{let t=null;switch(e.key){case"Enter":m(e);break;case"ArrowRight":{const n=s.indexOf(e.currentTarget)+1;t=s[n]??s[0];break}case"ArrowLeft":{const n=s.indexOf(e.currentTarget)-1;t=s[n]??s[s.length-1];break}}t?.focus()};return a.createElement("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,o.Z)("tabs",{"tabs--block":n},t)},c.map((e=>{let{value:t,label:n,attributes:i}=e;return a.createElement("li",(0,r.Z)({role:"tab",tabIndex:l===t?0:-1,"aria-selected":l===t,key:t,ref:e=>s.push(e),onKeyDown:d,onClick:m},i,{className:(0,o.Z)("tabs__item",h,i?.className,{"tabs__item--active":l===t})}),n??t)})))}function v(e){let{lazy:t,children:n,selectedValue:r}=e;if(t){const e=n.find((e=>e.props.value===r));return e?(0,a.cloneElement)(e,{className:"margin-top--md"}):null}return a.createElement("div",{className:"margin-top--md"},n.map(((e,t)=>(0,a.cloneElement)(e,{key:t,hidden:e.props.value!==r}))))}function C(e){const t=k(e);return a.createElement("div",{className:(0,o.Z)("tabs-container",b)},a.createElement(N,(0,r.Z)({},e,t)),a.createElement(v,(0,r.Z)({},e,t)))}function S(e){const t=(0,f.Z)();return a.createElement(C,(0,r.Z)({key:String(t)},e))}},1057:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>u,default:()=>d,frontMatter:()=>l,metadata:()=>c,toc:()=>p});var r=n(87462),a=(n(67294),n(3905)),o=n(74866),i=n(85162);const l={title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",slug:"tomcat-retrospective",tags:["Woowahan Techcourse","Retrospective"]},u=void 0,c={permalink:"/tomcat-retrospective",editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx",source:"@site/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx",title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",description:"\ud1b0\ucea3 \uad6c\ud604",date:"2023-09-11T00:00:00.000Z",formattedDate:"2023\ub144 9\uc6d4 11\uc77c",tags:[{label:"Woowahan Techcourse",permalink:"/tags/woowahan-techcourse"},{label:"Retrospective",permalink:"/tags/retrospective"}],readingTime:12.27,hasTruncateMarker:!1,authors:[],frontMatter:{title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",slug:"tomcat-retrospective",tags:["Woowahan Techcourse","Retrospective"]},prevItem:{title:"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac",permalink:"/async-exception"},nextItem:{title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958",permalink:"/performance-test-type"}},s={authorsImageUrls:[]},p=[{value:"\ud1b0\ucea3 \uad6c\ud604",id:"\ud1b0\ucea3-\uad6c\ud604",level:3},{value:"\ub2e4\uc774\uc5b4\uadf8\ub7a8",id:"\ub2e4\uc774\uc5b4\uadf8\ub7a8",level:3},{value:"\ucf54\ub4dc \ub9ac\ubdf0",id:"\ucf54\ub4dc-\ub9ac\ubdf0",level:3},{value:"SessionConfig",id:"sessionconfig",level:3},{value:"HTTP \uc218\uc5c5",id:"http-\uc218\uc5c5",level:3},{value:"Thread \uc218\uc5c5",id:"thread-\uc218\uc5c5",level:3},{value:"\ub9c8\uce58\uba70",id:"\ub9c8\uce58\uba70",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],m={toc:p};function d(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ud1b0\ucea3-\uad6c\ud604"},"\ud1b0\ucea3 \uad6c\ud604"),(0,a.kt)("p",null,"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\ub97c \uc9c0\uc6d0\ud560 \ub54c \uac1d\uccb4\uc9c0\ud5a5\uacfc \uad00\ub828\ub41c \ubbf8\uc158\ub3c4 \uae30\ub300\ub97c \ub9ce\uc774 \ud588\uc9c0\ub9cc \ub808\ubca8 4\uc5d0 \uc9c4\ud589\ud558\ub294 \ubbf8\uc158\uc774 \uc815\ub9d0 \ud558\uace0 \uc2f6\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uadf8\ub798\uc11c \ubbf8\uc158\uc744 \ud560 \uc218 \uc788\uc744\uae4c\ub77c\ub294 \uac71\uc815 \ubc18, \ubbf8\uc158\uc5d0 \ub300\ud55c \uae30\ub300 \ubc18\uc73c\ub85c \ubd80\ud47c \ub9c8\uc74c\uc744 \uac00\uc9c0\uace0 \ubbf8\uc158\uc744 \uc2dc\uc791\ud588\ub358 \uac83 \uac19\ub2e4. "),(0,a.kt)("p",null,"\uc774\ubc88 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc801\uc808\ud558\uac8c \ucd94\uc0c1\ud654\ud558\uace0, \ubbf8\uc158\uc758 \ubcf8\uc9c8\uc744 \uc774\ud574\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158\uc740 ",(0,a.kt)("a",{parentName:"p",href:"https://datatracker.ietf.org/doc/html/rfc2616/"},"RFC 2616"),"\uc5d0 \uba85\uc2dc\ub41c \uc2a4\ud399(\uc644\ubcbd\ud558\uc9c0 \uc54a\uc9c0\ub9cc \ubbf8\uc158\uc5d0\uc11c \uc8fc\uc5b4\uc9c4 \uc694\uad6c\uc0ac\ud56d\ub9cc \ub9cc\uc871\ud558\ub3c4\ub85d)\uc73c\ub85c \uc694\uccad\uc744 \ubc1b\uc544 \ucc98\ub9ac \ud6c4 \ubc18\ud658\ud558\ub294\ub370 \uc9d1\uc911\ud588\ub2e4. "),(0,a.kt)("h3",{id:"\ub2e4\uc774\uc5b4\uadf8\ub7a8"},"\ub2e4\uc774\uc5b4\uadf8\ub7a8"),(0,a.kt)("p",null,"Catalina\ub294 Tomcat\uc758 \uc11c\ube14\ub9bf \ucee8\ud14c\uc774\ub108, Coyote\ub294 HTTP 1.1 \uc6f9 \uc11c\ubc84\ub97c \uc9c0\uc6d0\ud558\ub294 \uad6c\uc131 \uc694\uc18c\ub77c\uace0 \uc0dd\uac01\ud558\uace0 \uc544\ub798\uc640 \uac19\uc774 \uad6c\uc131\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0ac\uc2e4 \ub0b4\ubd80 \uad6c\uc870\ub97c \uae4a\uac8c \uacf5\ubd80\ud560 \uc2dc\uac04\uc744 \uac00\uc9c0\uc9c0 \ubabb\ud574\uc11c \uac01 \uad6c\uc131 \uc694\uc18c\uac00 \uc65c \ud574\ub2f9 \uc704\uce58\uc5d0 \uc788\ub294\uc9c0 \uc644\ubcbd\ud558\uac8c \uc124\uba85\ud558\uc9c0\ub294 \ubabb\ud558\uc9c0\ub9cc \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc774\uac74 \uc5ec\uae30\uc5d0 \uc788\uc73c\uba74 \uc88b\uc744 \uac83 \uac19\uc740\ub370? \ub77c\ub294 \uc0dd\uac01\uc774 \ub4e4\uba74 \uc801\uc808\ud55c \ud328\ud0a4\uc9c0\uc5d0 \uc704\uce58\uc2dc\ud0a4\ub294 \ubc29\ud5a5\uc73c\ub85c \uc9c4\ud589\uc744 \ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub610\ud55c \uc801\uc808\ud558\uac8c \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc758\uc874\uc131 \ubc29\ud5a5\uc744 \ub2e8\ubc29\ud5a5\uc73c\ub85c \ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. "),(0,a.kt)("mermaid",{value:"graph LR\n\tsubgraph coyote\n\t\tHP[Http11Processor] --\x3e A\n\t\tHP --\x3e HttpRequestParser\n\t\tHP --\x3e HttpResponseGenerator\n\t\tA[Adapter]\n end\n\n subgraph catalina\n\t\tRA[RequestAdapter] -.-> A\n\t\tAC[AbstractController] -.-> C[Controller]\n\t\tStaticController -.-> AC\n\t\tSM[SessionManger] -.-> Manager\n\t\tTC[Tomcat] --\x3e RA\n\t\tRA --\x3e C\n\t\tRA --\x3e Manager\n\t\tRA --\x3e RM\n\t\tRM[RequestMapper] --\x3e C\n end\n\n subgraph jwp\n\t\tLC[LoginController] -.-> AC\n\t\tApplication --\x3e TC\n end\n"}),(0,a.kt)("h3",{id:"\ucf54\ub4dc-\ub9ac\ubdf0"},"\ucf54\ub4dc \ub9ac\ubdf0"),(0,a.kt)("p",null,"\ud06c\ub8e8 \uc911 \ud55c \uba85\uc774 \ub098\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\uace0, \ub0b4\uac00 \ub2e4\ub978 \ud06c\ub8e8\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\ub294 \ud615\ud0dc\ub85c \uc9c4\ud589\uc774 \ub418\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\uc758 \ub9ac\ubdf0\uc5b4\ub294 \ub514\ub178, \ub9ac\ubdf0\uc774\ub294 \ud544\ub9bd\uc774\uc5c8\ub2e4. "),(0,a.kt)("p",null,"\ub514\ub178(\ub9e4\uc758 \ub208\uc774 \uc544\ub2cc \uacf5\ub8e1\uc758 \ub208?)\uac00 \ub9e4\uc6b0 \uaf3c\uaf3c\ud558\uac8c \ucf54\ub4dc \ub9ac\ubdf0\ub97c \ud574\uc8fc\uc5b4\uc11c \uc870\uae08 \ub354 \ub098\uc740 \ucf54\ub4dc\ub97c \uc791\uc131\ud560 \uc218 \uc788\uc5c8\uace0, \ud544\ub9bd\uc758 \ucf54\ub4dc\uc5d0\uc11c\ub294 \uaf3c\uaf3c\ud558\uac8c \uc608\uc678\ucc98\ub9ac \ud558\ub294 \ubd80\ubd84\uc744 \ubc30\uc6b8 \uc218 \uc788\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud55c \uac00\uc9c0 \uc544\uc26c\uc6b4 \uc810\uc740 \ud544\ub9bd\uc5d0\uac8c \uc791\uc131\ud55c \ub098\uc758 \ucf54\uba58\ud2b8\ub4e4\uc774 \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uacbd\ud5d8 \uae30\ubc18\uc73c\ub85c \uc791\uc131\ud55c \ub0b4\uc6a9\uc774 \ub9ce\uc544 \uadfc\uac70\uac00 \uc870\uae08 \ubd80\uc871\ud588\uace0, \uc815\ub9ac\ub418\uc9c0 \uc54a\uc740 \ubd80\ubd84\uc774 \ub9ce\uc558\ub358 \uac83 \uac19\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e4\uc74c \ubbf8\uc158\ubd80\ud130 \ub9ac\ubdf0\ud560 \ub54c \uc870\uae08 \ub354 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ub354 \uc88b\uc740 \ub0b4\uc6a9\uc744 \ud06c\ub8e8\ub4e4\uacfc \uacf5\uc720\ud560 \uc218 \uc788\ub3c4\ub85d \ub178\ub825\ud574\uc57c\uaca0\ub2e4. "),(0,a.kt)("h3",{id:"sessionconfig"},"SessionConfig"),(0,a.kt)("p",null,"\ubbf8\uc158\uc744 \uc9c4\ud589 \uc911 catalina \ud328\ud0a4\uc9c0\uc758 Session \uad00\ub828 \ubd80\ubd84\uc744 \ubcf4\uba74\uc11c \uc911\ubcf5 \ub85c\uc9c1\uc744 \uac1c\uc120\ud574 \ubcfc \uc218 \uc788\uc744 \uac83 \uac19\uc544 ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/apache/tomcat/pull/660"},"\ucee8\ud2b8\ub9ac\ubdf0\ud2b8"),"\ub97c \uc2dc\ub3c4\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc138\uc158 \ucfe0\ud0a4\uc758 \uc774\ub984\uc744 \uac00\uc838\uc624\ub294 Util \ud074\ub798\uc2a4\uc758 \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub294\ub370 \uae30\ubcf8 \uac12\uc740 JSESSIONID \uc9c0\ub9cc \uc124\uc815\uc5d0 \ub530\ub77c\uc11c \uc138\uc158 \ucfe0\ud0a4\uba85\uc744 \ub2e4\ub974\uac8c \uc0ac\uc6a9\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \ud574\ub2f9 \ub85c\uc9c1\uc774 \uc788\ub294 \uac83\uc73c\ub85c \uc0dd\uac01\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uae30\uc874\uc758 \ucf54\ub4dc\ub294 \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \ucf54\ub4dc\uc758 \ud750\ub984\uc774 \uc77c\uce58\ud558\uc9c0 \uc54a\uc544\uc11c \uc57d\uac04 \uc774\ud574\ud558\uae30 \uc5b4\ub824\uc6e0\ub2e4. "),(0,a.kt)("p",null,"\ucd08\uae30\uc5d0 \uc694\uccad\ud588\ub358 PR\uc740 \uae30\uc874\uc758 \ucf54\ub4dc\ubcf4\ub2e4 \uc804\uccb4\uc801\uc73c\ub85c \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, context\uac00 null\uc778 \uacbd\uc6b0 \ubc14\ub85c \uae30\ubcf8 \uac12\uc744 \ubc18\ud658\ud568\uc73c\ub85c\uc368 \uc131\ub2a5 \uac1c\uc120\uc758 \ud6a8\uacfc\uac00 \uc788\uc744 \uac70\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uba54\uc778\ud14c\uc774\ub108\uc778 Mark Thomas \ud615\uc774 \ud574\ub2f9 \ub85c\uc9c1\uc758 \uacbd\uc6b0 \ucef4\ud30c\uc77c\ub7ec\uac00 \ud574\ub2f9 \ubd80\ubd84\uc744 \ucd5c\uc801\ud654 \ud560 \uc218 \uc788\uc744 \uac70\ub77c\uace0 \uae30\ub300\ud55c\ub2e4\uace0 \ud588\uace0, \uac00\ub3c5\uc131\uc744 \uac1c\uc120\uc2dc\ucf1c\ubcf4\ub77c\uace0 \uc870\uc5b8\ud574\uc8fc\uc168\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ucef4\ud30c\uc77c\ub7ec \ucd5c\uc801\ud654\ub294 \uace0\ub824\ud574\ubcf4\uc9c0 \ubabb\ud55c \ubd80\ubd84\uc778\ub370, \uc55e\uc73c\ub85c \ud559\uc2b5\ud574\uc57c \ud560 \ubd80\ubd84\uc774 \uc0b0\ub354\ubbf8\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. "),(0,a.kt)("p",null,"\ub0a8\uaca8\uc900 \ucf54\uba58\ud2b8\uc5d0 \ub530\ub77c \ucd5c\uc885\uc801\uc73c\ub85c\ub294 \uc911\ubcf5\ub41c \ucf54\ub4dc\ub97c \uc904\uc774\ub294 \ubc29\ud5a5\uc73c\ub85c \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uacb0\uacfc\uc801\uc73c\ub85c \uae30\uc874 \ub85c\uc9c1 \ub300\ube44 \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \uc720\uc0ac\ud55c \ud750\ub984\uc758 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\uc5ec \uc88b\uc740 \ubc29\ud5a5\uc73c\ub85c \ub9ac\ud329\ud130\ub9c1\uc744 \ud588\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. "),(0,a.kt)(o.Z,{mdxType:"Tabs"},(0,a.kt)(i.Z,{value:"\uae30\uc874",label:"\uae30\uc874",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n\n String result = getConfiguredSessionCookieName(context);\n\n if (result == null) {\n result = DEFAULT_SESSION_COOKIE_NAME;\n }\n\n return result;\n}\n\npublic static String getSessionUriParamName(Context context) {\n\n String result = getConfiguredSessionCookieName(context);\n\n if (result == null) {\n result = DEFAULT_SESSION_PARAMETER_NAME;\n }\n\n return result;\n}\n\nprivate static String getConfiguredSessionCookieName(Context context) {\n\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n if (context != null) {\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc =\n context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n }\n\n return null;\n}\n"))),(0,a.kt)(i.Z,{value:"PR \uc694\uccad",label:"PR \uc694\uccad",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n if (context == null) {\n return DEFAULT_SESSION_COOKIE_NAME;\n }\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\n}\n\npublic static String getSessionUriParamName(Context context) {\n if (context == null) {\n return DEFAULT_SESSION_PARAMETER_NAME;\n }\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\n}\n\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n return defaultName;\n}\n"))),(0,a.kt)(i.Z,{value:"\ucd5c\uc885",label:"\ucd5c\uc885",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\n}\n\npublic static String getSessionUriParamName(Context context) {\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\n}\n\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n if (context != null) {\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n }\n return defaultName;\n}\n")))),(0,a.kt)("h3",{id:"http-\uc218\uc5c5"},"HTTP \uc218\uc5c5"),(0,a.kt)("p",null,"\ubbf8\uc158 \uc911\uac04\uc5d0 \uc9c4\ud589\ub418\uc5c8\ub358 HTTP \uc218\uc5c5\uc5d0\ub294 HTTP\ub97c \uc801\uc808\ud558\uac8c \ud65c\uc6a9\ud558\ub294 \ubd80\ubd84\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud56d\uc0c1 \uc131\ub2a5 \uac1c\uc120\uc744 \uc704\ud574 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ub2e8\uc5d0\uc11c \ucd5c\uc801\ud654\ud574\ubcf4\ub824\uace0 \ub178\ub825\uc744 \ud588\uc9c0\ub9cc, \ub354 \uc801\uc740 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ud6a8\uc728\uc801\uc73c\ub85c \uc131\ub2a5\uc744 \uac1c\uc120\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc5d0 \ub300\ud574 \uc54c \uc218 \uc788\uc5c8\ub358 \uc218\uc5c5\uc774\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","HTTP \uc555\ucd95, HTTP \uce90\uc2f1, \ub9ac\uc18c\uc2a4 \ucd5c\uc801\ud654 \uae30\ubc95\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4. "),(0,a.kt)("p",null,"\uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \ub2e4\uc74c \uc635\uc158\uc744 \uc124\uc815\ud558\uc5ec http\uc758 \uc1a1\uc218\uc2e0\uc758 \uc555\ucd95\uc744 \uc9c4\ud589\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yml"},"server:\n compression:\n enabled: true\n")),(0,a.kt)("p",null,"\uc218\uc5c5 \uc911 \ud574\ub2f9 \uc555\ucd95 \uc131\ub2a5\uc774 \uc88b\ub2e4\uba74 \uc65c \uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \uae30\ubcf8 \uac12\uc73c\ub85c \uc124\uc815\uc744 \ud558\uc9c0 \uc54a\uc558\ub294\uc9c0 \uad81\uae08\ud574\uc84c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uad81\uae08\uc99d\uc744 \ud574\uc18c\ud558\uc9c0 \ubabb\ud588\ub294\ub370 \ub9d0\ub791\uc774 \uc7a1\ub2f4 \ucc44\ub110\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc740 ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/spring-projects/spring-boot/issues/21369"},"issue"),"\ub97c \ucc3e\uc544\uc8fc\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub0b4\uc6a9\uc744 \uc694\uc57d\ud574 \ubcf4\uc790\uba74 WAS \ubcc4\ub85c \uc555\ucd95\uc744 \ud558\uae30 \uc704\ud574 \uc124\uc815\ud574\uc57c \ud558\ub294 \uac83\uc774 \ub2e4\ub974\uace0, \ubb34\uc870\uac74 \uc555\ucd95\uc744 \ud558\ub294 \uac83\uc774 \ucd5c\uc801\uc758 \uacbd\uc6b0\uac00 \uc544\ub2d0 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \uae30\ubcf8\uac12\uc73c\ub85c \uc124\uc815\ud558\uc9c0 \uc54a\ub294 \uac83 \uac19\ub2e4. "),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"If you're developing a public-facing application then it's probably likely gzip compression would be worthwhile. If, however, you're a microservice application and you're in a dataceter, you may well prefer to reduce CPU load because you know you'll only be talking to other microservices and you have a reliable gigabit network.")),(0,a.kt)("p",null,"Phil Webb \ud615\ub2d8\uc758 \ub9d0\uc5d0 \ub530\ub974\uba74 \uc77c\ubc18\uc801\uc778 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uac1c\ubc1c\ud558\ub294 \uacbd\uc6b0 gzip \uc555\ucd95\uc744 \uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\uc9c0\ub9cc, MSA \ud658\uacbd + \ub370\uc774\ud130 \uc13c\ud130\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \uc624\uc9c1 \ub2e4\ub978 MSA \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uacfc \ud1b5\uc2e0\ud558\uace0, \uace0\uc131\ub2a5 \ub124\ud2b8\uc6cc\ud06c\uac00 \uc788\uae30 \ub54c\ubb38\uc5d0 CPU \ubd80\ud558\ub97c \uc904\uc774\ub294 \uac83\uc774 \uc6b0\uc120\uc2dc \ub420 \uc218\ub3c4 \uc788\ub2e4\ub294 \uac83\uc774\uc5c8\ub2e4. "),(0,a.kt)("p",null,"\uc774\uc678\uc5d0\ub3c4 \uc758\ub3c4\ud558\uc9c0 \uc54a\uc740 \uce90\uc2f1\uc744 \ub9c9\uae30 \uc704\ud574 \ud734\ub9ac\uc2a4\ud2f1 \uce90\uc2f1\uc744 \uc81c\uac70\ud558\uac70\ub098, \uac1c\uc778 \uc815\ubcf4 \uc720\ucd9c\uc744 \ub9c9\uae30 \uc704\ud574 \uc751\ub2f5 \ud5e4\ub354\uc5d0 private\uc744 \uc124\uc815, ETag\ub3c4 \ud559\uc2b5\ud588\ub2e4. "),(0,a.kt)("admonition",{title:"ETag",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"ETag HTTP \uc751\ub2f5 \ud5e4\ub354\ub294 \ud2b9\uc815 \ubc84\uc804\uc758 \ub9ac\uc18c\uc2a4\ub97c \uc2dd\ubcc4\ud558\ub294 \uc2dd\ubcc4\uc790\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc6f9 \uc11c\ubc84\uac00 \ub0b4\uc6a9\uc744 \ud655\uc778\ud558\uace0 \ubcc0\ud558\uc9c0 \uc54a\uc558\uc73c\uba74, \uc6f9 \uc11c\ubc84\ub85c full \uc694\uccad\uc744 \ubcf4\ub0b4\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0, \uce90\uc2dc\uac00 \ub354 \ud6a8\uc728\uc801\uc774\uac8c \ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","MDN")),(0,a.kt)("h3",{id:"thread-\uc218\uc5c5"},"Thread \uc218\uc5c5"),(0,a.kt)("p",null,"\uc2a4\ub808\ub4dc\uc5d0 \ub300\ud55c \uc218\uc5c5\uc744 \ub4e4\uc5c8\uc9c0\ub9cc, \ubcf5\uc7a1\ud55c \ub0b4\uc6a9\ub3c4 \uc6cc\ub099 \ub9ce\uc558\uae30 \ub54c\ubb38\uc5d0 \uc124\uba85\ud558\ub77c\uace0 \ud558\uba74 \uc798 \ubabb\ud560 \uac83 \uac19\uc740 \ub290\ub08c\uc774 \ub4e0\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud604\uc7ac \ud504\ub85c\uc81d\ud2b8, \ubbf8\uc158, \ud14c\ucf54\ud1a1 \uc900\ube44\ub97c \ubcd1\ud589\ud574\uc57c \ud574\uc11c \uc138\ubd80\uc801\uc778 \ub0b4\uc6a9\uc740 \uc2dc\uac04 \ub0a0 \ub54c \ubcf5\uc2b5\ud558\ub824\uace0 \ud55c\ub2e4. "),(0,a.kt)("p",null,"\uc2a4\ub808\ub4dc\ub97c \uc774\ud574\ud558\uace0, WAS\uc5d0 \uc2a4\ub808\ub4dc \uc124\uc815 \uad00\ub828\ud55c \uc2e4\uc2b5\uc774 \uc788\uc5c8\ub294\ub370 \ud14c\uc624\uc640 \uac19\uc774 1\uc2dc\uac04 \uc815\ub3c4 \ud398\uc5b4\ub85c Thread \uc2e4\uc2b5\uc744 \uc9c4\ud589\ud574 \ubcf4\uc558\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud559\uc2b5\ud55c \ub0b4\uc6a9\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. "),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"threads.max"),": Tomcat\uc758 \ucd5c\ub300 \uc2a4\ub808\ub4dc \uac1c\uc218",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"max-connections"),": Tomcat\uc774 \uc720\uc9c0\ud560 \uc218 \uc788\ub294 \ucd5c\ub300 \ucee4\ub125\uc158 \uac1c\uc218",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"accept-count"),": \ucd5c\ub300 \uc5f0\uacb0 \uc218\uc5d0 \ub3c4\ub2ec\ud588\uc744 \ub54c \uc5f0\uacb0 \uc694\uccad\uc5d0 \ub300\ud574 \uc6b4\uc601 \uccb4\uc81c\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \ub300\uae30\uc5f4\uc758 \ucd5c\ub300 \uae38\uc774. \ud574\ub2f9 Queue\uc5d0 \uc694\uccad\uc774 \uc313\uc774\ub294 \uac83\uc740 Tomcat\uc774 \ub354 \uc774\uc0c1 \uc694\uccad\uc744 \ubc1b\uc744 \uc218 \uc5c6\ub2e4\ub294 \ub73b\uc774\ub2e4. accpet-count queue\uc5d0\ub3c4 \uc694\uccad\uc774 \uac00\ub4dd\ucc28\uba74 \uadf8 \uc774\ud6c4\uc5d0 \uc624\ub294 \uc694\uccad\uc740 \uac70\ubd80\ub41c\ub2e4. "),(0,a.kt)("mermaid",{value:'graph LR\n C("Client") -- request --\x3e ACQ("\uc6b4\uc601\uccb4\uc81c\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue \n size = accept-count") --\x3e TCQ("Tomcat Connector\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue\n size = max-connections") --\x3e TP("Thread Pool\n size = threads.max")'}),(0,a.kt)("h3",{id:"\ub9c8\uce58\uba70"},"\ub9c8\uce58\uba70"),(0,a.kt)("p",null,"\uc2dc\uac04\uc740 \ub108\ubb34 \ube60\ub974\uac8c \uac00\uace0 \ud560 \uc77c\uc740 \ub9ce\uc740 \uac83 \uac19\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc6b0\uc120\uc21c\uc704\ub97c \uc798 \uc815\ud558\uace0 \ud559\uc2b5\uc744 \uc9c4\ud589\ud574\uc57c\uaca0\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud604\uc7ac \ub370\uc774\ud130 \ub2e4\ub8e8\ub294 \ubd80\ubd84(DB)\uc5d0 \ub300\ud55c \ud559\uc2b5\uc774 \ub9ce\uc774 \ubd80\uc871\ud55c \uac83 \uac19\ub2e4. \ud574\ub2f9 \ubd80\ubd84\uc740 \ud14c\ucf54\ud1a1\uc774 \ub05d\ub098\ub294\ub300\ub85c \ucc44\uc6cc\uc57c\uaca0\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://datatracker.ietf.org/doc/html/rfc2616/"},"RFC 2616"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/ETag"},"ETag, mdn"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://tomcat.apache.org/tomcat-8.5-doc/config/http.html"},"Apache Tomcat 8 Configuration Reference"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://bcho.tistory.com/788"},"Apache Tomcat Tuning, Terry Cho"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://dev-ws.tistory.com/96"},"maxThreads, maxConnections, acceptCount\ub85c Tomcat \ud29c\ub2dd\ud558\uae30")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/96adae60.b0a3bff9.js b/assets/js/96adae60.b3f5d5ae.js similarity index 57% rename from assets/js/96adae60.b0a3bff9.js rename to assets/js/96adae60.b3f5d5ae.js index 1a9319c92..1f49234d5 100644 --- a/assets/js/96adae60.b0a3bff9.js +++ b/assets/js/96adae60.b3f5d5ae.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[172],{54217:e=>{e.exports=JSON.parse('{"permalink":"/page/19","page":19,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/18","nextPage":"/page/20","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[172],{54217:e=>{e.exports=JSON.parse('{"permalink":"/page/19","page":19,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/18","nextPage":"/page/20","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/981f7647.b7931b3d.js b/assets/js/981f7647.3ede4ce8.js similarity index 97% rename from assets/js/981f7647.b7931b3d.js rename to assets/js/981f7647.3ede4ce8.js index bdcaa758f..02c55cc97 100644 --- a/assets/js/981f7647.b7931b3d.js +++ b/assets/js/981f7647.3ede4ce8.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2947],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function l(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var c=n.createContext({}),p=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},u=function(e){var t=p(e.components);return n.createElement(c.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),d=p(r),f=a,g=d["".concat(c,".").concat(f)]||d[f]||s[f]||o;return r?n.createElement(g,l(l({ref:t},u),{},{components:r})):n.createElement(g,l({ref:t},u))}));function f(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,l=new Array(o);l[0]=d;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i.mdxType="string"==typeof e?e:a,l[1]=i;for(var p=2;p{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>s,frontMatter:()=>o,metadata:()=>i,toc:()=>p});var n=r(87462),a=(r(67294),r(3905));const o={title:"\ud328\ud0a4\uc9c0",slug:"/design/package",tags:["package"]},l=void 0,i={unversionedId:"\uc124\uacc4/\ud328\ud0a4\uc9c0",id:"\uc124\uacc4/\ud328\ud0a4\uc9c0",title:"\ud328\ud0a4\uc9c0",description:"\uacc4\uce35 \uae30\ubc18 \ud328\ud0a4\uc9c0",source:"@site/docs/\uc124\uacc4/\ud328\ud0a4\uc9c0.mdx",sourceDirName:"\uc124\uacc4",slug:"/design/package",permalink:"/docs/design/package",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uc124\uacc4/\ud328\ud0a4\uc9c0.mdx",tags:[{label:"package",permalink:"/docs/tags/package"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\ud328\ud0a4\uc9c0",slug:"/design/package",tags:["package"]},sidebar:"tutorialSidebar",previous:{title:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c",permalink:"/docs/culture/postmortem"},next:{title:"Throughput \ubaa9\ud46f\uac12",permalink:"/docs/performance/throughput"}},c={},p=[{value:"\uacc4\uce35 \uae30\ubc18 \ud328\ud0a4\uc9c0",id:"\uacc4\uce35-\uae30\ubc18-\ud328\ud0a4\uc9c0",level:3},{value:"\uae30\ub2a5 \uae30\ubc18 \ud328\ud0a4\uc9c0",id:"\uae30\ub2a5-\uae30\ubc18-\ud328\ud0a4\uc9c0",level:3},{value:"\ud3ec\ud2b8\uc640 \uc5b4\ub311\ud130",id:"\ud3ec\ud2b8\uc640-\uc5b4\ub311\ud130",level:3},{value:"\ucef4\ud3ec\ub10c\ud2b8 \uae30\ubc18 \ud328\ud0a4\uc9c0",id:"\ucef4\ud3ec\ub10c\ud2b8-\uae30\ubc18-\ud328\ud0a4\uc9c0",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:p};function s(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\uacc4\uce35-\uae30\ubc18-\ud328\ud0a4\uc9c0"},"\uacc4\uce35 \uae30\ubc18 \ud328\ud0a4\uc9c0"),(0,a.kt)("p",null,"\uc804\ud1b5\uc801\uc778 \uc218\ud3c9 \uacc4\uce35\ud615 \uc544\ud0a4\ud14d\ucc98",(0,a.kt)("br",{parentName:"p"}),"\n","\uae30\uc220\uc801\uc778 \uad00\uc810\uc5d0\uc11c \ud574\ub2f9 \ucf54\ub4dc\uac00 \ud558\ub294 \uc77c\uc5d0 \uae30\ubc18\ud574 \ucf54\ub4dc\ub97c \ubd84\ud560"),(0,a.kt)("h3",{id:"\uae30\ub2a5-\uae30\ubc18-\ud328\ud0a4\uc9c0"},"\uae30\ub2a5 \uae30\ubc18 \ud328\ud0a4\uc9c0"),(0,a.kt)("p",null,"\uc11c\ub85c \uc5f0\uad00\ub41c \uae30\ub2a5, \ub3c4\uba54\uc778 \uac1c\ub150, \ub610\ub294 Aggregate Root\uc5d0 \uae30\ubc18\ud558\uc5ec \uc218\uc9c1\uc758 \uc587\uc740 \uc870\uac01\uc73c\ub85c \ucf54\ub4dc\ub97c \ub098\ub204\ub294 \ubc29\uc2dd"),(0,a.kt)("h3",{id:"\ud3ec\ud2b8\uc640-\uc5b4\ub311\ud130"},"\ud3ec\ud2b8\uc640 \uc5b4\ub311\ud130"),(0,a.kt)("p",null,"\uc5c5\ubb34/\ub3c4\uba54\uc778\uc5d0 \ucd08\uc810\uc744 \ub454 \ucf54\ub4dc\uac00 \ud504\ub808\uc784\uc6cc\ud06c \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uac19\uc740 \uae30\uc220\uc801\uc778 \uc138\ubd80 \uad6c\ud604\uacfc \ub3c5\ub9bd\uc801\uc774\uba70 \ubd84\ub9ac\ub41c \uc544\ud0a4\ud14d\ucc98\ub97c \ub9cc\ub4e4\uae30 \uc704\ud574 \uc0ac\uc6a9"),(0,a.kt)("h3",{id:"\ucef4\ud3ec\ub10c\ud2b8-\uae30\ubc18-\ud328\ud0a4\uc9c0"},"\ucef4\ud3ec\ub10c\ud2b8 \uae30\ubc18 \ud328\ud0a4\uc9c0"),(0,a.kt)("p",null,"\ud070 \ub2e8\uc704\uc758 \ub2e8\uc77c \ucef4\ud3ec\ub10c\ud2b8\uc640 \uad00\ub828\ub41c \ubaa8\ub4e0 \ucc45\uc784\uc744 \ud558\ub098\uc758 \uc790\ubc14 \ud328\ud0a4\uc9c0\ub85c \ubb36\ub294 \ub370 \uc8fc\uc548\uc810\uc744 \ub460",(0,a.kt)("br",{parentName:"p"}),"\n","\ubaa8\ub178\ub9ac\ud2f1 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0\uc11c \ucef4\ud3ec\ub10c\ud2b8\ub97c \uc798 \uc815\uc758\ud558\uba74 MSA\ub85c \uac00\uae30 \uc704\ud55c \ubc1c\ud310\uc73c\ub85c \uc0bc\uc744 \uc218 \uc788\uc74c"),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\ud074\ub9b0 \uc544\ud0a4\ud14d\ucc98, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4 p.316"))}s.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2947],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function l(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var c=n.createContext({}),p=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},u=function(e){var t=p(e.components);return n.createElement(c.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),d=p(r),f=a,g=d["".concat(c,".").concat(f)]||d[f]||s[f]||o;return r?n.createElement(g,l(l({ref:t},u),{},{components:r})):n.createElement(g,l({ref:t},u))}));function f(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,l=new Array(o);l[0]=d;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i.mdxType="string"==typeof e?e:a,l[1]=i;for(var p=2;p{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>s,frontMatter:()=>o,metadata:()=>i,toc:()=>p});var n=r(87462),a=(r(67294),r(3905));const o={title:"\ud328\ud0a4\uc9c0",slug:"/design/package",tags:["package"]},l=void 0,i={unversionedId:"\uc124\uacc4/\ud328\ud0a4\uc9c0",id:"\uc124\uacc4/\ud328\ud0a4\uc9c0",title:"\ud328\ud0a4\uc9c0",description:"\uacc4\uce35 \uae30\ubc18 \ud328\ud0a4\uc9c0",source:"@site/docs/\uc124\uacc4/\ud328\ud0a4\uc9c0.mdx",sourceDirName:"\uc124\uacc4",slug:"/design/package",permalink:"/docs/design/package",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uc124\uacc4/\ud328\ud0a4\uc9c0.mdx",tags:[{label:"package",permalink:"/docs/tags/package"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\ud328\ud0a4\uc9c0",slug:"/design/package",tags:["package"]},sidebar:"tutorialSidebar",previous:{title:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c",permalink:"/docs/culture/postmortem"},next:{title:"Throughput \ubaa9\ud46f\uac12",permalink:"/docs/performance/throughput"}},c={},p=[{value:"\uacc4\uce35 \uae30\ubc18 \ud328\ud0a4\uc9c0",id:"\uacc4\uce35-\uae30\ubc18-\ud328\ud0a4\uc9c0",level:3},{value:"\uae30\ub2a5 \uae30\ubc18 \ud328\ud0a4\uc9c0",id:"\uae30\ub2a5-\uae30\ubc18-\ud328\ud0a4\uc9c0",level:3},{value:"\ud3ec\ud2b8\uc640 \uc5b4\ub311\ud130",id:"\ud3ec\ud2b8\uc640-\uc5b4\ub311\ud130",level:3},{value:"\ucef4\ud3ec\ub10c\ud2b8 \uae30\ubc18 \ud328\ud0a4\uc9c0",id:"\ucef4\ud3ec\ub10c\ud2b8-\uae30\ubc18-\ud328\ud0a4\uc9c0",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:p};function s(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\uacc4\uce35-\uae30\ubc18-\ud328\ud0a4\uc9c0"},"\uacc4\uce35 \uae30\ubc18 \ud328\ud0a4\uc9c0"),(0,a.kt)("p",null,"\uc804\ud1b5\uc801\uc778 \uc218\ud3c9 \uacc4\uce35\ud615 \uc544\ud0a4\ud14d\ucc98",(0,a.kt)("br",{parentName:"p"}),"\n","\uae30\uc220\uc801\uc778 \uad00\uc810\uc5d0\uc11c \ud574\ub2f9 \ucf54\ub4dc\uac00 \ud558\ub294 \uc77c\uc5d0 \uae30\ubc18\ud574 \ucf54\ub4dc\ub97c \ubd84\ud560"),(0,a.kt)("h3",{id:"\uae30\ub2a5-\uae30\ubc18-\ud328\ud0a4\uc9c0"},"\uae30\ub2a5 \uae30\ubc18 \ud328\ud0a4\uc9c0"),(0,a.kt)("p",null,"\uc11c\ub85c \uc5f0\uad00\ub41c \uae30\ub2a5, \ub3c4\uba54\uc778 \uac1c\ub150, \ub610\ub294 Aggregate Root\uc5d0 \uae30\ubc18\ud558\uc5ec \uc218\uc9c1\uc758 \uc587\uc740 \uc870\uac01\uc73c\ub85c \ucf54\ub4dc\ub97c \ub098\ub204\ub294 \ubc29\uc2dd"),(0,a.kt)("h3",{id:"\ud3ec\ud2b8\uc640-\uc5b4\ub311\ud130"},"\ud3ec\ud2b8\uc640 \uc5b4\ub311\ud130"),(0,a.kt)("p",null,"\uc5c5\ubb34/\ub3c4\uba54\uc778\uc5d0 \ucd08\uc810\uc744 \ub454 \ucf54\ub4dc\uac00 \ud504\ub808\uc784\uc6cc\ud06c \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uac19\uc740 \uae30\uc220\uc801\uc778 \uc138\ubd80 \uad6c\ud604\uacfc \ub3c5\ub9bd\uc801\uc774\uba70 \ubd84\ub9ac\ub41c \uc544\ud0a4\ud14d\ucc98\ub97c \ub9cc\ub4e4\uae30 \uc704\ud574 \uc0ac\uc6a9"),(0,a.kt)("h3",{id:"\ucef4\ud3ec\ub10c\ud2b8-\uae30\ubc18-\ud328\ud0a4\uc9c0"},"\ucef4\ud3ec\ub10c\ud2b8 \uae30\ubc18 \ud328\ud0a4\uc9c0"),(0,a.kt)("p",null,"\ud070 \ub2e8\uc704\uc758 \ub2e8\uc77c \ucef4\ud3ec\ub10c\ud2b8\uc640 \uad00\ub828\ub41c \ubaa8\ub4e0 \ucc45\uc784\uc744 \ud558\ub098\uc758 \uc790\ubc14 \ud328\ud0a4\uc9c0\ub85c \ubb36\ub294 \ub370 \uc8fc\uc548\uc810\uc744 \ub460",(0,a.kt)("br",{parentName:"p"}),"\n","\ubaa8\ub178\ub9ac\ud2f1 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0\uc11c \ucef4\ud3ec\ub10c\ud2b8\ub97c \uc798 \uc815\uc758\ud558\uba74 MSA\ub85c \uac00\uae30 \uc704\ud55c \ubc1c\ud310\uc73c\ub85c \uc0bc\uc744 \uc218 \uc788\uc74c"),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\ud074\ub9b0 \uc544\ud0a4\ud14d\ucc98, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4 p.316"))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/9cfe8fd1.9d0c80a1.js b/assets/js/9cfe8fd1.0b589244.js similarity index 57% rename from assets/js/9cfe8fd1.9d0c80a1.js rename to assets/js/9cfe8fd1.0b589244.js index 18e9222ed..65549a77f 100644 --- a/assets/js/9cfe8fd1.9d0c80a1.js +++ b/assets/js/9cfe8fd1.0b589244.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7725],{97113:e=>{e.exports=JSON.parse('{"permalink":"/page/18","page":18,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/17","nextPage":"/page/19","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7725],{97113:e=>{e.exports=JSON.parse('{"permalink":"/page/18","page":18,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/17","nextPage":"/page/19","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/9e477a5e.66ca6253.js b/assets/js/9e477a5e.85b777bc.js similarity index 97% rename from assets/js/9e477a5e.66ca6253.js rename to assets/js/9e477a5e.85b777bc.js index e4e981e10..04c2db9c8 100644 --- a/assets/js/9e477a5e.66ca6253.js +++ b/assets/js/9e477a5e.85b777bc.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[8312],{3905:(e,t,r)=>{r.d(t,{Zo:()=>m,kt:()=>d});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function p(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),c=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):p(p({},t),e)),r},m=function(e){var t=c(e.components);return n.createElement(l.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,l=e.parentName,m=i(e,["components","mdxType","originalType","parentName"]),u=c(r),d=o,f=u["".concat(l,".").concat(d)]||u[d]||s[d]||a;return r?n.createElement(f,p(p({ref:t},m),{},{components:r})):n.createElement(f,p({ref:t},m))}));function d(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,p=new Array(a);p[0]=u;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i.mdxType="string"==typeof e?e:o,p[1]=i;for(var c=2;c{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>p,default:()=>s,frontMatter:()=>a,metadata:()=>i,toc:()=>c});var n=r(87462),o=(r(67294),r(3905));const a={title:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c",slug:"/culture/postmortem",tags:["postmortem"]},p=void 0,i={unversionedId:"\ubb38\ud654/\ud3ec\uc2a4\ud2b8_\ubaa8\ud15c",id:"\ubb38\ud654/\ud3ec\uc2a4\ud2b8_\ubaa8\ud15c",title:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c",description:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c(Postmortem)",source:"@site/docs/\ubb38\ud654/\ud3ec\uc2a4\ud2b8_\ubaa8\ud15c.mdx",sourceDirName:"\ubb38\ud654",slug:"/culture/postmortem",permalink:"/docs/culture/postmortem",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ubb38\ud654/\ud3ec\uc2a4\ud2b8_\ubaa8\ud15c.mdx",tags:[{label:"postmortem",permalink:"/docs/tags/postmortem"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c",slug:"/culture/postmortem",tags:["postmortem"]},sidebar:"tutorialSidebar",previous:{title:"\ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131",permalink:"/docs/monitoring/intro"},next:{title:"\ud328\ud0a4\uc9c0",permalink:"/docs/design/package"}},l={},c=[{value:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c(Postmortem)",id:"\ud3ec\uc2a4\ud2b8-\ubaa8\ud15cpostmortem",level:3},{value:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c\uc5d0 \ub2f4\uaca8\uc57c \ud558\ub294 \ub0b4\uc6a9",id:"\ud3ec\uc2a4\ud2b8-\ubaa8\ud15c\uc5d0-\ub2f4\uaca8\uc57c-\ud558\ub294-\ub0b4\uc6a9",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],m={toc:c};function s(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},m,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h3",{id:"\ud3ec\uc2a4\ud2b8-\ubaa8\ud15cpostmortem"},"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c(Postmortem)"),(0,o.kt)("p",null,"\uc2e4\ud328\ud55c \uadfc\ubcf8 \uc6d0\uc778\uc744 \ubd84\uc11d\ud558\uc5ec \ubb38\uc11c\ub85c \ub0a8\uae30\ub294 \uac83"),(0,o.kt)("h3",{id:"\ud3ec\uc2a4\ud2b8-\ubaa8\ud15c\uc5d0-\ub2f4\uaca8\uc57c-\ud558\ub294-\ub0b4\uc6a9"},"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c\uc5d0 \ub2f4\uaca8\uc57c \ud558\ub294 \ub0b4\uc6a9"),(0,o.kt)("p",null,"\uc0ac\uac74\uc758 \uac1c\uc694",(0,o.kt)("br",{parentName:"p"}),"\n","\uc0ac\uac74\uc744 \uc778\uc9c0\ud558\uace0 \ud574\uacb0\uc5d0 \uc774\ub974\uae30\uae4c\uc9c0\uc758 \ud0c0\uc784\ub77c\uc778",(0,o.kt)("br",{parentName:"p"}),"\n","\uc0ac\uac74\uc758 \uadfc\ubcf8 \uc6d0\uc778",(0,o.kt)("br",{parentName:"p"}),"\n","\uc601\ud5a5\uacfc \ud53c\ud574 \ud3c9\uac00",(0,o.kt)("br",{parentName:"p"}),"\n","\ubb38\uc81c\ub97c \uc989\uc2dc \ud574\uacb0\ud558\uae30 \uc704\ud55c \uc870\uce58 \ud56d\ubaa9(\uc18c\uc720\uc790 \uba85\uc2dc)",(0,o.kt)("br",{parentName:"p"}),"\n","\uac1c\ubc1c \ubc29\uc9c0\ub97c \uc704\ud55c \uc870\uce58 \ud56d\ubaa9",(0,o.kt)("br",{parentName:"p"}),"\n","\ud574\ub2f9 \uacbd\ud5d8\uc5d0\uc11c \uc5bb\uc740 \uad50\ud6c8 "),(0,o.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,o.kt)("p",null,"\uad6c\uae00 \uc5d4\uc9c0\ub2c8\uc5b4\ub294 \uc774\ub807\uac8c \uc77c\ud55c\ub2e4, \ud0c0\uc774\ud130\uc2a4 \uc708\ud130\uc2a4, \ud1b0 \ub9e8\uc26c\ub809, \ud558\uc774\ub7fc \ub77c\uc774\ud2b8 p.86",(0,o.kt)("br",{parentName:"p"}),"\n",(0,o.kt)("a",{parentName:"p",href:"https://techblog.woowahan.com/4886/"},"\uc6b0\uc544\ud55c \uc7a5\uc560\ub300\uc751")))}s.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[8312],{3905:(e,t,r)=>{r.d(t,{Zo:()=>m,kt:()=>d});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function p(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),c=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):p(p({},t),e)),r},m=function(e){var t=c(e.components);return n.createElement(l.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,l=e.parentName,m=i(e,["components","mdxType","originalType","parentName"]),u=c(r),d=o,f=u["".concat(l,".").concat(d)]||u[d]||s[d]||a;return r?n.createElement(f,p(p({ref:t},m),{},{components:r})):n.createElement(f,p({ref:t},m))}));function d(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,p=new Array(a);p[0]=u;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i.mdxType="string"==typeof e?e:o,p[1]=i;for(var c=2;c{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>p,default:()=>s,frontMatter:()=>a,metadata:()=>i,toc:()=>c});var n=r(87462),o=(r(67294),r(3905));const a={title:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c",slug:"/culture/postmortem",tags:["postmortem"]},p=void 0,i={unversionedId:"\ubb38\ud654/\ud3ec\uc2a4\ud2b8_\ubaa8\ud15c",id:"\ubb38\ud654/\ud3ec\uc2a4\ud2b8_\ubaa8\ud15c",title:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c",description:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c(Postmortem)",source:"@site/docs/\ubb38\ud654/\ud3ec\uc2a4\ud2b8_\ubaa8\ud15c.mdx",sourceDirName:"\ubb38\ud654",slug:"/culture/postmortem",permalink:"/docs/culture/postmortem",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ubb38\ud654/\ud3ec\uc2a4\ud2b8_\ubaa8\ud15c.mdx",tags:[{label:"postmortem",permalink:"/docs/tags/postmortem"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c",slug:"/culture/postmortem",tags:["postmortem"]},sidebar:"tutorialSidebar",previous:{title:"\ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131",permalink:"/docs/monitoring/intro"},next:{title:"\ud328\ud0a4\uc9c0",permalink:"/docs/design/package"}},l={},c=[{value:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c(Postmortem)",id:"\ud3ec\uc2a4\ud2b8-\ubaa8\ud15cpostmortem",level:3},{value:"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c\uc5d0 \ub2f4\uaca8\uc57c \ud558\ub294 \ub0b4\uc6a9",id:"\ud3ec\uc2a4\ud2b8-\ubaa8\ud15c\uc5d0-\ub2f4\uaca8\uc57c-\ud558\ub294-\ub0b4\uc6a9",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],m={toc:c};function s(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},m,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h3",{id:"\ud3ec\uc2a4\ud2b8-\ubaa8\ud15cpostmortem"},"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c(Postmortem)"),(0,o.kt)("p",null,"\uc2e4\ud328\ud55c \uadfc\ubcf8 \uc6d0\uc778\uc744 \ubd84\uc11d\ud558\uc5ec \ubb38\uc11c\ub85c \ub0a8\uae30\ub294 \uac83"),(0,o.kt)("h3",{id:"\ud3ec\uc2a4\ud2b8-\ubaa8\ud15c\uc5d0-\ub2f4\uaca8\uc57c-\ud558\ub294-\ub0b4\uc6a9"},"\ud3ec\uc2a4\ud2b8 \ubaa8\ud15c\uc5d0 \ub2f4\uaca8\uc57c \ud558\ub294 \ub0b4\uc6a9"),(0,o.kt)("p",null,"\uc0ac\uac74\uc758 \uac1c\uc694",(0,o.kt)("br",{parentName:"p"}),"\n","\uc0ac\uac74\uc744 \uc778\uc9c0\ud558\uace0 \ud574\uacb0\uc5d0 \uc774\ub974\uae30\uae4c\uc9c0\uc758 \ud0c0\uc784\ub77c\uc778",(0,o.kt)("br",{parentName:"p"}),"\n","\uc0ac\uac74\uc758 \uadfc\ubcf8 \uc6d0\uc778",(0,o.kt)("br",{parentName:"p"}),"\n","\uc601\ud5a5\uacfc \ud53c\ud574 \ud3c9\uac00",(0,o.kt)("br",{parentName:"p"}),"\n","\ubb38\uc81c\ub97c \uc989\uc2dc \ud574\uacb0\ud558\uae30 \uc704\ud55c \uc870\uce58 \ud56d\ubaa9(\uc18c\uc720\uc790 \uba85\uc2dc)",(0,o.kt)("br",{parentName:"p"}),"\n","\uac1c\ubc1c \ubc29\uc9c0\ub97c \uc704\ud55c \uc870\uce58 \ud56d\ubaa9",(0,o.kt)("br",{parentName:"p"}),"\n","\ud574\ub2f9 \uacbd\ud5d8\uc5d0\uc11c \uc5bb\uc740 \uad50\ud6c8 "),(0,o.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,o.kt)("p",null,"\uad6c\uae00 \uc5d4\uc9c0\ub2c8\uc5b4\ub294 \uc774\ub807\uac8c \uc77c\ud55c\ub2e4, \ud0c0\uc774\ud130\uc2a4 \uc708\ud130\uc2a4, \ud1b0 \ub9e8\uc26c\ub809, \ud558\uc774\ub7fc \ub77c\uc774\ud2b8 p.86",(0,o.kt)("br",{parentName:"p"}),"\n",(0,o.kt)("a",{parentName:"p",href:"https://techblog.woowahan.com/4886/"},"\uc6b0\uc544\ud55c \uc7a5\uc560\ub300\uc751")))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/9f586ca3.0edad504.js b/assets/js/9f586ca3.0edad504.js new file mode 100644 index 000000000..b3c83f071 --- /dev/null +++ b/assets/js/9f586ca3.0edad504.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3673],{41912:e=>{e.exports=JSON.parse('{"permalink":"/tags/async/page/2","page":2,"postsPerPage":1,"totalPages":2,"totalCount":2,"previousPage":"/tags/async","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/a1877440.7a23249c.js b/assets/js/a1877440.7a23249c.js deleted file mode 100644 index 95245c6d8..000000000 --- a/assets/js/a1877440.7a23249c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7648],{23235:e=>{e.exports=JSON.parse('{"permalink":"/tags/async","page":1,"postsPerPage":1,"totalPages":1,"totalCount":1,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/a1877440.88e9cb51.js b/assets/js/a1877440.88e9cb51.js new file mode 100644 index 000000000..baea202b1 --- /dev/null +++ b/assets/js/a1877440.88e9cb51.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7648],{23235:e=>{e.exports=JSON.parse('{"permalink":"/tags/async","page":1,"postsPerPage":1,"totalPages":2,"totalCount":2,"nextPage":"/tags/async/page/2","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/a43f2942.341ff912.js b/assets/js/a43f2942.90e79db4.js similarity index 98% rename from assets/js/a43f2942.341ff912.js rename to assets/js/a43f2942.90e79db4.js index 9e7476364..dd4f3bab7 100644 --- a/assets/js/a43f2942.341ff912.js +++ b/assets/js/a43f2942.90e79db4.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[104],{3905:(e,t,r)=>{r.d(t,{Zo:()=>l,kt:()=>d});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function s(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var c=n.createContext({}),p=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):s(s({},t),e)),r},l=function(e){var t=p(e.components);return n.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,l=i(e,["components","mdxType","originalType","parentName"]),f=p(r),d=a,m=f["".concat(c,".").concat(d)]||f[d]||u[d]||o;return r?n.createElement(m,s(s({ref:t},l),{},{components:r})):n.createElement(m,s({ref:t},l))}));function d(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,s=new Array(o);s[0]=f;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i.mdxType="string"==typeof e?e:a,s[1]=i;for(var p=2;p{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>u,frontMatter:()=>o,metadata:()=>i,toc:()=>p});var n=r(87462),a=(r(67294),r(3905));const o={title:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8",slug:"/test/stairstep",tags:["test"]},s=void 0,i={unversionedId:"\ud14c\uc2a4\ud2b8/\uacc4\ub2e8_\ud14c\uc2a4\ud2b8",id:"\ud14c\uc2a4\ud2b8/\uacc4\ub2e8_\ud14c\uc2a4\ud2b8",title:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8",description:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8(Stairstep Test)",source:"@site/docs/\ud14c\uc2a4\ud2b8/\uacc4\ub2e8_\ud14c\uc2a4\ud2b8.mdx",sourceDirName:"\ud14c\uc2a4\ud2b8",slug:"/test/stairstep",permalink:"/docs/test/stairstep",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ud14c\uc2a4\ud2b8/\uacc4\ub2e8_\ud14c\uc2a4\ud2b8.mdx",tags:[{label:"test",permalink:"/docs/tags/test"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8",slug:"/test/stairstep",tags:["test"]},sidebar:"tutorialSidebar",previous:{title:"FIRST",permalink:"/docs/test/first"},next:{title:"TDD heuristics",permalink:"/docs/test/heuristics"}},c={},p=[{value:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8(Stairstep Test)",id:"\uacc4\ub2e8-\ud14c\uc2a4\ud2b8stairstep-test",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],l={toc:p};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},l,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\uacc4\ub2e8-\ud14c\uc2a4\ud2b8stairstep-test"},"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8(Stairstep Test)"),(0,a.kt)("p",null,"\ucd94\ud6c4\uc5d0 \ud544\uc694\ub85c \ud560 \ud074\ub798\uc2a4, \ud568\uc218, \ub2e4\ub978 \uad6c\uc870\ub97c \ub9cc\ub4e4\ub3c4\ub85d \uac15\uc81c\ud558\uae30 \uc704\ud574 \uc791\uc131\ud558\ub294 \ud14c\uc2a4\ud2b8",(0,a.kt)("br",{parentName:"p"}),"\n","\uc544\ubb34\ub7f0 \ub2e8\uc815\ubb38\uc774 \uc5c6\uc744 \uc218\ub3c4 \uc788\uace0, \uae30\ub2a5\uc774 \uc870\uae08 \ub354 \uad6c\ud604\ub41c\ub2e4\uba74 \uc81c\uac70\ud558\uace0 \ud3ec\uad04\uc801\uc778 \ud14c\uc2a4\ud2b8\ub85c \ub300\uc2e0\ud560 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ubcf5\uc7a1\ub3c4\ub97c \ud544\uc694\ud55c \uc218\uc900\uae4c\uc9c0 \uc810\uc9c4\uc801\uc73c\ub85c \uc99d\uac00\uc2dc\ud0ac \uc218 \uc788\uac8c \ub3c4\uc640\uc8fc\ub294 \uacc4\ub2e8 \uc5ed\ud560\uc744 \ud558\uae30 \ub54c\ubb38\uc5d0 \uacc4\ub2e8 \ud14c\uc2a4\ud2b8\ub77c\uace0 \ubd80\ub978\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc7a5\uc778 \uc815\uc2e0 \uc774\uc57c\uae30, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4 p.74"))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[104],{3905:(e,t,r)=>{r.d(t,{Zo:()=>l,kt:()=>d});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function s(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var c=n.createContext({}),p=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):s(s({},t),e)),r},l=function(e){var t=p(e.components);return n.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,l=i(e,["components","mdxType","originalType","parentName"]),f=p(r),d=a,m=f["".concat(c,".").concat(d)]||f[d]||u[d]||o;return r?n.createElement(m,s(s({ref:t},l),{},{components:r})):n.createElement(m,s({ref:t},l))}));function d(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,s=new Array(o);s[0]=f;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i.mdxType="string"==typeof e?e:a,s[1]=i;for(var p=2;p{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>u,frontMatter:()=>o,metadata:()=>i,toc:()=>p});var n=r(87462),a=(r(67294),r(3905));const o={title:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8",slug:"/test/stairstep",tags:["test"]},s=void 0,i={unversionedId:"\ud14c\uc2a4\ud2b8/\uacc4\ub2e8_\ud14c\uc2a4\ud2b8",id:"\ud14c\uc2a4\ud2b8/\uacc4\ub2e8_\ud14c\uc2a4\ud2b8",title:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8",description:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8(Stairstep Test)",source:"@site/docs/\ud14c\uc2a4\ud2b8/\uacc4\ub2e8_\ud14c\uc2a4\ud2b8.mdx",sourceDirName:"\ud14c\uc2a4\ud2b8",slug:"/test/stairstep",permalink:"/docs/test/stairstep",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ud14c\uc2a4\ud2b8/\uacc4\ub2e8_\ud14c\uc2a4\ud2b8.mdx",tags:[{label:"test",permalink:"/docs/tags/test"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8",slug:"/test/stairstep",tags:["test"]},sidebar:"tutorialSidebar",previous:{title:"FIRST",permalink:"/docs/test/first"},next:{title:"TDD heuristics",permalink:"/docs/test/heuristics"}},c={},p=[{value:"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8(Stairstep Test)",id:"\uacc4\ub2e8-\ud14c\uc2a4\ud2b8stairstep-test",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],l={toc:p};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},l,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\uacc4\ub2e8-\ud14c\uc2a4\ud2b8stairstep-test"},"\uacc4\ub2e8 \ud14c\uc2a4\ud2b8(Stairstep Test)"),(0,a.kt)("p",null,"\ucd94\ud6c4\uc5d0 \ud544\uc694\ub85c \ud560 \ud074\ub798\uc2a4, \ud568\uc218, \ub2e4\ub978 \uad6c\uc870\ub97c \ub9cc\ub4e4\ub3c4\ub85d \uac15\uc81c\ud558\uae30 \uc704\ud574 \uc791\uc131\ud558\ub294 \ud14c\uc2a4\ud2b8",(0,a.kt)("br",{parentName:"p"}),"\n","\uc544\ubb34\ub7f0 \ub2e8\uc815\ubb38\uc774 \uc5c6\uc744 \uc218\ub3c4 \uc788\uace0, \uae30\ub2a5\uc774 \uc870\uae08 \ub354 \uad6c\ud604\ub41c\ub2e4\uba74 \uc81c\uac70\ud558\uace0 \ud3ec\uad04\uc801\uc778 \ud14c\uc2a4\ud2b8\ub85c \ub300\uc2e0\ud560 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ubcf5\uc7a1\ub3c4\ub97c \ud544\uc694\ud55c \uc218\uc900\uae4c\uc9c0 \uc810\uc9c4\uc801\uc73c\ub85c \uc99d\uac00\uc2dc\ud0ac \uc218 \uc788\uac8c \ub3c4\uc640\uc8fc\ub294 \uacc4\ub2e8 \uc5ed\ud560\uc744 \ud558\uae30 \ub54c\ubb38\uc5d0 \uacc4\ub2e8 \ud14c\uc2a4\ud2b8\ub77c\uace0 \ubd80\ub978\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc7a5\uc778 \uc815\uc2e0 \uc774\uc57c\uae30, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4 p.74"))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/a5557bb9.31255d4d.js b/assets/js/a5557bb9.dae55ef5.js similarity index 52% rename from assets/js/a5557bb9.31255d4d.js rename to assets/js/a5557bb9.dae55ef5.js index f5672a9d6..3ecdd1c9c 100644 --- a/assets/js/a5557bb9.31255d4d.js +++ b/assets/js/a5557bb9.dae55ef5.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5991],{93885:e=>{e.exports=JSON.parse('{"permalink":"/","page":1,"postsPerPage":1,"totalPages":45,"totalCount":45,"nextPage":"/page/2","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5991],{93885:e=>{e.exports=JSON.parse('{"permalink":"/","page":1,"postsPerPage":1,"totalPages":46,"totalCount":46,"nextPage":"/page/2","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/a710d533.3086487e.js b/assets/js/a710d533.3086487e.js deleted file mode 100644 index 1b526bc02..000000000 --- a/assets/js/a710d533.3086487e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[1711],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>d});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var u=r.createContext({}),c=function(e){var t=r.useContext(u),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=c(e.components);return r.createElement(u.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,u=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),m=c(n),d=a,g=m["".concat(u,".").concat(d)]||m[d]||p[d]||o;return n?r.createElement(g,i(i({ref:t},s),{},{components:n})):r.createElement(g,i({ref:t},s))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var u in t)hasOwnProperty.call(t,u)&&(l[u]=t[u]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var c=2;c{n.d(t,{Z:()=>i});var r=n(67294),a=n(86010);const o="tabItem_Ymn6";function i(e){let{children:t,hidden:n,className:i}=e;return r.createElement("div",{role:"tabpanel",className:(0,a.Z)(o,i),hidden:n},t)}},74866:(e,t,n)=>{n.d(t,{Z:()=>S});var r=n(87462),a=n(67294),o=n(86010),i=n(12466),l=n(16550),u=n(91980),c=n(67392),s=n(50012);function p(e){return function(e){return a.Children.map(e,(e=>{if((0,a.isValidElement)(e)&&"value"in e.props)return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))}(e).map((e=>{let{props:{value:t,label:n,attributes:r,default:a}}=e;return{value:t,label:n,attributes:r,default:a}}))}function m(e){const{values:t,children:n}=e;return(0,a.useMemo)((()=>{const e=t??p(n);return function(e){const t=(0,c.l)(e,((e,t)=>e.value===t.value));if(t.length>0)throw new Error(`Docusaurus error: Duplicate values "${t.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[t,n])}function d(e){let{value:t,tabValues:n}=e;return n.some((e=>e.value===t))}function g(e){let{queryString:t=!1,groupId:n}=e;const r=(0,l.k6)(),o=function(e){let{queryString:t=!1,groupId:n}=e;if("string"==typeof t)return t;if(!1===t)return null;if(!0===t&&!n)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:t,groupId:n});return[(0,u._X)(o),(0,a.useCallback)((e=>{if(!o)return;const t=new URLSearchParams(r.location.search);t.set(o,e),r.replace({...r.location,search:t.toString()})}),[o,r])]}function k(e){const{defaultValue:t,queryString:n=!1,groupId:r}=e,o=m(e),[i,l]=(0,a.useState)((()=>function(e){let{defaultValue:t,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the component requires at least one children component");if(t){if(!d({value:t,tabValues:n}))throw new Error(`Docusaurus error: The has a defaultValue "${t}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return t}const r=n.find((e=>e.default))??n[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:t,tabValues:o}))),[u,c]=g({queryString:n,groupId:r}),[p,k]=function(e){let{groupId:t}=e;const n=function(e){return e?`docusaurus.tab.${e}`:null}(t),[r,o]=(0,s.Nk)(n);return[r,(0,a.useCallback)((e=>{n&&o.set(e)}),[n,o])]}({groupId:r}),f=(()=>{const e=u??p;return d({value:e,tabValues:o})?e:null})();(0,a.useEffect)((()=>{f&&l(f)}),[f]);return{selectedValue:i,selectValue:(0,a.useCallback)((e=>{if(!d({value:e,tabValues:o}))throw new Error(`Can't select invalid tab value=${e}`);l(e),c(e),k(e)}),[c,k,o]),tabValues:o}}var f=n(72389);const b="tabList__CuJ",h="tabItem_LNqP";function N(e){let{className:t,block:n,selectedValue:l,selectValue:u,tabValues:c}=e;const s=[],{blockElementScrollPositionUntilNextRender:p}=(0,i.o5)(),m=e=>{const t=e.currentTarget,n=s.indexOf(t),r=c[n].value;r!==l&&(p(t),u(r))},d=e=>{let t=null;switch(e.key){case"Enter":m(e);break;case"ArrowRight":{const n=s.indexOf(e.currentTarget)+1;t=s[n]??s[0];break}case"ArrowLeft":{const n=s.indexOf(e.currentTarget)-1;t=s[n]??s[s.length-1];break}}t?.focus()};return a.createElement("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,o.Z)("tabs",{"tabs--block":n},t)},c.map((e=>{let{value:t,label:n,attributes:i}=e;return a.createElement("li",(0,r.Z)({role:"tab",tabIndex:l===t?0:-1,"aria-selected":l===t,key:t,ref:e=>s.push(e),onKeyDown:d,onClick:m},i,{className:(0,o.Z)("tabs__item",h,i?.className,{"tabs__item--active":l===t})}),n??t)})))}function v(e){let{lazy:t,children:n,selectedValue:r}=e;if(t){const e=n.find((e=>e.props.value===r));return e?(0,a.cloneElement)(e,{className:"margin-top--md"}):null}return a.createElement("div",{className:"margin-top--md"},n.map(((e,t)=>(0,a.cloneElement)(e,{key:t,hidden:e.props.value!==r}))))}function C(e){const t=k(e);return a.createElement("div",{className:(0,o.Z)("tabs-container",b)},a.createElement(N,(0,r.Z)({},e,t)),a.createElement(v,(0,r.Z)({},e,t)))}function S(e){const t=(0,f.Z)();return a.createElement(C,(0,r.Z)({key:String(t)},e))}},95220:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>u,default:()=>d,frontMatter:()=>l,metadata:()=>c,toc:()=>p});var r=n(87462),a=(n(67294),n(3905)),o=n(74866),i=n(85162);const l={title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",slug:"tomcat-retrospective",tags:["Woowahan Techcourse","Retrospective"]},u=void 0,c={permalink:"/tomcat-retrospective",editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx",source:"@site/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx",title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",description:"\ud1b0\ucea3 \uad6c\ud604",date:"2023-09-11T00:00:00.000Z",formattedDate:"2023\ub144 9\uc6d4 11\uc77c",tags:[{label:"Woowahan Techcourse",permalink:"/tags/woowahan-techcourse"},{label:"Retrospective",permalink:"/tags/retrospective"}],readingTime:12.27,hasTruncateMarker:!1,authors:[],frontMatter:{title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",slug:"tomcat-retrospective",tags:["Woowahan Techcourse","Retrospective"]},nextItem:{title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958",permalink:"/performance-test-type"}},s={authorsImageUrls:[]},p=[{value:"\ud1b0\ucea3 \uad6c\ud604",id:"\ud1b0\ucea3-\uad6c\ud604",level:3},{value:"\ub2e4\uc774\uc5b4\uadf8\ub7a8",id:"\ub2e4\uc774\uc5b4\uadf8\ub7a8",level:3},{value:"\ucf54\ub4dc \ub9ac\ubdf0",id:"\ucf54\ub4dc-\ub9ac\ubdf0",level:3},{value:"SessionConfig",id:"sessionconfig",level:3},{value:"HTTP \uc218\uc5c5",id:"http-\uc218\uc5c5",level:3},{value:"Thread \uc218\uc5c5",id:"thread-\uc218\uc5c5",level:3},{value:"\ub9c8\uce58\uba70",id:"\ub9c8\uce58\uba70",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],m={toc:p};function d(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ud1b0\ucea3-\uad6c\ud604"},"\ud1b0\ucea3 \uad6c\ud604"),(0,a.kt)("p",null,"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\ub97c \uc9c0\uc6d0\ud560 \ub54c \uac1d\uccb4\uc9c0\ud5a5\uacfc \uad00\ub828\ub41c \ubbf8\uc158\ub3c4 \uae30\ub300\ub97c \ub9ce\uc774 \ud588\uc9c0\ub9cc \ub808\ubca8 4\uc5d0 \uc9c4\ud589\ud558\ub294 \ubbf8\uc158\uc774 \uc815\ub9d0 \ud558\uace0 \uc2f6\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uadf8\ub798\uc11c \ubbf8\uc158\uc744 \ud560 \uc218 \uc788\uc744\uae4c\ub77c\ub294 \uac71\uc815 \ubc18, \ubbf8\uc158\uc5d0 \ub300\ud55c \uae30\ub300 \ubc18\uc73c\ub85c \ubd80\ud47c \ub9c8\uc74c\uc744 \uac00\uc9c0\uace0 \ubbf8\uc158\uc744 \uc2dc\uc791\ud588\ub358 \uac83 \uac19\ub2e4. "),(0,a.kt)("p",null,"\uc774\ubc88 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc801\uc808\ud558\uac8c \ucd94\uc0c1\ud654\ud558\uace0, \ubbf8\uc158\uc758 \ubcf8\uc9c8\uc744 \uc774\ud574\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158\uc740 ",(0,a.kt)("a",{parentName:"p",href:"https://datatracker.ietf.org/doc/html/rfc2616/"},"RFC 2616"),"\uc5d0 \uba85\uc2dc\ub41c \uc2a4\ud399(\uc644\ubcbd\ud558\uc9c0 \uc54a\uc9c0\ub9cc \ubbf8\uc158\uc5d0\uc11c \uc8fc\uc5b4\uc9c4 \uc694\uad6c\uc0ac\ud56d\ub9cc \ub9cc\uc871\ud558\ub3c4\ub85d)\uc73c\ub85c \uc694\uccad\uc744 \ubc1b\uc544 \ucc98\ub9ac \ud6c4 \ubc18\ud658\ud558\ub294\ub370 \uc9d1\uc911\ud588\ub2e4. "),(0,a.kt)("h3",{id:"\ub2e4\uc774\uc5b4\uadf8\ub7a8"},"\ub2e4\uc774\uc5b4\uadf8\ub7a8"),(0,a.kt)("p",null,"Catalina\ub294 Tomcat\uc758 \uc11c\ube14\ub9bf \ucee8\ud14c\uc774\ub108, Coyote\ub294 HTTP 1.1 \uc6f9 \uc11c\ubc84\ub97c \uc9c0\uc6d0\ud558\ub294 \uad6c\uc131 \uc694\uc18c\ub77c\uace0 \uc0dd\uac01\ud558\uace0 \uc544\ub798\uc640 \uac19\uc774 \uad6c\uc131\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0ac\uc2e4 \ub0b4\ubd80 \uad6c\uc870\ub97c \uae4a\uac8c \uacf5\ubd80\ud560 \uc2dc\uac04\uc744 \uac00\uc9c0\uc9c0 \ubabb\ud574\uc11c \uac01 \uad6c\uc131 \uc694\uc18c\uac00 \uc65c \ud574\ub2f9 \uc704\uce58\uc5d0 \uc788\ub294\uc9c0 \uc644\ubcbd\ud558\uac8c \uc124\uba85\ud558\uc9c0\ub294 \ubabb\ud558\uc9c0\ub9cc \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc774\uac74 \uc5ec\uae30\uc5d0 \uc788\uc73c\uba74 \uc88b\uc744 \uac83 \uac19\uc740\ub370? \ub77c\ub294 \uc0dd\uac01\uc774 \ub4e4\uba74 \uc801\uc808\ud55c \ud328\ud0a4\uc9c0\uc5d0 \uc704\uce58\uc2dc\ud0a4\ub294 \ubc29\ud5a5\uc73c\ub85c \uc9c4\ud589\uc744 \ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub610\ud55c \uc801\uc808\ud558\uac8c \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc758\uc874\uc131 \ubc29\ud5a5\uc744 \ub2e8\ubc29\ud5a5\uc73c\ub85c \ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. "),(0,a.kt)("mermaid",{value:"graph LR\n\tsubgraph coyote\n\t\tHP[Http11Processor] --\x3e A\n\t\tHP --\x3e HttpRequestParser\n\t\tHP --\x3e HttpResponseGenerator\n\t\tA[Adapter]\n end\n\n subgraph catalina\n\t\tRA[RequestAdapter] -.-> A\n\t\tAC[AbstractController] -.-> C[Controller]\n\t\tStaticController -.-> AC\n\t\tSM[SessionManger] -.-> Manager\n\t\tTC[Tomcat] --\x3e RA\n\t\tRA --\x3e C\n\t\tRA --\x3e Manager\n\t\tRA --\x3e RM\n\t\tRM[RequestMapper] --\x3e C\n end\n\n subgraph jwp\n\t\tLC[LoginController] -.-> AC\n\t\tApplication --\x3e TC\n end\n"}),(0,a.kt)("h3",{id:"\ucf54\ub4dc-\ub9ac\ubdf0"},"\ucf54\ub4dc \ub9ac\ubdf0"),(0,a.kt)("p",null,"\ud06c\ub8e8 \uc911 \ud55c \uba85\uc774 \ub098\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\uace0, \ub0b4\uac00 \ub2e4\ub978 \ud06c\ub8e8\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\ub294 \ud615\ud0dc\ub85c \uc9c4\ud589\uc774 \ub418\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\uc758 \ub9ac\ubdf0\uc5b4\ub294 \ub514\ub178, \ub9ac\ubdf0\uc774\ub294 \ud544\ub9bd\uc774\uc5c8\ub2e4. "),(0,a.kt)("p",null,"\ub514\ub178(\ub9e4\uc758 \ub208\uc774 \uc544\ub2cc \uacf5\ub8e1\uc758 \ub208?)\uac00 \ub9e4\uc6b0 \uaf3c\uaf3c\ud558\uac8c \ucf54\ub4dc \ub9ac\ubdf0\ub97c \ud574\uc8fc\uc5b4\uc11c \uc870\uae08 \ub354 \ub098\uc740 \ucf54\ub4dc\ub97c \uc791\uc131\ud560 \uc218 \uc788\uc5c8\uace0, \ud544\ub9bd\uc758 \ucf54\ub4dc\uc5d0\uc11c\ub294 \uaf3c\uaf3c\ud558\uac8c \uc608\uc678\ucc98\ub9ac \ud558\ub294 \ubd80\ubd84\uc744 \ubc30\uc6b8 \uc218 \uc788\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud55c \uac00\uc9c0 \uc544\uc26c\uc6b4 \uc810\uc740 \ud544\ub9bd\uc5d0\uac8c \uc791\uc131\ud55c \ub098\uc758 \ucf54\uba58\ud2b8\ub4e4\uc774 \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uacbd\ud5d8 \uae30\ubc18\uc73c\ub85c \uc791\uc131\ud55c \ub0b4\uc6a9\uc774 \ub9ce\uc544 \uadfc\uac70\uac00 \uc870\uae08 \ubd80\uc871\ud588\uace0, \uc815\ub9ac\ub418\uc9c0 \uc54a\uc740 \ubd80\ubd84\uc774 \ub9ce\uc558\ub358 \uac83 \uac19\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e4\uc74c \ubbf8\uc158\ubd80\ud130 \ub9ac\ubdf0\ud560 \ub54c \uc870\uae08 \ub354 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ub354 \uc88b\uc740 \ub0b4\uc6a9\uc744 \ud06c\ub8e8\ub4e4\uacfc \uacf5\uc720\ud560 \uc218 \uc788\ub3c4\ub85d \ub178\ub825\ud574\uc57c\uaca0\ub2e4. "),(0,a.kt)("h3",{id:"sessionconfig"},"SessionConfig"),(0,a.kt)("p",null,"\ubbf8\uc158\uc744 \uc9c4\ud589 \uc911 catalina \ud328\ud0a4\uc9c0\uc758 Session \uad00\ub828 \ubd80\ubd84\uc744 \ubcf4\uba74\uc11c \uc911\ubcf5 \ub85c\uc9c1\uc744 \uac1c\uc120\ud574 \ubcfc \uc218 \uc788\uc744 \uac83 \uac19\uc544 ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/apache/tomcat/pull/660"},"\ucee8\ud2b8\ub9ac\ubdf0\ud2b8"),"\ub97c \uc2dc\ub3c4\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc138\uc158 \ucfe0\ud0a4\uc758 \uc774\ub984\uc744 \uac00\uc838\uc624\ub294 Util \ud074\ub798\uc2a4\uc758 \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub294\ub370 \uae30\ubcf8 \uac12\uc740 JSESSIONID \uc9c0\ub9cc \uc124\uc815\uc5d0 \ub530\ub77c\uc11c \uc138\uc158 \ucfe0\ud0a4\uba85\uc744 \ub2e4\ub974\uac8c \uc0ac\uc6a9\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \ud574\ub2f9 \ub85c\uc9c1\uc774 \uc788\ub294 \uac83\uc73c\ub85c \uc0dd\uac01\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uae30\uc874\uc758 \ucf54\ub4dc\ub294 \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \ucf54\ub4dc\uc758 \ud750\ub984\uc774 \uc77c\uce58\ud558\uc9c0 \uc54a\uc544\uc11c \uc57d\uac04 \uc774\ud574\ud558\uae30 \uc5b4\ub824\uc6e0\ub2e4. "),(0,a.kt)("p",null,"\ucd08\uae30\uc5d0 \uc694\uccad\ud588\ub358 PR\uc740 \uae30\uc874\uc758 \ucf54\ub4dc\ubcf4\ub2e4 \uc804\uccb4\uc801\uc73c\ub85c \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, context\uac00 null\uc778 \uacbd\uc6b0 \ubc14\ub85c \uae30\ubcf8 \uac12\uc744 \ubc18\ud658\ud568\uc73c\ub85c\uc368 \uc131\ub2a5 \uac1c\uc120\uc758 \ud6a8\uacfc\uac00 \uc788\uc744 \uac70\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uba54\uc778\ud14c\uc774\ub108\uc778 Mark Thomas \ud615\uc774 \ud574\ub2f9 \ub85c\uc9c1\uc758 \uacbd\uc6b0 \ucef4\ud30c\uc77c\ub7ec\uac00 \ud574\ub2f9 \ubd80\ubd84\uc744 \ucd5c\uc801\ud654 \ud560 \uc218 \uc788\uc744 \uac70\ub77c\uace0 \uae30\ub300\ud55c\ub2e4\uace0 \ud588\uace0, \uac00\ub3c5\uc131\uc744 \uac1c\uc120\uc2dc\ucf1c\ubcf4\ub77c\uace0 \uc870\uc5b8\ud574\uc8fc\uc168\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ucef4\ud30c\uc77c\ub7ec \ucd5c\uc801\ud654\ub294 \uace0\ub824\ud574\ubcf4\uc9c0 \ubabb\ud55c \ubd80\ubd84\uc778\ub370, \uc55e\uc73c\ub85c \ud559\uc2b5\ud574\uc57c \ud560 \ubd80\ubd84\uc774 \uc0b0\ub354\ubbf8\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. "),(0,a.kt)("p",null,"\ub0a8\uaca8\uc900 \ucf54\uba58\ud2b8\uc5d0 \ub530\ub77c \ucd5c\uc885\uc801\uc73c\ub85c\ub294 \uc911\ubcf5\ub41c \ucf54\ub4dc\ub97c \uc904\uc774\ub294 \ubc29\ud5a5\uc73c\ub85c \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uacb0\uacfc\uc801\uc73c\ub85c \uae30\uc874 \ub85c\uc9c1 \ub300\ube44 \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \uc720\uc0ac\ud55c \ud750\ub984\uc758 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\uc5ec \uc88b\uc740 \ubc29\ud5a5\uc73c\ub85c \ub9ac\ud329\ud130\ub9c1\uc744 \ud588\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. "),(0,a.kt)(o.Z,{mdxType:"Tabs"},(0,a.kt)(i.Z,{value:"\uae30\uc874",label:"\uae30\uc874",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n\n String result = getConfiguredSessionCookieName(context);\n\n if (result == null) {\n result = DEFAULT_SESSION_COOKIE_NAME;\n }\n\n return result;\n}\n\npublic static String getSessionUriParamName(Context context) {\n\n String result = getConfiguredSessionCookieName(context);\n\n if (result == null) {\n result = DEFAULT_SESSION_PARAMETER_NAME;\n }\n\n return result;\n}\n\nprivate static String getConfiguredSessionCookieName(Context context) {\n\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n if (context != null) {\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc =\n context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n }\n\n return null;\n}\n"))),(0,a.kt)(i.Z,{value:"PR \uc694\uccad",label:"PR \uc694\uccad",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n if (context == null) {\n return DEFAULT_SESSION_COOKIE_NAME;\n }\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\n}\n\npublic static String getSessionUriParamName(Context context) {\n if (context == null) {\n return DEFAULT_SESSION_PARAMETER_NAME;\n }\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\n}\n\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n return defaultName;\n}\n"))),(0,a.kt)(i.Z,{value:"\ucd5c\uc885",label:"\ucd5c\uc885",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\n}\n\npublic static String getSessionUriParamName(Context context) {\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\n}\n\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n if (context != null) {\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n }\n return defaultName;\n}\n")))),(0,a.kt)("h3",{id:"http-\uc218\uc5c5"},"HTTP \uc218\uc5c5"),(0,a.kt)("p",null,"\ubbf8\uc158 \uc911\uac04\uc5d0 \uc9c4\ud589\ub418\uc5c8\ub358 HTTP \uc218\uc5c5\uc5d0\ub294 HTTP\ub97c \uc801\uc808\ud558\uac8c \ud65c\uc6a9\ud558\ub294 \ubd80\ubd84\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud56d\uc0c1 \uc131\ub2a5 \uac1c\uc120\uc744 \uc704\ud574 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ub2e8\uc5d0\uc11c \ucd5c\uc801\ud654\ud574\ubcf4\ub824\uace0 \ub178\ub825\uc744 \ud588\uc9c0\ub9cc, \ub354 \uc801\uc740 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ud6a8\uc728\uc801\uc73c\ub85c \uc131\ub2a5\uc744 \uac1c\uc120\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc5d0 \ub300\ud574 \uc54c \uc218 \uc788\uc5c8\ub358 \uc218\uc5c5\uc774\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","HTTP \uc555\ucd95, HTTP \uce90\uc2f1, \ub9ac\uc18c\uc2a4 \ucd5c\uc801\ud654 \uae30\ubc95\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4. "),(0,a.kt)("p",null,"\uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \ub2e4\uc74c \uc635\uc158\uc744 \uc124\uc815\ud558\uc5ec http\uc758 \uc1a1\uc218\uc2e0\uc758 \uc555\ucd95\uc744 \uc9c4\ud589\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yml"},"server:\n compression:\n enabled: true\n")),(0,a.kt)("p",null,"\uc218\uc5c5 \uc911 \ud574\ub2f9 \uc555\ucd95 \uc131\ub2a5\uc774 \uc88b\ub2e4\uba74 \uc65c \uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \uae30\ubcf8 \uac12\uc73c\ub85c \uc124\uc815\uc744 \ud558\uc9c0 \uc54a\uc558\ub294\uc9c0 \uad81\uae08\ud574\uc84c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uad81\uae08\uc99d\uc744 \ud574\uc18c\ud558\uc9c0 \ubabb\ud588\ub294\ub370 \ub9d0\ub791\uc774 \uc7a1\ub2f4 \ucc44\ub110\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc740 ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/spring-projects/spring-boot/issues/21369"},"issue"),"\ub97c \ucc3e\uc544\uc8fc\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub0b4\uc6a9\uc744 \uc694\uc57d\ud574 \ubcf4\uc790\uba74 WAS \ubcc4\ub85c \uc555\ucd95\uc744 \ud558\uae30 \uc704\ud574 \uc124\uc815\ud574\uc57c \ud558\ub294 \uac83\uc774 \ub2e4\ub974\uace0, \ubb34\uc870\uac74 \uc555\ucd95\uc744 \ud558\ub294 \uac83\uc774 \ucd5c\uc801\uc758 \uacbd\uc6b0\uac00 \uc544\ub2d0 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \uae30\ubcf8\uac12\uc73c\ub85c \uc124\uc815\ud558\uc9c0 \uc54a\ub294 \uac83 \uac19\ub2e4. "),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"If you're developing a public-facing application then it's probably likely gzip compression would be worthwhile. If, however, you're a microservice application and you're in a dataceter, you may well prefer to reduce CPU load because you know you'll only be talking to other microservices and you have a reliable gigabit network.")),(0,a.kt)("p",null,"Phil Webb \ud615\ub2d8\uc758 \ub9d0\uc5d0 \ub530\ub974\uba74 \uc77c\ubc18\uc801\uc778 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uac1c\ubc1c\ud558\ub294 \uacbd\uc6b0 gzip \uc555\ucd95\uc744 \uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\uc9c0\ub9cc, MSA \ud658\uacbd + \ub370\uc774\ud130 \uc13c\ud130\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \uc624\uc9c1 \ub2e4\ub978 MSA \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uacfc \ud1b5\uc2e0\ud558\uace0, \uace0\uc131\ub2a5 \ub124\ud2b8\uc6cc\ud06c\uac00 \uc788\uae30 \ub54c\ubb38\uc5d0 CPU \ubd80\ud558\ub97c \uc904\uc774\ub294 \uac83\uc774 \uc6b0\uc120\uc2dc \ub420 \uc218\ub3c4 \uc788\ub2e4\ub294 \uac83\uc774\uc5c8\ub2e4. "),(0,a.kt)("p",null,"\uc774\uc678\uc5d0\ub3c4 \uc758\ub3c4\ud558\uc9c0 \uc54a\uc740 \uce90\uc2f1\uc744 \ub9c9\uae30 \uc704\ud574 \ud734\ub9ac\uc2a4\ud2f1 \uce90\uc2f1\uc744 \uc81c\uac70\ud558\uac70\ub098, \uac1c\uc778 \uc815\ubcf4 \uc720\ucd9c\uc744 \ub9c9\uae30 \uc704\ud574 \uc751\ub2f5 \ud5e4\ub354\uc5d0 private\uc744 \uc124\uc815, ETag\ub3c4 \ud559\uc2b5\ud588\ub2e4. "),(0,a.kt)("admonition",{title:"ETag",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"ETag HTTP \uc751\ub2f5 \ud5e4\ub354\ub294 \ud2b9\uc815 \ubc84\uc804\uc758 \ub9ac\uc18c\uc2a4\ub97c \uc2dd\ubcc4\ud558\ub294 \uc2dd\ubcc4\uc790\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc6f9 \uc11c\ubc84\uac00 \ub0b4\uc6a9\uc744 \ud655\uc778\ud558\uace0 \ubcc0\ud558\uc9c0 \uc54a\uc558\uc73c\uba74, \uc6f9 \uc11c\ubc84\ub85c full \uc694\uccad\uc744 \ubcf4\ub0b4\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0, \uce90\uc2dc\uac00 \ub354 \ud6a8\uc728\uc801\uc774\uac8c \ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","MDN")),(0,a.kt)("h3",{id:"thread-\uc218\uc5c5"},"Thread \uc218\uc5c5"),(0,a.kt)("p",null,"\uc2a4\ub808\ub4dc\uc5d0 \ub300\ud55c \uc218\uc5c5\uc744 \ub4e4\uc5c8\uc9c0\ub9cc, \ubcf5\uc7a1\ud55c \ub0b4\uc6a9\ub3c4 \uc6cc\ub099 \ub9ce\uc558\uae30 \ub54c\ubb38\uc5d0 \uc124\uba85\ud558\ub77c\uace0 \ud558\uba74 \uc798 \ubabb\ud560 \uac83 \uac19\uc740 \ub290\ub08c\uc774 \ub4e0\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud604\uc7ac \ud504\ub85c\uc81d\ud2b8, \ubbf8\uc158, \ud14c\ucf54\ud1a1 \uc900\ube44\ub97c \ubcd1\ud589\ud574\uc57c \ud574\uc11c \uc138\ubd80\uc801\uc778 \ub0b4\uc6a9\uc740 \uc2dc\uac04 \ub0a0 \ub54c \ubcf5\uc2b5\ud558\ub824\uace0 \ud55c\ub2e4. "),(0,a.kt)("p",null,"\uc2a4\ub808\ub4dc\ub97c \uc774\ud574\ud558\uace0, WAS\uc5d0 \uc2a4\ub808\ub4dc \uc124\uc815 \uad00\ub828\ud55c \uc2e4\uc2b5\uc774 \uc788\uc5c8\ub294\ub370 \ud14c\uc624\uc640 \uac19\uc774 1\uc2dc\uac04 \uc815\ub3c4 \ud398\uc5b4\ub85c Thread \uc2e4\uc2b5\uc744 \uc9c4\ud589\ud574 \ubcf4\uc558\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud559\uc2b5\ud55c \ub0b4\uc6a9\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. "),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"threads.max"),": Tomcat\uc758 \ucd5c\ub300 \uc2a4\ub808\ub4dc \uac1c\uc218",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"max-connections"),": Tomcat\uc774 \uc720\uc9c0\ud560 \uc218 \uc788\ub294 \ucd5c\ub300 \ucee4\ub125\uc158 \uac1c\uc218",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"accept-count"),": \ucd5c\ub300 \uc5f0\uacb0 \uc218\uc5d0 \ub3c4\ub2ec\ud588\uc744 \ub54c \uc5f0\uacb0 \uc694\uccad\uc5d0 \ub300\ud574 \uc6b4\uc601 \uccb4\uc81c\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \ub300\uae30\uc5f4\uc758 \ucd5c\ub300 \uae38\uc774. \ud574\ub2f9 Queue\uc5d0 \uc694\uccad\uc774 \uc313\uc774\ub294 \uac83\uc740 Tomcat\uc774 \ub354 \uc774\uc0c1 \uc694\uccad\uc744 \ubc1b\uc744 \uc218 \uc5c6\ub2e4\ub294 \ub73b\uc774\ub2e4. accpet-count queue\uc5d0\ub3c4 \uc694\uccad\uc774 \uac00\ub4dd\ucc28\uba74 \uadf8 \uc774\ud6c4\uc5d0 \uc624\ub294 \uc694\uccad\uc740 \uac70\ubd80\ub41c\ub2e4. "),(0,a.kt)("mermaid",{value:'graph LR\n C("Client") -- request --\x3e ACQ("\uc6b4\uc601\uccb4\uc81c\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue \n size = accept-count") --\x3e TCQ("Tomcat Connector\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue\n size = max-connections") --\x3e TP("Thread Pool\n size = threads.max")'}),(0,a.kt)("h3",{id:"\ub9c8\uce58\uba70"},"\ub9c8\uce58\uba70"),(0,a.kt)("p",null,"\uc2dc\uac04\uc740 \ub108\ubb34 \ube60\ub974\uac8c \uac00\uace0 \ud560 \uc77c\uc740 \ub9ce\uc740 \uac83 \uac19\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc6b0\uc120\uc21c\uc704\ub97c \uc798 \uc815\ud558\uace0 \ud559\uc2b5\uc744 \uc9c4\ud589\ud574\uc57c\uaca0\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud604\uc7ac \ub370\uc774\ud130 \ub2e4\ub8e8\ub294 \ubd80\ubd84(DB)\uc5d0 \ub300\ud55c \ud559\uc2b5\uc774 \ub9ce\uc774 \ubd80\uc871\ud55c \uac83 \uac19\ub2e4. \ud574\ub2f9 \ubd80\ubd84\uc740 \ud14c\ucf54\ud1a1\uc774 \ub05d\ub098\ub294\ub300\ub85c \ucc44\uc6cc\uc57c\uaca0\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://datatracker.ietf.org/doc/html/rfc2616/"},"RFC 2616"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/ETag"},"ETag, mdn"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://tomcat.apache.org/tomcat-8.5-doc/config/http.html"},"Apache Tomcat 8 Configuration Reference"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://bcho.tistory.com/788"},"Apache Tomcat Tuning, Terry Cho"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://dev-ws.tistory.com/96"},"maxThreads, maxConnections, acceptCount\ub85c Tomcat \ud29c\ub2dd\ud558\uae30")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/a710d533.a76e7019.js b/assets/js/a710d533.a76e7019.js new file mode 100644 index 000000000..b9bf7ae54 --- /dev/null +++ b/assets/js/a710d533.a76e7019.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[1711],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>d});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var u=r.createContext({}),c=function(e){var t=r.useContext(u),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=c(e.components);return r.createElement(u.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,u=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),m=c(n),d=a,g=m["".concat(u,".").concat(d)]||m[d]||p[d]||o;return n?r.createElement(g,i(i({ref:t},s),{},{components:n})):r.createElement(g,i({ref:t},s))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var u in t)hasOwnProperty.call(t,u)&&(l[u]=t[u]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var c=2;c{n.d(t,{Z:()=>i});var r=n(67294),a=n(86010);const o="tabItem_Ymn6";function i(e){let{children:t,hidden:n,className:i}=e;return r.createElement("div",{role:"tabpanel",className:(0,a.Z)(o,i),hidden:n},t)}},74866:(e,t,n)=>{n.d(t,{Z:()=>S});var r=n(87462),a=n(67294),o=n(86010),i=n(12466),l=n(16550),u=n(91980),c=n(67392),s=n(50012);function p(e){return function(e){return a.Children.map(e,(e=>{if((0,a.isValidElement)(e)&&"value"in e.props)return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))}(e).map((e=>{let{props:{value:t,label:n,attributes:r,default:a}}=e;return{value:t,label:n,attributes:r,default:a}}))}function m(e){const{values:t,children:n}=e;return(0,a.useMemo)((()=>{const e=t??p(n);return function(e){const t=(0,c.l)(e,((e,t)=>e.value===t.value));if(t.length>0)throw new Error(`Docusaurus error: Duplicate values "${t.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[t,n])}function d(e){let{value:t,tabValues:n}=e;return n.some((e=>e.value===t))}function g(e){let{queryString:t=!1,groupId:n}=e;const r=(0,l.k6)(),o=function(e){let{queryString:t=!1,groupId:n}=e;if("string"==typeof t)return t;if(!1===t)return null;if(!0===t&&!n)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:t,groupId:n});return[(0,u._X)(o),(0,a.useCallback)((e=>{if(!o)return;const t=new URLSearchParams(r.location.search);t.set(o,e),r.replace({...r.location,search:t.toString()})}),[o,r])]}function k(e){const{defaultValue:t,queryString:n=!1,groupId:r}=e,o=m(e),[i,l]=(0,a.useState)((()=>function(e){let{defaultValue:t,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the component requires at least one children component");if(t){if(!d({value:t,tabValues:n}))throw new Error(`Docusaurus error: The has a defaultValue "${t}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return t}const r=n.find((e=>e.default))??n[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:t,tabValues:o}))),[u,c]=g({queryString:n,groupId:r}),[p,k]=function(e){let{groupId:t}=e;const n=function(e){return e?`docusaurus.tab.${e}`:null}(t),[r,o]=(0,s.Nk)(n);return[r,(0,a.useCallback)((e=>{n&&o.set(e)}),[n,o])]}({groupId:r}),f=(()=>{const e=u??p;return d({value:e,tabValues:o})?e:null})();(0,a.useEffect)((()=>{f&&l(f)}),[f]);return{selectedValue:i,selectValue:(0,a.useCallback)((e=>{if(!d({value:e,tabValues:o}))throw new Error(`Can't select invalid tab value=${e}`);l(e),c(e),k(e)}),[c,k,o]),tabValues:o}}var f=n(72389);const b="tabList__CuJ",h="tabItem_LNqP";function N(e){let{className:t,block:n,selectedValue:l,selectValue:u,tabValues:c}=e;const s=[],{blockElementScrollPositionUntilNextRender:p}=(0,i.o5)(),m=e=>{const t=e.currentTarget,n=s.indexOf(t),r=c[n].value;r!==l&&(p(t),u(r))},d=e=>{let t=null;switch(e.key){case"Enter":m(e);break;case"ArrowRight":{const n=s.indexOf(e.currentTarget)+1;t=s[n]??s[0];break}case"ArrowLeft":{const n=s.indexOf(e.currentTarget)-1;t=s[n]??s[s.length-1];break}}t?.focus()};return a.createElement("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,o.Z)("tabs",{"tabs--block":n},t)},c.map((e=>{let{value:t,label:n,attributes:i}=e;return a.createElement("li",(0,r.Z)({role:"tab",tabIndex:l===t?0:-1,"aria-selected":l===t,key:t,ref:e=>s.push(e),onKeyDown:d,onClick:m},i,{className:(0,o.Z)("tabs__item",h,i?.className,{"tabs__item--active":l===t})}),n??t)})))}function v(e){let{lazy:t,children:n,selectedValue:r}=e;if(t){const e=n.find((e=>e.props.value===r));return e?(0,a.cloneElement)(e,{className:"margin-top--md"}):null}return a.createElement("div",{className:"margin-top--md"},n.map(((e,t)=>(0,a.cloneElement)(e,{key:t,hidden:e.props.value!==r}))))}function C(e){const t=k(e);return a.createElement("div",{className:(0,o.Z)("tabs-container",b)},a.createElement(N,(0,r.Z)({},e,t)),a.createElement(v,(0,r.Z)({},e,t)))}function S(e){const t=(0,f.Z)();return a.createElement(C,(0,r.Z)({key:String(t)},e))}},95220:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>u,default:()=>d,frontMatter:()=>l,metadata:()=>c,toc:()=>p});var r=n(87462),a=(n(67294),n(3905)),o=n(74866),i=n(85162);const l={title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",slug:"tomcat-retrospective",tags:["Woowahan Techcourse","Retrospective"]},u=void 0,c={permalink:"/tomcat-retrospective",editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx",source:"@site/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx",title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",description:"\ud1b0\ucea3 \uad6c\ud604",date:"2023-09-11T00:00:00.000Z",formattedDate:"2023\ub144 9\uc6d4 11\uc77c",tags:[{label:"Woowahan Techcourse",permalink:"/tags/woowahan-techcourse"},{label:"Retrospective",permalink:"/tags/retrospective"}],readingTime:12.27,hasTruncateMarker:!1,authors:[],frontMatter:{title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",slug:"tomcat-retrospective",tags:["Woowahan Techcourse","Retrospective"]},prevItem:{title:"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac",permalink:"/async-exception"},nextItem:{title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958",permalink:"/performance-test-type"}},s={authorsImageUrls:[]},p=[{value:"\ud1b0\ucea3 \uad6c\ud604",id:"\ud1b0\ucea3-\uad6c\ud604",level:3},{value:"\ub2e4\uc774\uc5b4\uadf8\ub7a8",id:"\ub2e4\uc774\uc5b4\uadf8\ub7a8",level:3},{value:"\ucf54\ub4dc \ub9ac\ubdf0",id:"\ucf54\ub4dc-\ub9ac\ubdf0",level:3},{value:"SessionConfig",id:"sessionconfig",level:3},{value:"HTTP \uc218\uc5c5",id:"http-\uc218\uc5c5",level:3},{value:"Thread \uc218\uc5c5",id:"thread-\uc218\uc5c5",level:3},{value:"\ub9c8\uce58\uba70",id:"\ub9c8\uce58\uba70",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],m={toc:p};function d(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ud1b0\ucea3-\uad6c\ud604"},"\ud1b0\ucea3 \uad6c\ud604"),(0,a.kt)("p",null,"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\ub97c \uc9c0\uc6d0\ud560 \ub54c \uac1d\uccb4\uc9c0\ud5a5\uacfc \uad00\ub828\ub41c \ubbf8\uc158\ub3c4 \uae30\ub300\ub97c \ub9ce\uc774 \ud588\uc9c0\ub9cc \ub808\ubca8 4\uc5d0 \uc9c4\ud589\ud558\ub294 \ubbf8\uc158\uc774 \uc815\ub9d0 \ud558\uace0 \uc2f6\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uadf8\ub798\uc11c \ubbf8\uc158\uc744 \ud560 \uc218 \uc788\uc744\uae4c\ub77c\ub294 \uac71\uc815 \ubc18, \ubbf8\uc158\uc5d0 \ub300\ud55c \uae30\ub300 \ubc18\uc73c\ub85c \ubd80\ud47c \ub9c8\uc74c\uc744 \uac00\uc9c0\uace0 \ubbf8\uc158\uc744 \uc2dc\uc791\ud588\ub358 \uac83 \uac19\ub2e4. "),(0,a.kt)("p",null,"\uc774\ubc88 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc801\uc808\ud558\uac8c \ucd94\uc0c1\ud654\ud558\uace0, \ubbf8\uc158\uc758 \ubcf8\uc9c8\uc744 \uc774\ud574\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158\uc740 ",(0,a.kt)("a",{parentName:"p",href:"https://datatracker.ietf.org/doc/html/rfc2616/"},"RFC 2616"),"\uc5d0 \uba85\uc2dc\ub41c \uc2a4\ud399(\uc644\ubcbd\ud558\uc9c0 \uc54a\uc9c0\ub9cc \ubbf8\uc158\uc5d0\uc11c \uc8fc\uc5b4\uc9c4 \uc694\uad6c\uc0ac\ud56d\ub9cc \ub9cc\uc871\ud558\ub3c4\ub85d)\uc73c\ub85c \uc694\uccad\uc744 \ubc1b\uc544 \ucc98\ub9ac \ud6c4 \ubc18\ud658\ud558\ub294\ub370 \uc9d1\uc911\ud588\ub2e4. "),(0,a.kt)("h3",{id:"\ub2e4\uc774\uc5b4\uadf8\ub7a8"},"\ub2e4\uc774\uc5b4\uadf8\ub7a8"),(0,a.kt)("p",null,"Catalina\ub294 Tomcat\uc758 \uc11c\ube14\ub9bf \ucee8\ud14c\uc774\ub108, Coyote\ub294 HTTP 1.1 \uc6f9 \uc11c\ubc84\ub97c \uc9c0\uc6d0\ud558\ub294 \uad6c\uc131 \uc694\uc18c\ub77c\uace0 \uc0dd\uac01\ud558\uace0 \uc544\ub798\uc640 \uac19\uc774 \uad6c\uc131\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0ac\uc2e4 \ub0b4\ubd80 \uad6c\uc870\ub97c \uae4a\uac8c \uacf5\ubd80\ud560 \uc2dc\uac04\uc744 \uac00\uc9c0\uc9c0 \ubabb\ud574\uc11c \uac01 \uad6c\uc131 \uc694\uc18c\uac00 \uc65c \ud574\ub2f9 \uc704\uce58\uc5d0 \uc788\ub294\uc9c0 \uc644\ubcbd\ud558\uac8c \uc124\uba85\ud558\uc9c0\ub294 \ubabb\ud558\uc9c0\ub9cc \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc774\uac74 \uc5ec\uae30\uc5d0 \uc788\uc73c\uba74 \uc88b\uc744 \uac83 \uac19\uc740\ub370? \ub77c\ub294 \uc0dd\uac01\uc774 \ub4e4\uba74 \uc801\uc808\ud55c \ud328\ud0a4\uc9c0\uc5d0 \uc704\uce58\uc2dc\ud0a4\ub294 \ubc29\ud5a5\uc73c\ub85c \uc9c4\ud589\uc744 \ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub610\ud55c \uc801\uc808\ud558\uac8c \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc758\uc874\uc131 \ubc29\ud5a5\uc744 \ub2e8\ubc29\ud5a5\uc73c\ub85c \ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. "),(0,a.kt)("mermaid",{value:"graph LR\n\tsubgraph coyote\n\t\tHP[Http11Processor] --\x3e A\n\t\tHP --\x3e HttpRequestParser\n\t\tHP --\x3e HttpResponseGenerator\n\t\tA[Adapter]\n end\n\n subgraph catalina\n\t\tRA[RequestAdapter] -.-> A\n\t\tAC[AbstractController] -.-> C[Controller]\n\t\tStaticController -.-> AC\n\t\tSM[SessionManger] -.-> Manager\n\t\tTC[Tomcat] --\x3e RA\n\t\tRA --\x3e C\n\t\tRA --\x3e Manager\n\t\tRA --\x3e RM\n\t\tRM[RequestMapper] --\x3e C\n end\n\n subgraph jwp\n\t\tLC[LoginController] -.-> AC\n\t\tApplication --\x3e TC\n end\n"}),(0,a.kt)("h3",{id:"\ucf54\ub4dc-\ub9ac\ubdf0"},"\ucf54\ub4dc \ub9ac\ubdf0"),(0,a.kt)("p",null,"\ud06c\ub8e8 \uc911 \ud55c \uba85\uc774 \ub098\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\uace0, \ub0b4\uac00 \ub2e4\ub978 \ud06c\ub8e8\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\ub294 \ud615\ud0dc\ub85c \uc9c4\ud589\uc774 \ub418\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\uc758 \ub9ac\ubdf0\uc5b4\ub294 \ub514\ub178, \ub9ac\ubdf0\uc774\ub294 \ud544\ub9bd\uc774\uc5c8\ub2e4. "),(0,a.kt)("p",null,"\ub514\ub178(\ub9e4\uc758 \ub208\uc774 \uc544\ub2cc \uacf5\ub8e1\uc758 \ub208?)\uac00 \ub9e4\uc6b0 \uaf3c\uaf3c\ud558\uac8c \ucf54\ub4dc \ub9ac\ubdf0\ub97c \ud574\uc8fc\uc5b4\uc11c \uc870\uae08 \ub354 \ub098\uc740 \ucf54\ub4dc\ub97c \uc791\uc131\ud560 \uc218 \uc788\uc5c8\uace0, \ud544\ub9bd\uc758 \ucf54\ub4dc\uc5d0\uc11c\ub294 \uaf3c\uaf3c\ud558\uac8c \uc608\uc678\ucc98\ub9ac \ud558\ub294 \ubd80\ubd84\uc744 \ubc30\uc6b8 \uc218 \uc788\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud55c \uac00\uc9c0 \uc544\uc26c\uc6b4 \uc810\uc740 \ud544\ub9bd\uc5d0\uac8c \uc791\uc131\ud55c \ub098\uc758 \ucf54\uba58\ud2b8\ub4e4\uc774 \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uacbd\ud5d8 \uae30\ubc18\uc73c\ub85c \uc791\uc131\ud55c \ub0b4\uc6a9\uc774 \ub9ce\uc544 \uadfc\uac70\uac00 \uc870\uae08 \ubd80\uc871\ud588\uace0, \uc815\ub9ac\ub418\uc9c0 \uc54a\uc740 \ubd80\ubd84\uc774 \ub9ce\uc558\ub358 \uac83 \uac19\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e4\uc74c \ubbf8\uc158\ubd80\ud130 \ub9ac\ubdf0\ud560 \ub54c \uc870\uae08 \ub354 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ub354 \uc88b\uc740 \ub0b4\uc6a9\uc744 \ud06c\ub8e8\ub4e4\uacfc \uacf5\uc720\ud560 \uc218 \uc788\ub3c4\ub85d \ub178\ub825\ud574\uc57c\uaca0\ub2e4. "),(0,a.kt)("h3",{id:"sessionconfig"},"SessionConfig"),(0,a.kt)("p",null,"\ubbf8\uc158\uc744 \uc9c4\ud589 \uc911 catalina \ud328\ud0a4\uc9c0\uc758 Session \uad00\ub828 \ubd80\ubd84\uc744 \ubcf4\uba74\uc11c \uc911\ubcf5 \ub85c\uc9c1\uc744 \uac1c\uc120\ud574 \ubcfc \uc218 \uc788\uc744 \uac83 \uac19\uc544 ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/apache/tomcat/pull/660"},"\ucee8\ud2b8\ub9ac\ubdf0\ud2b8"),"\ub97c \uc2dc\ub3c4\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc138\uc158 \ucfe0\ud0a4\uc758 \uc774\ub984\uc744 \uac00\uc838\uc624\ub294 Util \ud074\ub798\uc2a4\uc758 \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub294\ub370 \uae30\ubcf8 \uac12\uc740 JSESSIONID \uc9c0\ub9cc \uc124\uc815\uc5d0 \ub530\ub77c\uc11c \uc138\uc158 \ucfe0\ud0a4\uba85\uc744 \ub2e4\ub974\uac8c \uc0ac\uc6a9\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \ud574\ub2f9 \ub85c\uc9c1\uc774 \uc788\ub294 \uac83\uc73c\ub85c \uc0dd\uac01\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uae30\uc874\uc758 \ucf54\ub4dc\ub294 \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \ucf54\ub4dc\uc758 \ud750\ub984\uc774 \uc77c\uce58\ud558\uc9c0 \uc54a\uc544\uc11c \uc57d\uac04 \uc774\ud574\ud558\uae30 \uc5b4\ub824\uc6e0\ub2e4. "),(0,a.kt)("p",null,"\ucd08\uae30\uc5d0 \uc694\uccad\ud588\ub358 PR\uc740 \uae30\uc874\uc758 \ucf54\ub4dc\ubcf4\ub2e4 \uc804\uccb4\uc801\uc73c\ub85c \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, context\uac00 null\uc778 \uacbd\uc6b0 \ubc14\ub85c \uae30\ubcf8 \uac12\uc744 \ubc18\ud658\ud568\uc73c\ub85c\uc368 \uc131\ub2a5 \uac1c\uc120\uc758 \ud6a8\uacfc\uac00 \uc788\uc744 \uac70\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uba54\uc778\ud14c\uc774\ub108\uc778 Mark Thomas \ud615\uc774 \ud574\ub2f9 \ub85c\uc9c1\uc758 \uacbd\uc6b0 \ucef4\ud30c\uc77c\ub7ec\uac00 \ud574\ub2f9 \ubd80\ubd84\uc744 \ucd5c\uc801\ud654 \ud560 \uc218 \uc788\uc744 \uac70\ub77c\uace0 \uae30\ub300\ud55c\ub2e4\uace0 \ud588\uace0, \uac00\ub3c5\uc131\uc744 \uac1c\uc120\uc2dc\ucf1c\ubcf4\ub77c\uace0 \uc870\uc5b8\ud574\uc8fc\uc168\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ucef4\ud30c\uc77c\ub7ec \ucd5c\uc801\ud654\ub294 \uace0\ub824\ud574\ubcf4\uc9c0 \ubabb\ud55c \ubd80\ubd84\uc778\ub370, \uc55e\uc73c\ub85c \ud559\uc2b5\ud574\uc57c \ud560 \ubd80\ubd84\uc774 \uc0b0\ub354\ubbf8\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. "),(0,a.kt)("p",null,"\ub0a8\uaca8\uc900 \ucf54\uba58\ud2b8\uc5d0 \ub530\ub77c \ucd5c\uc885\uc801\uc73c\ub85c\ub294 \uc911\ubcf5\ub41c \ucf54\ub4dc\ub97c \uc904\uc774\ub294 \ubc29\ud5a5\uc73c\ub85c \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uacb0\uacfc\uc801\uc73c\ub85c \uae30\uc874 \ub85c\uc9c1 \ub300\ube44 \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \uc720\uc0ac\ud55c \ud750\ub984\uc758 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\uc5ec \uc88b\uc740 \ubc29\ud5a5\uc73c\ub85c \ub9ac\ud329\ud130\ub9c1\uc744 \ud588\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. "),(0,a.kt)(o.Z,{mdxType:"Tabs"},(0,a.kt)(i.Z,{value:"\uae30\uc874",label:"\uae30\uc874",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n\n String result = getConfiguredSessionCookieName(context);\n\n if (result == null) {\n result = DEFAULT_SESSION_COOKIE_NAME;\n }\n\n return result;\n}\n\npublic static String getSessionUriParamName(Context context) {\n\n String result = getConfiguredSessionCookieName(context);\n\n if (result == null) {\n result = DEFAULT_SESSION_PARAMETER_NAME;\n }\n\n return result;\n}\n\nprivate static String getConfiguredSessionCookieName(Context context) {\n\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n if (context != null) {\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc =\n context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n }\n\n return null;\n}\n"))),(0,a.kt)(i.Z,{value:"PR \uc694\uccad",label:"PR \uc694\uccad",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n if (context == null) {\n return DEFAULT_SESSION_COOKIE_NAME;\n }\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\n}\n\npublic static String getSessionUriParamName(Context context) {\n if (context == null) {\n return DEFAULT_SESSION_PARAMETER_NAME;\n }\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\n}\n\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n return defaultName;\n}\n"))),(0,a.kt)(i.Z,{value:"\ucd5c\uc885",label:"\ucd5c\uc885",mdxType:"TabItem"},(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java"},"public static String getSessionCookieName(Context context) {\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\n}\n\npublic static String getSessionUriParamName(Context context) {\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\n}\n\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\n // Priority is:\n // 1. Cookie name defined in context\n // 2. Cookie name configured for app\n // 3. Default defined by spec\n if (context != null) {\n String cookieName = context.getSessionCookieName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\n cookieName = scc.getName();\n if (cookieName != null && cookieName.length() > 0) {\n return cookieName;\n }\n }\n return defaultName;\n}\n")))),(0,a.kt)("h3",{id:"http-\uc218\uc5c5"},"HTTP \uc218\uc5c5"),(0,a.kt)("p",null,"\ubbf8\uc158 \uc911\uac04\uc5d0 \uc9c4\ud589\ub418\uc5c8\ub358 HTTP \uc218\uc5c5\uc5d0\ub294 HTTP\ub97c \uc801\uc808\ud558\uac8c \ud65c\uc6a9\ud558\ub294 \ubd80\ubd84\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud56d\uc0c1 \uc131\ub2a5 \uac1c\uc120\uc744 \uc704\ud574 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ub2e8\uc5d0\uc11c \ucd5c\uc801\ud654\ud574\ubcf4\ub824\uace0 \ub178\ub825\uc744 \ud588\uc9c0\ub9cc, \ub354 \uc801\uc740 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ud6a8\uc728\uc801\uc73c\ub85c \uc131\ub2a5\uc744 \uac1c\uc120\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc5d0 \ub300\ud574 \uc54c \uc218 \uc788\uc5c8\ub358 \uc218\uc5c5\uc774\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","HTTP \uc555\ucd95, HTTP \uce90\uc2f1, \ub9ac\uc18c\uc2a4 \ucd5c\uc801\ud654 \uae30\ubc95\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4. "),(0,a.kt)("p",null,"\uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \ub2e4\uc74c \uc635\uc158\uc744 \uc124\uc815\ud558\uc5ec http\uc758 \uc1a1\uc218\uc2e0\uc758 \uc555\ucd95\uc744 \uc9c4\ud589\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yml"},"server:\n compression:\n enabled: true\n")),(0,a.kt)("p",null,"\uc218\uc5c5 \uc911 \ud574\ub2f9 \uc555\ucd95 \uc131\ub2a5\uc774 \uc88b\ub2e4\uba74 \uc65c \uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \uae30\ubcf8 \uac12\uc73c\ub85c \uc124\uc815\uc744 \ud558\uc9c0 \uc54a\uc558\ub294\uc9c0 \uad81\uae08\ud574\uc84c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uad81\uae08\uc99d\uc744 \ud574\uc18c\ud558\uc9c0 \ubabb\ud588\ub294\ub370 \ub9d0\ub791\uc774 \uc7a1\ub2f4 \ucc44\ub110\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc740 ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/spring-projects/spring-boot/issues/21369"},"issue"),"\ub97c \ucc3e\uc544\uc8fc\uc5c8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub0b4\uc6a9\uc744 \uc694\uc57d\ud574 \ubcf4\uc790\uba74 WAS \ubcc4\ub85c \uc555\ucd95\uc744 \ud558\uae30 \uc704\ud574 \uc124\uc815\ud574\uc57c \ud558\ub294 \uac83\uc774 \ub2e4\ub974\uace0, \ubb34\uc870\uac74 \uc555\ucd95\uc744 \ud558\ub294 \uac83\uc774 \ucd5c\uc801\uc758 \uacbd\uc6b0\uac00 \uc544\ub2d0 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \uae30\ubcf8\uac12\uc73c\ub85c \uc124\uc815\ud558\uc9c0 \uc54a\ub294 \uac83 \uac19\ub2e4. "),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"If you're developing a public-facing application then it's probably likely gzip compression would be worthwhile. If, however, you're a microservice application and you're in a dataceter, you may well prefer to reduce CPU load because you know you'll only be talking to other microservices and you have a reliable gigabit network.")),(0,a.kt)("p",null,"Phil Webb \ud615\ub2d8\uc758 \ub9d0\uc5d0 \ub530\ub974\uba74 \uc77c\ubc18\uc801\uc778 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uac1c\ubc1c\ud558\ub294 \uacbd\uc6b0 gzip \uc555\ucd95\uc744 \uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\uc9c0\ub9cc, MSA \ud658\uacbd + \ub370\uc774\ud130 \uc13c\ud130\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \uc624\uc9c1 \ub2e4\ub978 MSA \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uacfc \ud1b5\uc2e0\ud558\uace0, \uace0\uc131\ub2a5 \ub124\ud2b8\uc6cc\ud06c\uac00 \uc788\uae30 \ub54c\ubb38\uc5d0 CPU \ubd80\ud558\ub97c \uc904\uc774\ub294 \uac83\uc774 \uc6b0\uc120\uc2dc \ub420 \uc218\ub3c4 \uc788\ub2e4\ub294 \uac83\uc774\uc5c8\ub2e4. "),(0,a.kt)("p",null,"\uc774\uc678\uc5d0\ub3c4 \uc758\ub3c4\ud558\uc9c0 \uc54a\uc740 \uce90\uc2f1\uc744 \ub9c9\uae30 \uc704\ud574 \ud734\ub9ac\uc2a4\ud2f1 \uce90\uc2f1\uc744 \uc81c\uac70\ud558\uac70\ub098, \uac1c\uc778 \uc815\ubcf4 \uc720\ucd9c\uc744 \ub9c9\uae30 \uc704\ud574 \uc751\ub2f5 \ud5e4\ub354\uc5d0 private\uc744 \uc124\uc815, ETag\ub3c4 \ud559\uc2b5\ud588\ub2e4. "),(0,a.kt)("admonition",{title:"ETag",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"ETag HTTP \uc751\ub2f5 \ud5e4\ub354\ub294 \ud2b9\uc815 \ubc84\uc804\uc758 \ub9ac\uc18c\uc2a4\ub97c \uc2dd\ubcc4\ud558\ub294 \uc2dd\ubcc4\uc790\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc6f9 \uc11c\ubc84\uac00 \ub0b4\uc6a9\uc744 \ud655\uc778\ud558\uace0 \ubcc0\ud558\uc9c0 \uc54a\uc558\uc73c\uba74, \uc6f9 \uc11c\ubc84\ub85c full \uc694\uccad\uc744 \ubcf4\ub0b4\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0, \uce90\uc2dc\uac00 \ub354 \ud6a8\uc728\uc801\uc774\uac8c \ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","MDN")),(0,a.kt)("h3",{id:"thread-\uc218\uc5c5"},"Thread \uc218\uc5c5"),(0,a.kt)("p",null,"\uc2a4\ub808\ub4dc\uc5d0 \ub300\ud55c \uc218\uc5c5\uc744 \ub4e4\uc5c8\uc9c0\ub9cc, \ubcf5\uc7a1\ud55c \ub0b4\uc6a9\ub3c4 \uc6cc\ub099 \ub9ce\uc558\uae30 \ub54c\ubb38\uc5d0 \uc124\uba85\ud558\ub77c\uace0 \ud558\uba74 \uc798 \ubabb\ud560 \uac83 \uac19\uc740 \ub290\ub08c\uc774 \ub4e0\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud604\uc7ac \ud504\ub85c\uc81d\ud2b8, \ubbf8\uc158, \ud14c\ucf54\ud1a1 \uc900\ube44\ub97c \ubcd1\ud589\ud574\uc57c \ud574\uc11c \uc138\ubd80\uc801\uc778 \ub0b4\uc6a9\uc740 \uc2dc\uac04 \ub0a0 \ub54c \ubcf5\uc2b5\ud558\ub824\uace0 \ud55c\ub2e4. "),(0,a.kt)("p",null,"\uc2a4\ub808\ub4dc\ub97c \uc774\ud574\ud558\uace0, WAS\uc5d0 \uc2a4\ub808\ub4dc \uc124\uc815 \uad00\ub828\ud55c \uc2e4\uc2b5\uc774 \uc788\uc5c8\ub294\ub370 \ud14c\uc624\uc640 \uac19\uc774 1\uc2dc\uac04 \uc815\ub3c4 \ud398\uc5b4\ub85c Thread \uc2e4\uc2b5\uc744 \uc9c4\ud589\ud574 \ubcf4\uc558\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud559\uc2b5\ud55c \ub0b4\uc6a9\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. "),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"threads.max"),": Tomcat\uc758 \ucd5c\ub300 \uc2a4\ub808\ub4dc \uac1c\uc218",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"max-connections"),": Tomcat\uc774 \uc720\uc9c0\ud560 \uc218 \uc788\ub294 \ucd5c\ub300 \ucee4\ub125\uc158 \uac1c\uc218",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"accept-count"),": \ucd5c\ub300 \uc5f0\uacb0 \uc218\uc5d0 \ub3c4\ub2ec\ud588\uc744 \ub54c \uc5f0\uacb0 \uc694\uccad\uc5d0 \ub300\ud574 \uc6b4\uc601 \uccb4\uc81c\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \ub300\uae30\uc5f4\uc758 \ucd5c\ub300 \uae38\uc774. \ud574\ub2f9 Queue\uc5d0 \uc694\uccad\uc774 \uc313\uc774\ub294 \uac83\uc740 Tomcat\uc774 \ub354 \uc774\uc0c1 \uc694\uccad\uc744 \ubc1b\uc744 \uc218 \uc5c6\ub2e4\ub294 \ub73b\uc774\ub2e4. accpet-count queue\uc5d0\ub3c4 \uc694\uccad\uc774 \uac00\ub4dd\ucc28\uba74 \uadf8 \uc774\ud6c4\uc5d0 \uc624\ub294 \uc694\uccad\uc740 \uac70\ubd80\ub41c\ub2e4. "),(0,a.kt)("mermaid",{value:'graph LR\n C("Client") -- request --\x3e ACQ("\uc6b4\uc601\uccb4\uc81c\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue \n size = accept-count") --\x3e TCQ("Tomcat Connector\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue\n size = max-connections") --\x3e TP("Thread Pool\n size = threads.max")'}),(0,a.kt)("h3",{id:"\ub9c8\uce58\uba70"},"\ub9c8\uce58\uba70"),(0,a.kt)("p",null,"\uc2dc\uac04\uc740 \ub108\ubb34 \ube60\ub974\uac8c \uac00\uace0 \ud560 \uc77c\uc740 \ub9ce\uc740 \uac83 \uac19\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc6b0\uc120\uc21c\uc704\ub97c \uc798 \uc815\ud558\uace0 \ud559\uc2b5\uc744 \uc9c4\ud589\ud574\uc57c\uaca0\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud604\uc7ac \ub370\uc774\ud130 \ub2e4\ub8e8\ub294 \ubd80\ubd84(DB)\uc5d0 \ub300\ud55c \ud559\uc2b5\uc774 \ub9ce\uc774 \ubd80\uc871\ud55c \uac83 \uac19\ub2e4. \ud574\ub2f9 \ubd80\ubd84\uc740 \ud14c\ucf54\ud1a1\uc774 \ub05d\ub098\ub294\ub300\ub85c \ucc44\uc6cc\uc57c\uaca0\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://datatracker.ietf.org/doc/html/rfc2616/"},"RFC 2616"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/ETag"},"ETag, mdn"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://tomcat.apache.org/tomcat-8.5-doc/config/http.html"},"Apache Tomcat 8 Configuration Reference"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://bcho.tistory.com/788"},"Apache Tomcat Tuning, Terry Cho"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://dev-ws.tistory.com/96"},"maxThreads, maxConnections, acceptCount\ub85c Tomcat \ud29c\ub2dd\ud558\uae30")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/b2b675dd.b8d348e9.js b/assets/js/b2b675dd.b8d348e9.js deleted file mode 100644 index 4ce4a7d37..000000000 --- a/assets/js/b2b675dd.b8d348e9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[533],{28017:n=>{n.exports=JSON.parse('{"blogPosts":[{"id":"tomcat-retrospective","metadata":{"permalink":"/tomcat-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0","description":"\ud1b0\ucea3 \uad6c\ud604","date":"2023-09-11T00:00:00.000Z","formattedDate":"2023\ub144 9\uc6d4 11\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":12.27,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0","slug":"tomcat-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"nextItem":{"title":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958","permalink":"/performance-test-type"}},"content":"### \ud1b0\ucea3 \uad6c\ud604\\n\\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\ub97c \uc9c0\uc6d0\ud560 \ub54c \uac1d\uccb4\uc9c0\ud5a5\uacfc \uad00\ub828\ub41c \ubbf8\uc158\ub3c4 \uae30\ub300\ub97c \ub9ce\uc774 \ud588\uc9c0\ub9cc \ub808\ubca8 4\uc5d0 \uc9c4\ud589\ud558\ub294 \ubbf8\uc158\uc774 \uc815\ub9d0 \ud558\uace0 \uc2f6\uc5c8\ub2e4. \\n\uadf8\ub798\uc11c \ubbf8\uc158\uc744 \ud560 \uc218 \uc788\uc744\uae4c\ub77c\ub294 \uac71\uc815 \ubc18, \ubbf8\uc158\uc5d0 \ub300\ud55c \uae30\ub300 \ubc18\uc73c\ub85c \ubd80\ud47c \ub9c8\uc74c\uc744 \uac00\uc9c0\uace0 \ubbf8\uc158\uc744 \uc2dc\uc791\ud588\ub358 \uac83 \uac19\ub2e4. \\n\\n\uc774\ubc88 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc801\uc808\ud558\uac8c \ucd94\uc0c1\ud654\ud558\uace0, \ubbf8\uc158\uc758 \ubcf8\uc9c8\uc744 \uc774\ud574\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. \\n\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158\uc740 [RFC 2616](https://datatracker.ietf.org/doc/html/rfc2616/)\uc5d0 \uba85\uc2dc\ub41c \uc2a4\ud399(\uc644\ubcbd\ud558\uc9c0 \uc54a\uc9c0\ub9cc \ubbf8\uc158\uc5d0\uc11c \uc8fc\uc5b4\uc9c4 \uc694\uad6c\uc0ac\ud56d\ub9cc \ub9cc\uc871\ud558\ub3c4\ub85d)\uc73c\ub85c \uc694\uccad\uc744 \ubc1b\uc544 \ucc98\ub9ac \ud6c4 \ubc18\ud658\ud558\ub294\ub370 \uc9d1\uc911\ud588\ub2e4. \\n\\n### \ub2e4\uc774\uc5b4\uadf8\ub7a8\\n\\nCatalina\ub294 Tomcat\uc758 \uc11c\ube14\ub9bf \ucee8\ud14c\uc774\ub108, Coyote\ub294 HTTP 1.1 \uc6f9 \uc11c\ubc84\ub97c \uc9c0\uc6d0\ud558\ub294 \uad6c\uc131 \uc694\uc18c\ub77c\uace0 \uc0dd\uac01\ud558\uace0 \uc544\ub798\uc640 \uac19\uc774 \uad6c\uc131\ud588\ub2e4. \\n\uc0ac\uc2e4 \ub0b4\ubd80 \uad6c\uc870\ub97c \uae4a\uac8c \uacf5\ubd80\ud560 \uc2dc\uac04\uc744 \uac00\uc9c0\uc9c0 \ubabb\ud574\uc11c \uac01 \uad6c\uc131 \uc694\uc18c\uac00 \uc65c \ud574\ub2f9 \uc704\uce58\uc5d0 \uc788\ub294\uc9c0 \uc644\ubcbd\ud558\uac8c \uc124\uba85\ud558\uc9c0\ub294 \ubabb\ud558\uc9c0\ub9cc \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc774\uac74 \uc5ec\uae30\uc5d0 \uc788\uc73c\uba74 \uc88b\uc744 \uac83 \uac19\uc740\ub370? \ub77c\ub294 \uc0dd\uac01\uc774 \ub4e4\uba74 \uc801\uc808\ud55c \ud328\ud0a4\uc9c0\uc5d0 \uc704\uce58\uc2dc\ud0a4\ub294 \ubc29\ud5a5\uc73c\ub85c \uc9c4\ud589\uc744 \ud588\ub2e4. \\n\ub610\ud55c \uc801\uc808\ud558\uac8c \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc758\uc874\uc131 \ubc29\ud5a5\uc744 \ub2e8\ubc29\ud5a5\uc73c\ub85c \ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. \\n\\n```mermaid\\ngraph LR\\n\\tsubgraph coyote\\n\\t\\tHP[Http11Processor] --\x3e A\\n\\t\\tHP --\x3e HttpRequestParser\\n\\t\\tHP --\x3e HttpResponseGenerator\\n\\t\\tA[Adapter]\\n end\\n\\n subgraph catalina\\n\\t\\tRA[RequestAdapter] -.-> A\\n\\t\\tAC[AbstractController] -.-> C[Controller]\\n\\t\\tStaticController -.-> AC\\n\\t\\tSM[SessionManger] -.-> Manager\\n\\t\\tTC[Tomcat] --\x3e RA\\n\\t\\tRA --\x3e C\\n\\t\\tRA --\x3e Manager\\n\\t\\tRA --\x3e RM\\n\\t\\tRM[RequestMapper] --\x3e C\\n end\\n\\n subgraph jwp\\n\\t\\tLC[LoginController] -.-> AC\\n\\t\\tApplication --\x3e TC\\n end\\n\\n```\\n\\n### \ucf54\ub4dc \ub9ac\ubdf0 \\n\\n\ud06c\ub8e8 \uc911 \ud55c \uba85\uc774 \ub098\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\uace0, \ub0b4\uac00 \ub2e4\ub978 \ud06c\ub8e8\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\ub294 \ud615\ud0dc\ub85c \uc9c4\ud589\uc774 \ub418\uc5c8\ub2e4. \\n\ub098\uc758 \ub9ac\ubdf0\uc5b4\ub294 \ub514\ub178, \ub9ac\ubdf0\uc774\ub294 \ud544\ub9bd\uc774\uc5c8\ub2e4. \\n\\n\ub514\ub178(\ub9e4\uc758 \ub208\uc774 \uc544\ub2cc \uacf5\ub8e1\uc758 \ub208?)\uac00 \ub9e4\uc6b0 \uaf3c\uaf3c\ud558\uac8c \ucf54\ub4dc \ub9ac\ubdf0\ub97c \ud574\uc8fc\uc5b4\uc11c \uc870\uae08 \ub354 \ub098\uc740 \ucf54\ub4dc\ub97c \uc791\uc131\ud560 \uc218 \uc788\uc5c8\uace0, \ud544\ub9bd\uc758 \ucf54\ub4dc\uc5d0\uc11c\ub294 \uaf3c\uaf3c\ud558\uac8c \uc608\uc678\ucc98\ub9ac \ud558\ub294 \ubd80\ubd84\uc744 \ubc30\uc6b8 \uc218 \uc788\uc5c8\ub2e4. \\n\ud55c \uac00\uc9c0 \uc544\uc26c\uc6b4 \uc810\uc740 \ud544\ub9bd\uc5d0\uac8c \uc791\uc131\ud55c \ub098\uc758 \ucf54\uba58\ud2b8\ub4e4\uc774 \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uacbd\ud5d8 \uae30\ubc18\uc73c\ub85c \uc791\uc131\ud55c \ub0b4\uc6a9\uc774 \ub9ce\uc544 \uadfc\uac70\uac00 \uc870\uae08 \ubd80\uc871\ud588\uace0, \uc815\ub9ac\ub418\uc9c0 \uc54a\uc740 \ubd80\ubd84\uc774 \ub9ce\uc558\ub358 \uac83 \uac19\ub2e4. \\n\ub2e4\uc74c \ubbf8\uc158\ubd80\ud130 \ub9ac\ubdf0\ud560 \ub54c \uc870\uae08 \ub354 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ub354 \uc88b\uc740 \ub0b4\uc6a9\uc744 \ud06c\ub8e8\ub4e4\uacfc \uacf5\uc720\ud560 \uc218 \uc788\ub3c4\ub85d \ub178\ub825\ud574\uc57c\uaca0\ub2e4. \\n\\n### SessionConfig\\n\\n\ubbf8\uc158\uc744 \uc9c4\ud589 \uc911 catalina \ud328\ud0a4\uc9c0\uc758 Session \uad00\ub828 \ubd80\ubd84\uc744 \ubcf4\uba74\uc11c \uc911\ubcf5 \ub85c\uc9c1\uc744 \uac1c\uc120\ud574 \ubcfc \uc218 \uc788\uc744 \uac83 \uac19\uc544 [\ucee8\ud2b8\ub9ac\ubdf0\ud2b8](https://github.com/apache/tomcat/pull/660)\ub97c \uc2dc\ub3c4\ud588\ub2e4. \\n\uc138\uc158 \ucfe0\ud0a4\uc758 \uc774\ub984\uc744 \uac00\uc838\uc624\ub294 Util \ud074\ub798\uc2a4\uc758 \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub294\ub370 \uae30\ubcf8 \uac12\uc740 JSESSIONID \uc9c0\ub9cc \uc124\uc815\uc5d0 \ub530\ub77c\uc11c \uc138\uc158 \ucfe0\ud0a4\uba85\uc744 \ub2e4\ub974\uac8c \uc0ac\uc6a9\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \ud574\ub2f9 \ub85c\uc9c1\uc774 \uc788\ub294 \uac83\uc73c\ub85c \uc0dd\uac01\ud588\ub2e4. \\n\uae30\uc874\uc758 \ucf54\ub4dc\ub294 \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \ucf54\ub4dc\uc758 \ud750\ub984\uc774 \uc77c\uce58\ud558\uc9c0 \uc54a\uc544\uc11c \uc57d\uac04 \uc774\ud574\ud558\uae30 \uc5b4\ub824\uc6e0\ub2e4. \\n\\n\ucd08\uae30\uc5d0 \uc694\uccad\ud588\ub358 PR\uc740 \uae30\uc874\uc758 \ucf54\ub4dc\ubcf4\ub2e4 \uc804\uccb4\uc801\uc73c\ub85c \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, context\uac00 null\uc778 \uacbd\uc6b0 \ubc14\ub85c \uae30\ubcf8 \uac12\uc744 \ubc18\ud658\ud568\uc73c\ub85c\uc368 \uc131\ub2a5 \uac1c\uc120\uc758 \ud6a8\uacfc\uac00 \uc788\uc744 \uac70\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uba54\uc778\ud14c\uc774\ub108\uc778 Mark Thomas \ud615\uc774 \ud574\ub2f9 \ub85c\uc9c1\uc758 \uacbd\uc6b0 \ucef4\ud30c\uc77c\ub7ec\uac00 \ud574\ub2f9 \ubd80\ubd84\uc744 \ucd5c\uc801\ud654 \ud560 \uc218 \uc788\uc744 \uac70\ub77c\uace0 \uae30\ub300\ud55c\ub2e4\uace0 \ud588\uace0, \uac00\ub3c5\uc131\uc744 \uac1c\uc120\uc2dc\ucf1c\ubcf4\ub77c\uace0 \uc870\uc5b8\ud574\uc8fc\uc168\ub2e4. \\n\ucef4\ud30c\uc77c\ub7ec \ucd5c\uc801\ud654\ub294 \uace0\ub824\ud574\ubcf4\uc9c0 \ubabb\ud55c \ubd80\ubd84\uc778\ub370, \uc55e\uc73c\ub85c \ud559\uc2b5\ud574\uc57c \ud560 \ubd80\ubd84\uc774 \uc0b0\ub354\ubbf8\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\\n\ub0a8\uaca8\uc900 \ucf54\uba58\ud2b8\uc5d0 \ub530\ub77c \ucd5c\uc885\uc801\uc73c\ub85c\ub294 \uc911\ubcf5\ub41c \ucf54\ub4dc\ub97c \uc904\uc774\ub294 \ubc29\ud5a5\uc73c\ub85c \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub2e4. \\n\uacb0\uacfc\uc801\uc73c\ub85c \uae30\uc874 \ub85c\uc9c1 \ub300\ube44 \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \uc720\uc0ac\ud55c \ud750\ub984\uc758 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\uc5ec \uc88b\uc740 \ubc29\ud5a5\uc73c\ub85c \ub9ac\ud329\ud130\ub9c1\uc744 \ud588\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\\nimport Tabs from \\"@theme/Tabs\\";\\nimport TabItem from \\"@theme/TabItem\\";\\n\\n\\n\\n\\n```java\\npublic static String getSessionCookieName(Context context) {\\n\\n String result = getConfiguredSessionCookieName(context);\\n\\n if (result == null) {\\n result = DEFAULT_SESSION_COOKIE_NAME;\\n }\\n\\n return result;\\n}\\n\\npublic static String getSessionUriParamName(Context context) {\\n\\n String result = getConfiguredSessionCookieName(context);\\n\\n if (result == null) {\\n result = DEFAULT_SESSION_PARAMETER_NAME;\\n }\\n\\n return result;\\n}\\n\\nprivate static String getConfiguredSessionCookieName(Context context) {\\n\\n // Priority is:\\n // 1. Cookie name defined in context\\n // 2. Cookie name configured for app\\n // 3. Default defined by spec\\n if (context != null) {\\n String cookieName = context.getSessionCookieName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n\\n SessionCookieConfig scc =\\n context.getServletContext().getSessionCookieConfig();\\n cookieName = scc.getName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n }\\n\\n return null;\\n}\\n```\\n\\n\\n\\n\\n\\n```java\\npublic static String getSessionCookieName(Context context) {\\n if (context == null) {\\n return DEFAULT_SESSION_COOKIE_NAME;\\n }\\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\\n}\\n\\npublic static String getSessionUriParamName(Context context) {\\n if (context == null) {\\n return DEFAULT_SESSION_PARAMETER_NAME;\\n }\\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\\n}\\n\\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\\n // Priority is:\\n // 1. Cookie name defined in context\\n // 2. Cookie name configured for app\\n // 3. Default defined by spec\\n String cookieName = context.getSessionCookieName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n\\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\\n cookieName = scc.getName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n\\n return defaultName;\\n}\\n```\\n\\n\\n\\n\\n```java\\npublic static String getSessionCookieName(Context context) {\\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\\n}\\n\\npublic static String getSessionUriParamName(Context context) {\\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\\n}\\n\\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\\n // Priority is:\\n // 1. Cookie name defined in context\\n // 2. Cookie name configured for app\\n // 3. Default defined by spec\\n if (context != null) {\\n String cookieName = context.getSessionCookieName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n\\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\\n cookieName = scc.getName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n }\\n return defaultName;\\n}\\n```\\n\\n\\n\\n\\n### HTTP \uc218\uc5c5\\n\\n\ubbf8\uc158 \uc911\uac04\uc5d0 \uc9c4\ud589\ub418\uc5c8\ub358 HTTP \uc218\uc5c5\uc5d0\ub294 HTTP\ub97c \uc801\uc808\ud558\uac8c \ud65c\uc6a9\ud558\ub294 \ubd80\ubd84\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4. \\n\ud56d\uc0c1 \uc131\ub2a5 \uac1c\uc120\uc744 \uc704\ud574 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ub2e8\uc5d0\uc11c \ucd5c\uc801\ud654\ud574\ubcf4\ub824\uace0 \ub178\ub825\uc744 \ud588\uc9c0\ub9cc, \ub354 \uc801\uc740 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ud6a8\uc728\uc801\uc73c\ub85c \uc131\ub2a5\uc744 \uac1c\uc120\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc5d0 \ub300\ud574 \uc54c \uc218 \uc788\uc5c8\ub358 \uc218\uc5c5\uc774\uc5c8\ub2e4. \\nHTTP \uc555\ucd95, HTTP \uce90\uc2f1, \ub9ac\uc18c\uc2a4 \ucd5c\uc801\ud654 \uae30\ubc95\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4. \\n\\n\uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \ub2e4\uc74c \uc635\uc158\uc744 \uc124\uc815\ud558\uc5ec http\uc758 \uc1a1\uc218\uc2e0\uc758 \uc555\ucd95\uc744 \uc9c4\ud589\ud560 \uc218 \uc788\ub2e4. \\n\\n```yml\\nserver:\\n compression:\\n enabled: true\\n```\\n\\n\uc218\uc5c5 \uc911 \ud574\ub2f9 \uc555\ucd95 \uc131\ub2a5\uc774 \uc88b\ub2e4\uba74 \uc65c \uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \uae30\ubcf8 \uac12\uc73c\ub85c \uc124\uc815\uc744 \ud558\uc9c0 \uc54a\uc558\ub294\uc9c0 \uad81\uae08\ud574\uc84c\ub2e4. \\n\uad81\uae08\uc99d\uc744 \ud574\uc18c\ud558\uc9c0 \ubabb\ud588\ub294\ub370 \ub9d0\ub791\uc774 \uc7a1\ub2f4 \ucc44\ub110\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc740 [issue](https://github.com/spring-projects/spring-boot/issues/21369)\ub97c \ucc3e\uc544\uc8fc\uc5c8\ub2e4. \\n\ub0b4\uc6a9\uc744 \uc694\uc57d\ud574 \ubcf4\uc790\uba74 WAS \ubcc4\ub85c \uc555\ucd95\uc744 \ud558\uae30 \uc704\ud574 \uc124\uc815\ud574\uc57c \ud558\ub294 \uac83\uc774 \ub2e4\ub974\uace0, \ubb34\uc870\uac74 \uc555\ucd95\uc744 \ud558\ub294 \uac83\uc774 \ucd5c\uc801\uc758 \uacbd\uc6b0\uac00 \uc544\ub2d0 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \uae30\ubcf8\uac12\uc73c\ub85c \uc124\uc815\ud558\uc9c0 \uc54a\ub294 \uac83 \uac19\ub2e4. \\n\\n> If you\'re developing a public-facing application then it\'s probably likely gzip compression would be worthwhile. If, however, you\'re a microservice application and you\'re in a dataceter, you may well prefer to reduce CPU load because you know you\'ll only be talking to other microservices and you have a reliable gigabit network.\\n\\nPhil Webb \ud615\ub2d8\uc758 \ub9d0\uc5d0 \ub530\ub974\uba74 \uc77c\ubc18\uc801\uc778 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uac1c\ubc1c\ud558\ub294 \uacbd\uc6b0 gzip \uc555\ucd95\uc744 \uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\uc9c0\ub9cc, MSA \ud658\uacbd + \ub370\uc774\ud130 \uc13c\ud130\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \uc624\uc9c1 \ub2e4\ub978 MSA \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uacfc \ud1b5\uc2e0\ud558\uace0, \uace0\uc131\ub2a5 \ub124\ud2b8\uc6cc\ud06c\uac00 \uc788\uae30 \ub54c\ubb38\uc5d0 CPU \ubd80\ud558\ub97c \uc904\uc774\ub294 \uac83\uc774 \uc6b0\uc120\uc2dc \ub420 \uc218\ub3c4 \uc788\ub2e4\ub294 \uac83\uc774\uc5c8\ub2e4. \\n\\n\uc774\uc678\uc5d0\ub3c4 \uc758\ub3c4\ud558\uc9c0 \uc54a\uc740 \uce90\uc2f1\uc744 \ub9c9\uae30 \uc704\ud574 \ud734\ub9ac\uc2a4\ud2f1 \uce90\uc2f1\uc744 \uc81c\uac70\ud558\uac70\ub098, \uac1c\uc778 \uc815\ubcf4 \uc720\ucd9c\uc744 \ub9c9\uae30 \uc704\ud574 \uc751\ub2f5 \ud5e4\ub354\uc5d0 private\uc744 \uc124\uc815, ETag\ub3c4 \ud559\uc2b5\ud588\ub2e4. \\n\\n:::note ETag\\nETag HTTP \uc751\ub2f5 \ud5e4\ub354\ub294 \ud2b9\uc815 \ubc84\uc804\uc758 \ub9ac\uc18c\uc2a4\ub97c \uc2dd\ubcc4\ud558\ub294 \uc2dd\ubcc4\uc790\ub2e4. \\n\uc6f9 \uc11c\ubc84\uac00 \ub0b4\uc6a9\uc744 \ud655\uc778\ud558\uace0 \ubcc0\ud558\uc9c0 \uc54a\uc558\uc73c\uba74, \uc6f9 \uc11c\ubc84\ub85c full \uc694\uccad\uc744 \ubcf4\ub0b4\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0, \uce90\uc2dc\uac00 \ub354 \ud6a8\uc728\uc801\uc774\uac8c \ub41c\ub2e4. \\nMDN\\n:::\\n\\n### Thread \uc218\uc5c5\\n\\n\uc2a4\ub808\ub4dc\uc5d0 \ub300\ud55c \uc218\uc5c5\uc744 \ub4e4\uc5c8\uc9c0\ub9cc, \ubcf5\uc7a1\ud55c \ub0b4\uc6a9\ub3c4 \uc6cc\ub099 \ub9ce\uc558\uae30 \ub54c\ubb38\uc5d0 \uc124\uba85\ud558\ub77c\uace0 \ud558\uba74 \uc798 \ubabb\ud560 \uac83 \uac19\uc740 \ub290\ub08c\uc774 \ub4e0\ub2e4. \\n\ud604\uc7ac \ud504\ub85c\uc81d\ud2b8, \ubbf8\uc158, \ud14c\ucf54\ud1a1 \uc900\ube44\ub97c \ubcd1\ud589\ud574\uc57c \ud574\uc11c \uc138\ubd80\uc801\uc778 \ub0b4\uc6a9\uc740 \uc2dc\uac04 \ub0a0 \ub54c \ubcf5\uc2b5\ud558\ub824\uace0 \ud55c\ub2e4. \\n\\n\uc2a4\ub808\ub4dc\ub97c \uc774\ud574\ud558\uace0, WAS\uc5d0 \uc2a4\ub808\ub4dc \uc124\uc815 \uad00\ub828\ud55c \uc2e4\uc2b5\uc774 \uc788\uc5c8\ub294\ub370 \ud14c\uc624\uc640 \uac19\uc774 1\uc2dc\uac04 \uc815\ub3c4 \ud398\uc5b4\ub85c Thread \uc2e4\uc2b5\uc744 \uc9c4\ud589\ud574 \ubcf4\uc558\ub2e4. \\n\ud559\uc2b5\ud55c \ub0b4\uc6a9\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\\n`threads.max`: Tomcat\uc758 \ucd5c\ub300 \uc2a4\ub808\ub4dc \uac1c\uc218 \\n`max-connections`: Tomcat\uc774 \uc720\uc9c0\ud560 \uc218 \uc788\ub294 \ucd5c\ub300 \ucee4\ub125\uc158 \uac1c\uc218 \\n`accept-count`: \ucd5c\ub300 \uc5f0\uacb0 \uc218\uc5d0 \ub3c4\ub2ec\ud588\uc744 \ub54c \uc5f0\uacb0 \uc694\uccad\uc5d0 \ub300\ud574 \uc6b4\uc601 \uccb4\uc81c\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \ub300\uae30\uc5f4\uc758 \ucd5c\ub300 \uae38\uc774. \ud574\ub2f9 Queue\uc5d0 \uc694\uccad\uc774 \uc313\uc774\ub294 \uac83\uc740 Tomcat\uc774 \ub354 \uc774\uc0c1 \uc694\uccad\uc744 \ubc1b\uc744 \uc218 \uc5c6\ub2e4\ub294 \ub73b\uc774\ub2e4. accpet-count queue\uc5d0\ub3c4 \uc694\uccad\uc774 \uac00\ub4dd\ucc28\uba74 \uadf8 \uc774\ud6c4\uc5d0 \uc624\ub294 \uc694\uccad\uc740 \uac70\ubd80\ub41c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n C(\\"Client\\") -- request --\x3e ACQ(\\"\uc6b4\uc601\uccb4\uc81c\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue \\n size = accept-count\\") --\x3e TCQ(\\"Tomcat Connector\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue\\n size = max-connections\\") --\x3e TP(\\"Thread Pool\\n size = threads.max\\")\\n```\\n\\n### \ub9c8\uce58\uba70\\n\\n\uc2dc\uac04\uc740 \ub108\ubb34 \ube60\ub974\uac8c \uac00\uace0 \ud560 \uc77c\uc740 \ub9ce\uc740 \uac83 \uac19\ub2e4. \\n\uc6b0\uc120\uc21c\uc704\ub97c \uc798 \uc815\ud558\uace0 \ud559\uc2b5\uc744 \uc9c4\ud589\ud574\uc57c\uaca0\ub2e4. \\n\ud604\uc7ac \ub370\uc774\ud130 \ub2e4\ub8e8\ub294 \ubd80\ubd84(DB)\uc5d0 \ub300\ud55c \ud559\uc2b5\uc774 \ub9ce\uc774 \ubd80\uc871\ud55c \uac83 \uac19\ub2e4. \ud574\ub2f9 \ubd80\ubd84\uc740 \ud14c\ucf54\ud1a1\uc774 \ub05d\ub098\ub294\ub300\ub85c \ucc44\uc6cc\uc57c\uaca0\ub2e4. \\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n[RFC 2616](https://datatracker.ietf.org/doc/html/rfc2616/) \\n[ETag, mdn](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/ETag) \\n[Apache Tomcat 8 Configuration Reference](https://tomcat.apache.org/tomcat-8.5-doc/config/http.html) \\n[Apache Tomcat Tuning, Terry Cho](https://bcho.tistory.com/788) \\n[maxThreads, maxConnections, acceptCount\ub85c Tomcat \ud29c\ub2dd\ud558\uae30](https://dev-ws.tistory.com/96)"},{"id":"performance-test-type","metadata":{"permalink":"/performance-test-type","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8.mdx","source":"@site/blog/2023-3/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8.mdx","title":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958","description":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8","date":"2023-09-10T00:00:00.000Z","formattedDate":"2023\ub144 9\uc6d4 10\uc77c","tags":[{"label":"performance test","permalink":"/tags/performance-test"}],"readingTime":5.8,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958","slug":"performance-test-type","tags":["performance test"]},"prevItem":{"title":"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0","permalink":"/tomcat-retrospective"},"nextItem":{"title":"DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30","permalink":"/db-replication"}},"content":"## \uc131\ub2a5 \ud14c\uc2a4\ud2b8\\n\\nAPI\uc758 \uc694\uccad\uc774 \ub9ce\uc740 \uc0c1\ud669\uc5d0\uc11c \uc11c\ubc84\uac00 \uc5b4\ub5bb\uac8c \ub3d9\uc791\ud558\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8\\n\\n\uc2dc\uc2a4\ud15c\uc5d0 \ubd80\ud558\uac00 \uac78\ub9ac\uba74 \ubb38\uc81c \uc0c1\ud669\uc774 \ubc1c\uc0dd\ud560 \uc218 \uc788\ub2e4. \\n\ub2e4\uc591\ud55c \uc0c1\ud669\uc5d0 \ub300\ube44\ud574\uc11c \uc131\ub2a5 \ud14c\uc2a4\ud2b8\ub97c \ud574\uc57c\ud55c\ub2e4. \\n\\n![./test.png](./test.png)\\n\\n### \uc2a4\ubaa8\ud06c \ud14c\uc2a4\ud2b8(Smoke Test)\\n\\n\ucd5c\uc18c\ud55c\uc758 \ubd80\ud558\ub97c \uc8fc\uc5b4 \uc2dc\uc2a4\ud15c\uc774 \uc815\uc0c1\uc801\uc73c\ub85c \ub3d9\uc791\ud558\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8 \\n\\nVU\ub97c \ucd5c\uc18c\ud55c\uc73c\ub85c \ub450\uace0, \uc9e7\uc740 \uc2dc\uac04\uc744 \uac00\uc9c0\uace0 \ud14c\uc2a4\ud2b8\ud55c\ub2e4. \\n\ub2e4\ub978 \ud14c\uc2a4\ud2b8\ub97c \uc2dc\uc791\ud558\uae30 \uc804\uc5d0 \uc2a4\ubaa8\ud06c \ud14c\uc2a4\ud2b8\ub97c \ud568\uc73c\ub85c\uc368 \ud14c\uc2a4\ud2b8 \uc2a4\ud06c\ub9bd\ud2b8\uc5d0 \uc624\ub958\uac00 \uc5c6\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\uace0, \uc131\ub2a5 \uc9c0\ud45c\uac00 \uc815\uc0c1\uc801\uc73c\ub85c \uc218\uc9d1, \ubaa8\ub2c8\ud130\ub9c1 \ub418\uace0 \uc788\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n:::note \uac00\uc0c1 \uc0ac\uc6a9\uc790(VU)\\n\uac00\uc0c1 \uc0ac\uc6a9\uc790\ub294 \uc11c\ubc84 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0 \ub300\ud574 \ud2b9\uc815 \ud14c\uc2a4\ud2b8\ub97c \uc2e4\ud589\ud55c\ub2e4. \\n\uc774\ub294 \ub2e4\ub978 \uac00\uc0c1 \uc0ac\uc6a9\uc790\uc640 \ub3c5\ub9bd\uc801\uc73c\ub85c \uc2e4\ud589\ub418\uba70, \uc5ec\ub7ec \uac00\uc0c1 \uc0ac\uc6a9\uc790\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub3d9\uc2dc \uc5f0\uacb0\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\uc2a4\ub808\ub4dc\ub77c\uace0 \uc0dd\uac01\ud558\uba74 \ub41c\ub2e4. \\n:::\\n\\n### \uc2a4\ud30c\uc774\ud06c \ud14c\uc2a4\ud2b8(Spike Test)\\n\\n\uc0ac\uc6a9\ub7c9\uc774 \uae09\uc99d\ud558\ub294 \uc0c1\ud669\uc5d0\uc11c \uc2dc\uc2a4\ud15c\uc774 \uacac\ub514\uace0 \uc131\ub2a5\uc5d0 \ubb38\uc81c\uac00 \uc5c6\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8 \\n\\n\ud2f0\ucf13 \ubc1c\uae09, \ud560\uc778 \ucfe0\ud3f0 \ubc1c\uae09\uacfc \uac19\uc740 \uc774\ubca4\ud2b8\ub97c \ud558\ub294 \uacbd\uc6b0 \ub300\uaddc\ubaa8 \ud2b8\ub798\ud53d\uc774 \ub4e4\uc5b4\uc628\ub2e4. \\n\uc2a4\ud30c\uc774\ud06c \ud14c\uc2a4\ud2b8\ub97c \ud1b5\ud574 \uae09\uc99d\ud558\ub294 \ubd80\ud558 \uc0c1\ud669\uc5d0\uc11c \uc2dc\uc2a4\ud15c\uc774 \uc5b4\ub5bb\uac8c \ub3d9\uc791\ud558\uace0, \ubd80\ud558\ub97c \uc798 \ubc84\ud2f0\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n### \ubd80\ud558 \ud14c\uc2a4\ud2b8(Load Test)\\n\\n\ubaa9\ud46f\uac12\uc5d0 \ud574\ub2f9\ub418\ub294 \ubd80\ud558\ub97c \uacac\ub51c \uc218 \uc788\uc744\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8 \\n\\n\uc77c\ubc18\uc801\uc778 \ubd80\ud558 \uc0c1\ud669\uc5d0\uc11c \uc2dc\uc2a4\ud15c\uc774 \uc5b4\ub5bb\uac8c \ub3d9\uc791\ud558\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8\ub2e4. \\n\ub7a8\ud504\uc5c5 \ub610\ub294 \ubb19\ud46f\uac12\uc5d0 \ud574\ub2f9\ud558\ub294 \ubd80\ud558 \uae30\uac04\ub3d9\uc548 \uc131\ub2a5\uc774 \ubb38\uc81c\uac00 \uc788\ub294\uc9c0 \ud655\uc778\ud558\uace0, \uc2dc\uc2a4\ud15c \ubcc0\uacbd \ud6c4\uc5d0\ub3c4 \ubd80\ud558 \ud14c\uc2a4\ud2b8\ub97c \ub3cc\ub824 \ub3d9\uc77c\ud558\uac8c \ubaa9\ud46f\uac12\uc744 \ucc98\ub9ac\ud558\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n:::note \ub7a8\ud504 \uc5c5(Ramp-up)\\n\ubd80\ud558 \ud14c\uc2a4\ud2b8\ub97c \uc704\ud574 \uc124\uc815\ud55c \uac00\uc0c1 \uc0ac\uc6a9\uc790 \uc218\uc5d0 \ub3c4\ub2ec\ud558\ub294 \ub370 \uac78\ub9ac\ub294 \uc2dc\uac04\\n:::\\n\\n### \uc2a4\ud2b8\ub808\uc2a4 \ud14c\uc2a4\ud2b8(Stress Test) \\n\\n\uc2dc\uc2a4\ud15c\uc758 \ucd5c\ub300\uce58\uc5d0 \ud574\ub2f9\ub418\ub294 \ubd80\ud558\ub97c \ubc1b\uc558\uc744 \ub54c \uc2dc\uc2a4\ud15c\uc774 \uc5b4\ub5bb\uac8c \ub3d9\uc791\ud558\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8 \\n\\n\uadf8\ub798\ud504\ub97c \ubd24\uc744 \ub54c \ubd80\ud558 \ud14c\uc2a4\ud2b8\uc640 \uc720\uc0ac\ud55c \ud615\ud0dc\ub85c \ubcf4\uc774\uc9c0\ub9cc, \ubd80\ud558\ub7c9\uc774 \ub2e4\ub974\ub2e4. \\n\uc77c\ubc18\uc801\uc73c\ub85c \ud3c9\uade0\uc801\uc778 \ubaa9\ud46f\uac12 \ub300\ube44 \uc791\uac8c\ub294 50% \uc774\uc0c1, \ud544\uc694\uc758 \uacbd\uc6b0 \uadf8 \uc774\uc0c1\uc73c\ub85c \ubd80\ud558\ub97c \uc900\ub2e4. \\n\uc2a4\ud2b8\ub808\uc2a4 \ud14c\uc2a4\ud2b8\ub294 \ubd80\ud558 \ud14c\uc2a4\ud2b8\ub97c \uc2e4\ud589\ud55c \ud6c4\uc5d0\ub9cc \uc2e4\ud589\ud574\uc57c \ud55c\ub2e4. \ubd80\ud558 \ud14c\uc2a4\ud2b8\uac00 \uc774\ub8e8\uc5b4\uc9c0\uc9c0 \uc54a\uc740 \uc0c1\ud669\uc5d0\uc11c \uc2a4\ud2b8\ub808\uc2a4 \ud14c\uc2a4\ud2b8\ub97c \uc2e4\ud589\ud558\ub294 \uacbd\uc6b0\uc5d0\ub294 \ubcd1\ubaa9 \uc9c0\uc810\uc774\ub098 \ubb38\uc81c \uc0c1\ud669\uc744 \ucc3e\uae30 \uc5b4\ub824\uc6cc\uc9c4\ub2e4. \\n\ub610\ud55c \ubd80\ud558 \ud14c\uc2a4\ud2b8\uc5d0\uc11c \uc0ac\uc6a9\ud55c \uc2a4\ud06c\ub9bd\ud2b8\ub97c VU\uac12(\uc2a4\ub808\ub4dc \uc218)\ub9cc \uc218\uc815\ud558\uc5ec \uc7ac\uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\ub2e4. \\n\\n### \ub0b4\uad6c \ud14c\uc2a4\ud2b8(Endurance Test) \\n\\n\ud3c9\uade0 \uc0ac\uc6a9\ub960\ub85c \uc77c\uc815 \ubd80\ud558\ub97c \uc9c0\uc18d\uc801\uc73c\ub85c \uc8fc\uba70 \uc2dc\uc2a4\ud15c\uc774 \ubb38\uc81c\ub418\ub294 \uc9c0\uc810\uc744 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8 \\n\\n\ud761\uc218 \ud14c\uc2a4\ud2b8(Soak Test)\ub77c\uace0\ub3c4 \ud558\uba70, \uae30\ubcf8\uc801\uc778 \ubd80\ud558 \ud14c\uc2a4\ud2b8\uc758 \ubcc0\ud615\uc774\ub77c\uace0 \ubcfc \uc218 \uc788\ub2e4. \\n\ub2e4\ub978 \ud14c\uc2a4\ud2b8\uc640 \ub2ec\ub9ac \uae34 \uc2dc\uac04\ub3d9\uc548 \ud14c\uc2a4\ud2b8\ub97c \ud558\ub294 \uac83\uc774 \ud2b9\uc9d5\uc774\uba70, \uba54\ubaa8\ub9ac \ub204\uc218 \ubb38\uc81c\uc640 \uac19\uc774 \uc7a5\uc2dc\uac04 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uc2e4\ud589\ud560 \ub54c \uc2dc\uc2a4\ud15c\uc758 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud558\ub294 \ubd80\ubd84\uc744 \ud655\uc778\ud558\ub294 \uac83\uc774 \ubaa9\uc801\uc774\ub2e4. \\n\\n### \uc911\ub2e8\uc810 \ud14c\uc2a4\ud2b8(Breakpoint Test)\\n\\n\uc784\uacc4 \uc9c0\uc810\uc744 \ucc3e\uae30 \uc704\ud574 \ubd80\ud558\ub97c \uc810\uc9c4\uc801\uc73c\ub85c \uc99d\uac00\uc2dc\ud0a4\uba70 \uc9c4\ud589\ud558\ub294 \ud14c\uc2a4\ud2b8\\n\\n\ubb38\uc81c\ub418\ub294 \ubd80\ubd84\uc744 \ub354 \ube68\ub9ac \ucc3e\uae30 \uc704\ud574 \ub2e4\ub978 \ud14c\uc2a4\ud2b8\ub97c \ud1b5\uacfc\ud55c \ub2e4\uc74c\uc5d0 \uc911\ub2e8\uc810 \ud14c\uc2a4\ud2b8\ub97c \uc9c4\ud589\ud558\uace0, \uc774 \ub54c \uc810\uc9c4\uc801\uc73c\ub85c \ubd80\ud558\ub97c \ub298\ub824\ub098\uac00\ub294 \uac83\uc774 \uc88b\ub2e4. \\n\uc2a4\ud2b8\ub808\uc2a4 \ud14c\uc2a4\ud2b8\ub97c \uc131\ub2a5 \ud29c\ub2dd\uacfc \ubc18\ubcf5\ud574\uc11c \uc9c4\ud589\ud55c\ub2e4\uba74, \uc2dc\uc2a4\ud15c\uc744 \ub354\uc6b1 \ubc1c\uc804\uc2dc\ud0ac \uc218 \uc788\ub2e4. \\n\ub2e4\ub9cc Auto Scaling\uc774 \uc801\uc6a9\ub41c \ud074\ub77c\uc6b0\ub4dc \ud658\uacbd\uc5d0\uc11c\ub294 \uc9c4\ud589\ud558\uc9c0 \uc54a\uc544\uc57c \ud55c\ub2e4. \\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n[Load test types, k6](https://k6.io/docs/test-types/load-test-types/) \\n\uc790\ubc14 \ucd5c\uc801\ud654 - \ubca4\uc800\ubbfc J. \uc5d0\ubc88\uc2a4, \uc81c\uc784\uc2a4 \uace0\ud504, \ud06c\ub9ac\uc2a4 \ub274\ub79c\ub4dc \\n\uc544\ub9c8\uc874 \uc6f9 \uc11c\ube44\uc2a4 \ubd80\ud558 \ud14c\uc2a4\ud2b8 \uc785\ubb38 - \ub098\uce74\uac00\uc640 \ud0c0\ub8e8\ud558\uce58, \ubaa8\ub9ac\uc2dc\ud0c0 \ucf04"},{"id":"db-replication","metadata":{"permalink":"/db-replication","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-08-22-DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30.mdx","source":"@site/blog/2023-3/2023-08-22-DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30.mdx","title":"DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30","description":"\ubcf5\uc81c(Replication)","date":"2023-08-22T00:00:00.000Z","formattedDate":"2023\ub144 8\uc6d4 22\uc77c","tags":[{"label":"mysql","permalink":"/tags/mysql"},{"label":"replication","permalink":"/tags/replication"}],"readingTime":20.58,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30","slug":"db-replication","tags":["mysql","replication"]},"prevItem":{"title":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958","permalink":"/performance-test-type"},"nextItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 3 \ud68c\uace0","permalink":"/woowacourse-level3-retrospective"}},"content":"## \ubcf5\uc81c(Replication)\\n\\n\ud55c \uc11c\ubc84\uc5d0\uc11c \ub2e4\ub978 \uc11c\ubc84\ub85c \ub370\uc774\ud130\ub97c \ub3d9\uae30\ud654\ud558\ub294 \uac83\uc744 \uc758\ubbf8\ud55c\ub2e4. \\n\uc6d0\ubcf8 \ub370\uc774\ud130\ub97c \uac00\uc9c0\ub294 \uc11c\ubc84\ub97c Primary \ub610\ub294 Source \ub77c\uace0 \ubd80\ub974\uace0, \ubcf5\uc81c\ub41c \ub370\uc774\ud130\ub97c \uac00\uc9c0\ub294 \uc11c\ubc84\ub97c Secondary \ub610\ub294 Replica \ub77c\uace0 \ubd80\ub978\ub2e4. \\n\\n### \ubcf5\uc81c\ub97c \ud558\ub294 \uc774\uc720\\n\\n**1. \uc2a4\ucf00\uc77c \uc544\uc6c3**\\n\\n\uc0ac\uc6a9\uc790\uc758 \ud2b8\ub798\ud53d\uc774 \uc99d\uac00\ud558\ub294 \uacbd\uc6b0, \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uac00\ud574\uc9c0\ub294 \ubd80\ud558\ub3c4 \uc790\uc5f0\uc2a4\ub7fd\uac8c \uc99d\uac00\ud55c\ub2e4. \\n\uc774\ub97c \ucc98\ub9ac\ud558\uae30 \uc704\ud574 \ubcf5\uc81c\ub97c \ud1b5\ud55c \uc2a4\ucf00\uc77c \uc544\uc6c3\uc744 \uc801\uc6a9\ud558\uc5ec \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \ucffc\ub9ac\ub4e4\uc744 \uac01\uac01\uc758 \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub85c \ubd84\uc0b0 \uc2dc\ud0ac \uc218 \uc788\ub2e4. \\n\\n**2. \ub370\uc774\ud130 \ubc31\uc5c5**\\n\\n\uc2e4\uc81c \uc6b4\uc601\ub418\ub294 \uc11c\ube44\uc2a4\uac00 \uc0ac\uc6a9\ud558\uace0 \uc788\ub294 DB\uc5d0\uc11c \ubc31\uc5c5\uc744 \uc9c4\ud589\ud558\ub294 \uacbd\uc6b0, \uc11c\ube44\uc2a4\uc5d0 \uc601\ud5a5\uc744 \ubbf8\uce60 \uc218 \uc788\ub2e4. \\n\ub530\ub77c\uc11c \uc2e4\uc81c \uc11c\ube44\uc2a4\uc5d0 \uc601\ud5a5\uc774 \uac00\uc9c0 \uc54a\ub3c4\ub85d \ubcf5\uc81c\ub97c \ud1b5\ud574 Replica \uc11c\ubc84\ub97c \uad6c\ucd95\ud558\uc5ec, Replica \uc11c\ubc84\uc5d0\uc11c \ubcf5\uc81c\ub97c \uc9c4\ud589\ud558\ub294 \ubc29\ubc95\uc73c\ub85c \uc601\ud5a5\uc744 \ucd5c\uc18c\ud654 \ud560 \uc218 \uc788\ub2e4. \\n\\n**3. \ub370\uc774\ud130 \ubd84\uc11d**\\n\\n\ubc31\uc5c5\uacfc \ub9c8\ucc2c\uac00\uc9c0\ub85c \ubcf5\uc7a1\ud558\uace0 \ubb34\uac70\uc6b4 \ubd84\uc11d\uc6a9 \ucffc\ub9ac\uc758 \uc11c\ube44\uc2a4\uc5d0 \uc601\ud5a5\uc744 \ubbf8\uce60 \uc218 \uc788\ub2e4. \\n\ub9c8\ucc2c\uac00\uc9c0\ub85c \ubcf5\uc81c\ub97c \uc0ac\uc6a9\ud574 \ubd84\uc11d\uc6a9 \ucffc\ub9ac\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub294 \ud658\uacbd\uc744 \ub9cc\ub4e4 \uc218 \uc788\ub2e4. \\n\\n**4. \ub370\uc774\ud130\uc758 \uc9c0\ub9ac\uc801 \ubd84\uc0b0**\\n\\n\ube60\ub978 \uc751\ub2f5\uc744 \uc704\ud574 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc11c\ubc84\uc5d0 \uac00\uae5d\uac8c \uc11c\ubc84\ub97c \uad6c\uc131\ud558\uac70\ub098, \uace0\uac00\uc6a9\uc131(High Availability)\uc744 \uc704\ud574\uc11c\ub3c4 \uc0ac\uc6a9\ub41c\ub2e4. \\n\\n### \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ud30c\uc77c \uc704\uce58 \uae30\ubc18 \ubcf5\uc81c\\n\\nMySQL \uc11c\ubc84\uc5d0\uc11c \ubc1c\uc0dd\ud558\ub294 \ubcc0\uacbd\uc0ac\ud56d\uc5d0 \ub300\ud55c \ub85c\uadf8 \ud30c\uc77c\uc744 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8\ub77c\uace0 \ud55c\ub2e4. \\n\ubc14\uc774\ub108\ub9ac \ub85c\uadf8\ub97c \ud1b5\ud574 \ub370\uc774\ud130 \ubcc0\uacbd, \ud14c\uc774\ube14 \uad6c\uc870 \ubcc0\uacbd, \uacc4\uc815\uc774\ub098 \uad8c\ud55c \ubcc0\uacbd\uc5d0 \ub300\ud55c \uc815\ubcf4\uac00 \uc800\uc7a5\ub41c\ub2e4. \\nMySQL\uc758 \ubcf5\uc81c\ub294 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \uae30\ubc18\uc73c\ub85c \uad6c\ud604\ub418\uc5b4 \uc788\ub2e4. \uc774\ub97c Replica \uc11c\ubc84\ub85c \uc804\ub2ec\ud558\uace0 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \uae30\ubc18\uc73c\ub85c \ub370\uc774\ud130\ub97c \ubcc0\uacbd \uc0ac\ud56d\uc744 \ubc18\uc601\ud55c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n\\tsubgraph Replica\\n\\t\\tdirection TB\\n\\t\\tIO[Replication I/O thread] -- save --\x3e RL[Relay Log]\\n\\t\\tSQL[Replication SQL Thread] -- read --\x3e RL\\n\\tend\\n\\n\\tsubgraph Source\\n\\t\\tdirection TB\\n\\t\\tBLD[Binary Log Dump Thread] -- Send Binary Log Dump--\x3e IO\\n\\tend\\n\\n```\\n\\n:::note \uc2a4\ub808\ub4dc\ubcc4 \uc5ed\ud560\\n\\nBinary Log Dump Thread: \ubc14\uc774\ub108\ub9ac \ub85c\uadf8\uc758 \ub0b4\uc6a9\uc744 Replica \uc11c\ubc84\ub85c \uc804\ub2ec \\nReplication I/O Thread: Binary \ub85c\uadf8 \uc774\ubca4\ud2b8\ub97c \uac00\uc838\uc640 \ub85c\uceec \uc11c\ubc84\uc758 \ud30c\uc77c(Relay Log)\ub85c \uc800\uc7a5 \\nReplication SQL Thread: \ub9b4\ub808\uc774 \ub85c\uadf8 \ud30c\uc77c\uc758 \uc774\ubca4\ud2b8\ub97c \uc77d\uace0 \uc2e4\ud589\\n\\n:::\\n\\n### \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ubc29\uc2dd\uc758 \ubb38\uc81c\uc810\\n\\n\ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ubc29\uc2dd\uc740 \uc11c\ubc84\uc5d0 \uc7a5\uc560\uac00 \ubc1c\uc0dd\ud588\uc744 \ub54c \ubcf5\uc81c \ud1a0\ud3f4\ub85c\uc9c0 \ubcc0\uacbd\uc774 \uae4c\ub2e4\ub86d\ub2e4. \\n\ud1a0\ud3f4\ub85c\uc9c0\ub780 \ub124\ud2b8\uc6cc\ud06c\uc758 \uc694\uc18c\ub4e4\uc744 \ubb3c\ub9ac\uc801\uc73c\ub85c \uc5f0\uacb0\ud574 \ub193\uc740 \uac83, \ub610\ub294 \uadf8 \uc5f0\uacb0 \ubc29\uc2dd\uc744 \ub9d0\ud55c\ub2e4.\\n\\n```mermaid\\ngraph TD\\n\\tA[A binary-log:300] --\x3e B[B Binary-log:300]\\n\\tA[A binary-log:300] --\x3e C[C Binary-log:200]\\n```\\n\\n\uc704\uc640 \uac19\uc774 Source \uc11c\ubc84, Replica 2\ub300\uac00 \uc874\uc7ac\ud558\uace0, C \uc11c\ubc84\uc5d0 \ubcf5\uc81c \uc9c0\uc5f0\uc774 \ub418\uc5c8\uc744 \ub54c \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud55c\ub2e4. \\n\\n```mermaid\\ngraph TD\\n\\tA[A binary-log:300]\\n\\tB[B Binary-log:300] --\x3e C[C Binary-log:200 \ubb38\uc81c \ubc1c\uc0dd]\\n```\\n\\nA \uc11c\ubc84\uc5d0\uc11c \uc7a5\uc560\uac00 \ubc1c\uc0dd\ud55c\ub2e4\uba74 B \uc11c\ubc84\ub97c Source \uc11c\ubc84\ub85c \uc2b9\uaca9\ud558\uace0, C\uc5d0\uac8c \uc870\ud68c \ucffc\ub9ac\ub97c \ubd84\uc0b0\uc2dc\ud0a8\ub2e4. \\n\ud558\uc9c0\ub9cc \uc5ec\uae30\uc11c C \uc11c\ubc84\uc5d0\ub294 A \uc11c\ubc84\uc640 \ub3d9\uae30\ud654\uac00 \uc548\ub418\uc5c8\uc73c\ub2c8 \uc870\ud68c \uc2dc \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud55c\ub2e4. \\n\ub4a4\ub2a6\uac8c B \uc11c\ubc84\uc640 \ub3d9\uae30\ud654\ub97c \ud558\ub824\uace0 \ud574\ub3c4, \uc5b4\ub5a4 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8, \uc5b4\ub5a4 \uc704\uce58\uc640 \ub3d9\uae30\ud654\ud574\uc57c\ud558\ub294\uc9c0 \uc54c\uae30 \uc5b4\ub835\ub2e4. \\n\\n### \uae00\ub85c\ubc8c \ud2b8\ub79c\uc7ad\uc158 \uc544\uc774\ub514(GTID) \uae30\ubc18 \ubcf5\uc81c\\n\\nGTID \ubc29\uc2dd\uc744 \uc0ac\uc6a9\ud558\uc5ec \ucc38\uc5ec\ud55c \ubaa8\ub4e0 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uac00 \ubc1c\uc0dd\ud55c \uc774\ubca4\ud2b8\uc5d0 \uace0\uc720\ud55c \uc2dd\ubcc4\uac12\uc744 \ubd80\uc5ec\ud55c\ub2e4\uba74, \ub3d9\uae30\ud654\uc5d0 \ub300\ud55c \ubb38\uc81c\ub97c \uac04\ub2e8\ud558\uac8c \ud574\uacb0\ud560 \uc218 \uc788\ub2e4. \\n\uc704\uc758 \uc608\uc2dc\uc640 \uac19\uc774 \ubcf5\uc81c \uc9c0\uc5f0\uacfc \ud568\uaed8 \uc7a5\uc560\uac00 \ubc1c\uc0dd\ud55c\ub2e4\ud574\ub3c4 \ud2b9\uc815 GTID \ubd80\ud130 \ubcf5\uc81c\ub97c \uc7ac\uac1c\ud558\uba74 \ub41c\ub2e4. \\n\\n:::note GTID\\n\ubcf5\uc81c \ud1a0\ud3f4\ub85c\uc9c0\uc5d0 \ucc38\uc5ec\ud55c \ubaa8\ub4e0 \uc11c\ubc84\uc5d0\uc11c \uace0\uc720\ud558\ub3c4\ub85d \uac01 \uc774\ubca4\ud2b8\uc5d0 \ubd80\uc5ec\ub41c \uc2dd\ubcc4\uac12 \\n[source_id]:[transaction_id]\ub85c \uad6c\uc131\ub418\uba70, source_id\ub294 \uc11c\ubc84\ub97c \uc2dd\ubcc4\ud558\uae30 \uc704\ud55c \uac12\uc774\uace0 transaction_id\ub294 \ucee4\ubc0b\ub41c \ud2b8\ub79c\uc7ad\uc158\uc744 \uc2dd\ubcc4\ud558\uae30 \uc704\ud55c \uac12\uc73c\ub85c 1\uc529 \uc99d\uac00\ud558\ub294 \ud615\ud0dc\ub85c \ubc1c\uae09\ub41c\ub2e4. \\n:::\\n\\n### \ubcf5\uc81c \ud1a0\ud3f4\ub85c\uc9c0\\n\\n**\uc2f1\uae00 \ub808\ud50c\ub9ac\uce74 \ubcf5\uc81c \uad6c\uc131**\\n\\n\uac00\uc7a5 \uac04\ub2e8\ud55c \uad6c\uc131\uc73c\ub85c \uc81c\uc77c \ub9ce\uc774 \uc0ac\uc6a9\ud558\ub294 \ud615\ud0dc\ub2e4. \\nreplica \uc11c\ubc84\ub97c \uc77d\uae30 \uc804\uc6a9, \uc608\ube44 \uc11c\ubc84, \ubc31\uc5c5 \uc6a9\ub3c4\ub85c \ub9ce\uc774 \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n W[Web Server] -- \uc77d\uae30 + \uc4f0\uae30 --\x3e S\\n W -- \uc77d\uae30 --\x3e R\\n S[Source] --\x3e R[Replica]\\n```\\n\\n**\uba40\ud2f0 \ub808\ud50c\ub9ac\uce74 \ubcf5\uc81c \uad6c\uc131**\\n\\n2\uac1c\uc758 replica \uc11c\ubc84\ub97c \uc0ac\uc6a9\ud558\ub294 \ud615\ud0dc\ub2e4. \\n\ud558\ub098\uc758 replica\ub294 \uc608\ube44 \uc6a9\ub3c4\ub85c \ub0a8\uaca8\ub450\ub294 \ud615\ud0dc\ub2e4. \\n\ucd94\ud6c4\uc5d0 \ud2b8\ub798\ud53d\uc774 \uc99d\uac00\ud558\ub294 \uacbd\uc6b0 \uc608\ube44 \uc6a9\ub3c4\uc758 replica\ub97c \uc0ac\uc6a9\ud568\uc73c\ub85c \uc77d\uae30 \uc694\uccad\uc758 \ubd80\ud558 \ubd84\uc0b0\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\\n```mermaid\\ngraph LR\\n W[Web Server] -- \uc77d\uae30 + \uc4f0\uae30 --\x3e S\\n W -- \uc77d\uae30 --\x3e R1\\n S[Source] --\x3e R1[Replica1]\\n S --\x3e R2[Replica2]\\n```\\n\\n**\uccb4\uc778 \ubcf5\uc81c \uad6c\uc131**\\n\\nreplica \uc11c\ubc84\uac00 \ub9ce\uc740 \uacbd\uc6b0 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8\ub97c \uc804\ub2ec\ud558\ub294 \uc791\uc5c5 \uc790\uccb4\uac00 \ubd80\ud558\uac00 \ub420 \uc218 \uc788\ub2e4. \\n\ub530\ub77c\uc11c 1:M:M \uad6c\uc870\ub85c \uccb4\uc778 \ubcf5\uc81c \uad6c\uc131\uc744 \uace0\ub824\ud560 \uc218 \uc788\ub2e4. \\n\\n```mermaid\\ngraph LR\\n W[Web Server] -- \uc77d\uae30 + \uc4f0\uae30 --\x3e S\\n W -- \uc77d\uae30 --\x3e R1\\n S[Source] --\x3e R1[Replica1]\\n S --\x3e R2[Replica2]\\n S --\x3e R3[Replica3]\\n\\n R3 --\x3e R3-1[Replica 3-1]\\n R3 --\x3e R3-2[Replica 3-2]\\n\\n B[Batch Server] --\x3e R3-2\\n```\\n\\n**\ub4c0\uc5bc \uc18c\uc2a4 \ubcf5\uc81c \uad6c\uc131**\\n\\n2\uac1c\uc758 MySQL \uc11c\ubc84 \ubaa8\ub450 \uc77d\uae30\uc640 \uc4f0\uae30\uac00 \uac00\ub2a5\ud558\ub3c4\ub85d \ud558\ub294 \uad6c\uc131\uc774\ub2e4. \\n\uac01 \uc11c\ubc84\uc5d0\uc11c \ubcc0\uacbd\ub41c \ub370\uc774\ud130\ub294 \ub2e4\ub978 \uc11c\ubc84\uc5d0 \ubc18\uc601\ub41c\ub2e4. \\n\ubaa9\uc801\uc5d0 \ub530\ub77c ACTIVE-ACTIVE \ud615\ud0dc \ub610\ub294 ACTIVE-PASSIVE \ud615\ud0dc\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. \\nACTIVE-PASSIVE \ud615\ud0dc\uc778 \uacbd\uc6b0 \uc2f1\uae00 \ub808\ud50c\ub9ac\uce74 \ubcf5\uc81c \uad6c\uc131\uacfc \ub3d9\uc77c\ud574\ubcf4\uc774\uc9c0\ub9cc, ACTIVE \uc11c\ubc84\uc5d0\uc11c \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud558\uba74 \uc124\uc815\uc758 \ubcc0\uacbd\uc5c6\uc774 PASSIVE \uc11c\ubc84\ub85c \uc4f0\uae30 \uc791\uc5c5\uc744 \uc804\ud658\ud560 \uc218 \uc788\ub2e4\ub294 \uac83\uc774 \uc7a5\uc810\uc774\ub2e4. \\n\\n```mermaid\\ngraph LR\\n W[Web Server] -- \uc77d\uae30 + \uc4f0\uae30 --\x3e SR1\\n W -- \uc77d\uae30 + \uc4f0\uae30 --\x3e SR2\\n SR1[Source/Replica 1] --\x3e SR2[Source/Replica 2]\\n```\\n\\n:::note ACTIVE-ACTIVE, ACTIVE-PASSIVE\\nACTIVE-ACTIVE: 2\uac1c\uc758 \uc11c\ubc84 \ubaa8\ub450 \uc4f0\uae30 \uc791\uc5c5\uc744 \uc218\ud589\ud558\ub294 \ud615\ud0dc \\nACTIVE-PASSIVE: \ud558\ub098\uc758 \uc11c\ubc84\uc5d0\uc11c\ub9cc \uc4f0\uae30 \uc791\uc5c5\uc744 \uc218\ud589\ud558\ub294 \ud615\ud0dc\\n:::\\n\\n**\uba40\ud2f0 \uc18c\uc2a4 \ubcf5\uc81c \uad6c\uc131**\\n\\n\uc5ec\ub7ec\uac1c\uc758 source \uc11c\ubc84\uc640 \ud558\ub098\uc758 replica \uc11c\ubc84\ub97c \uc0ac\uc6a9\ud558\ub294 \uad6c\uc131\uc774\ub2e4. \\n\uc774\ub294 source \uc11c\ubc84\uc758 \ub370\uc774\ud130\ub97c \ud55c \uacf3\uc5d0 \ubc31\uc5c5\ud558\ub294 \uc6a9\ub3c4\ub85c \uc0ac\uc6a9, \uc5ec\ub7ec \uc11c\ubc84\uc5d0 \uc874\uc7ac\ud558\ub294 \ub370\uc774\ud130\ub97c \ud1b5\ud569, \uc0e4\ub529\ub418\uc5b4\uc788\ub294 \ud14c\uc774\ube14 \ub370\uc774\ud130\ub97c \ud1b5\ud569\ud560 \ub54c \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n S1[Source 1] --\x3e R[Replica]\\n S2[Source 2] --\x3e R\\n```\\n\\n## \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ubc29\uc2dd Replication \uad6c\uc131\ud558\uae30\\n\\nmysql 2\ub300\ub97c \uc774\uc6a9\ud558\uc5ec replication\uc744 \uad6c\uc131\ud558\uace0, spring boot application\uc73c\ub85c source, replica \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc811\uadfc\ud574\ubcf4\ub294 \uc608\uc81c\uc774\ub2e4. \\n[https://github.com/bbiac/db-replication](https://github.com/bbiac/db-replication) \\n\\n### MySQL \ud658\uacbd \uad6c\uc131\\n\\nMySQL \ubc84\uc804\uc740 8.1\uc744 \uc0ac\uc6a9\ud588\ub2e4. \\n13306, 13307 \ud3ec\ud2b8\ub97c \uc0ac\uc6a9\ud574\uc11c MySQL \uc11c\ubc84 2\ub300\ub97c \ub744\uc6e0\ub2e4. \\n\ub610\ud55c \uc0ac\uc2e4 IP \ub300\uc5ed\uc73c\ub85c \ud1b5\uc2e0\ud560 \uc218 \uc788\ub3c4\ub85d \ucee4\uc2a4\ud140 \ub124\ud2b8\uc6cc\ud06c\ub97c \ucd94\uac00\ud588\ub2e4. \\n\\n```yml\\nversion: \'3.8\'\\n\\nservices:\\n source:\\n platform: linux/x86_64\\n image: mysql:latest\\n restart: always\\n container_name: mysql-source\\n environment:\\n TZ: \'Asia/Seoul\'\\n MYSQL_DATABASE: \'db\'\\n MYSQL_USER: \'user\'\\n MYSQL_PASSWORD: \'password\'\\n MYSQL_ROOT_PASSWORD: \'password\'\\n ports:\\n - \\"13306:3306\\"\\n volumes:\\n - db-source:/var/lib/mysql\\n - db-source:/var/lib/mysql-files\\n - ./docker/source.cnf:/etc/mysql/my.cnf\\n networks:\\n - mysql_network\\n\\n replica:\\n platform: linux/x86_64\\n image: mysql:latest\\n restart: always\\n container_name: mysql-replica\\n environment:\\n TZ: \'Asia/Seoul\'\\n MYSQL_DATABASE: \'db\'\\n MYSQL_USER: \'user\'\\n MYSQL_PASSWORD: \'password\'\\n MYSQL_ROOT_PASSWORD: \'password\'\\n ports:\\n - \\"13307:3306\\"\\n volumes:\\n - db-replica:/var/lib/mysql\\n - db-replica:/var/lib/mysql-files\\n - ./docker/replica.cnf:/etc/mysql/my.cnf\\n networks:\\n - mysql_network\\n\\nvolumes:\\n db-source:\\n db-replica:\\n\\nnetworks:\\n mysql_network:\\n driver: bridge\\n```\\n\\n\ub610\ud55c source, replica \uac01\uac01 \ub2e4\uc74c\uacfc \uac19\uc774 db \uc124\uc815\uc744 \ud588\ub2e4. \\n\\n| \uc124\uc815 | \uc124\uba85 |\\n| --- | --- |\\n| server_id | \uac01\uac01\uc758 mysql \ub9c8\ub2e4 \uace0\uc720\ud55c \uac12\uc744 \uac00\uc838\uc57c \ud55c\ub2e4. |\\n| log_bin | \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ud30c\uc77c \uacbd\ub85c \uc124\uc815\uc73c\ub85c \uc808\ub300\uacbd\ub85c\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294\ub2e4\uba74 /var/lib/mysql \uc544\ub798 \ud574\ub2f9 log_bin\uc5d0 \uc124\uc815\ub41c \uac12\uc73c\ub85c \ub85c\uadf8\uac00 \uc0dd\uc131\ub41c\ub2e4. |\\n| sync_binlog | N\uac1c\uc758 \ud2b8\ub79c\uc7ad\uc158 \ub2f9 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8\ub97c \ub514\uc2a4\ud06c\uc640 \ub3d9\uae30\ud654 \uc791\uc5c5\uc744 \ud558\ub3c4\ub85d \ud55c\ub2e4.\xa01\uc740 \uae30\ubcf8\uac12\uc73c\ub85c \uc548\uc815\uc801\uc774\uc9c0\ub9cc, \uac00\uc7a5 \ub290\ub9ac\ub2e4. |\\n| relay_log | \ub9b4\ub808\uc774 \ub85c\uadf8 \ud30c\uc77c \uacbd\ub85c \uc124\uc815 |\\n| relay_log_purge | \ud544\uc694 \uc5c6\ub294 \ub9b4\ub808\uc774 \ub85c\uadf8 \ud30c\uc77c\uc744 \uc790\ub3d9\uc73c\ub85c \uc0ad\uc81c\ud558\ub294 \uc635\uc158 |\\n| read_only | \uc77d\uae30 \uc804\uc6a9 \uc124\uc815 |\\n| log_replica_updates | Replication SQL Thread\ub85c \uc778\ud574 \uc2e4\ud589\ub418\ub294 \uc815\ubcf4\ub97c \ubc14\uc774\ub108\ub9ac \ub85c\uadf8\uc5d0 \uae30\ub85d \ucd94\ud6c4\uc5d0 \uc18c\uc2a4 \uc11c\ubc84\ub85c \uc2b9\uaca9\ub418\ub294 \uacbd\uc6b0\ub97c \uace0\ub824\ud558\uba74 \uc124\uc815\ud558\ub294 \uac83\uc774 \uc88b\ub2e4. |\\n\\nimport Tabs from \\"@theme/Tabs\\";\\nimport TabItem from \\"@theme/TabItem\\";\\n\\n\\n\\n\\n```cnf title=\\"/docker/source.cnf\\"\\n[mysqld]\\nserver_id=1\\nlog_bin=mysql-bin\\nsync_binlog=1\\n```\\n\\n\\n\\n\\n\\n```cnf title=\\"/docker/replica.cnf\\"\\n[mysqld]\\nserver_id=2\\nrelay_log=mysql-relay-bin\\nrelay_log_purge=ON\\nread_only\\nlog_replica_updates\\n```\\n\\n\\n\\n\\n### \ub3c4\ucee4 \uc2e4\ud589\\n\\ndocker-compose up \uba85\ub839\uc5b4\ub85c docker-compose \uc124\uc815\uc73c\ub85c docker\ub97c \ub744\uc6b4\ub2e4. \\n-d \uc635\uc158\uc744 \ubd99\uc774\uba74 \ubc31\uadf8\ub77c\uc6b4\ub4dc \ubaa8\ub4dc\ub85c \uc2e4\ud589\ub41c\ub2e4. \\n\\n```\\ndocker-compose up -d\\n```\\n\\n### replication slave \uad8c\ud55c \uc124\uc815\\n\\nREPLICATION SLAVE \uad8c\ud55c\uc774 \uc124\uc815\ub418\uc5b4 \uc788\uc5b4\uc57c replica \uc11c\ubc84\uc5d0\uc11c source \uc11c\ubc84\uc5d0 \uc811\uadfc\ud558\uc5ec \ub85c\uadf8\ub97c \uc77d\uc5b4\uc62c \uc218 \uc788\ub2e4. \\nsource \uc11c\ubc84\uc5d0 \uc811\uadfc\ud558\uc5ec user \uacc4\uc815\uc5d0 \ud574\ub2f9 \uad8c\ud55c\uc744 \uc124\uc815\ud574\uc900\ub2e4. \\n\\nSOURCE \uc811\uc18d\\n\\n```bash\\ndocker exec -it mysql-source mysql -u root -p\\n```\\n\\nuser \uacc4\uc815\uc5d0 REPLICATION SLAVE \uad8c\ud55c \ucd94\uac00\\n\\n```mysql\\nGRANT REPLICATION SLAVE ON *.* TO \'user\'@\'%\';\\nFLUSH PRIVILEGES;\\n```\\n\\n### SOURCE DB \uc815\ubcf4 \ud655\uc778\\n\\nreplica \uc124\uc815\uc5d0 \ud544\uc694\ud55c source db\uc758 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ud30c\uc77c\uba85\uacfc Position\uc744 \ud655\uc778\ud55c\ub2e4. \\nPosition \uac12\uc740 \uc2e4\uc81c \ud30c\uc77c\uc758 \ubc14\uc774\ud2b8 \uc218\ub97c \uc758\ubbf8\ud55c\ub2e4. \\n\ud655\uc778\ud55c File(SOURCE_LOG_FILE)\uacfc Position(SOURCE_LOG_POS) \uac12\uc740 replica \uc124\uc815\uc5d0\uc11c \uc0ac\uc6a9\ud55c\ub2e4.\\n\\n```mysql\\nSHOW MASTER STATUS;\\n\\n+------------------+----------+--------------+------------------+-------------------+\\n| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |\\n+------------------+----------+--------------+------------------+-------------------+\\n| mysql-bin.000003 | 1082 | | | |\\n+------------------+----------+--------------+------------------+-------------------+\\n```\\n\\n### SOURCE ip \uc8fc\uc18c \ud655\uc778\\n\\ndocker inspect -f \uc635\uc158\uc744 \uc0ac\uc6a9\ud558\uba74 \ud574\ub2f9 \ucee8\ud14c\uc774\ub108\uc758 \uc138\ubd80 \uc815\ubcf4\ub97c \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\ub2e4\uc74c \uba85\ub839\uc5b4\ub97c \uc774\uc6a9\ud574 docker-compose \ud30c\uc77c\uc5d0 \uc124\uc815\ud574\ub454 mysql_network\uc5d0\uc11c \uc0ac\uc6a9\ub418\ub294 \uc0ac\uc124 \uc544\uc774\ud53c \uc8fc\uc18c\ub97c \ud655\uc778\ud55c\ub2e4. \\n\\n```bash\\ndocker inspect -f \\"{{with index .NetworkSettings.Networks \\\\\\"db-replication_mysql_network\\\\\\"}}{{.IPAddress}}{{end}}\\" mysql-source\\n```\\n\\nip \uc8fc\uc18c\uac00 \ub098\uc624\uc9c0 \uc54a\ub294 \uacbd\uc6b0 docker inspect mysql-source\ub85c \ud655\uc778\ud55c\ub2e4. \\n\ud655\uc778\ud55c IP\uc8fc\uc18c(SOURCE_HOST) \uac12\uc740 replica \uc124\uc815\uc5d0\uc11c \uc0ac\uc6a9\ud55c\ub2e4.\\n\\n### replica mysql \uc811\uc18d\\n\\nsource db\uc5d0 \uc811\uc18d\ud588\ub358 \ubc29\ubc95\uacfc \ub3d9\uc77c\ud558\uac8c replica db\uc5d0 \uc811\uc18d\ud55c\ub2e4. \\n\\n```bash\\ndocker exec -it mysql-replica mysql -u root -p\\n```\\n\\n### replica \uc124\uc815\\n\\n\uc774\uc804\uc5d0 source db\uc5d0\uc11c \uc5bb\uc5c8\ub358 \uc815\ubcf4\ub4e4\uc744 \uc0ac\uc6a9\ud558\uc5ec replica \uc124\uc815\uc744 \uc9c4\ud589\ud55c\ub2e4. \\n\uc2e4\uc81c DB \uc11c\ubc84\uc5d0\uc11c \ubcf5\uc81c\ud558\ub294 \uacbd\uc6b0 \ucd94\uac00\uc801\uc73c\ub85c source DB\uc758 \ud30c\uc77c\uc744 \ubcf5\uc81c\ud574\uc57c\ud558\uc9c0\ub9cc \ud604\uc7ac \ubcf5\uc81c\ud560 \ub370\uc774\ud130\uac00 \uc5c6\uae30 \ub54c\ubb38\uc5d0 \ud574\ub2f9 \ubd80\ubd84\uc740 \uc0dd\ub7b5\ud588\ub2e4. \\nSOURCE_HOST, SOURCE_LOG_FILE, SOURCE_LOG_POS \ub97c \uc801\uc808\ud788 \ubcc0\uacbd\ud55c\ub2e4.\\n\\n```mysql\\nSTOP REPLICA;\\n\\nCHANGE REPLICATION SOURCE TO \\nSOURCE_HOST=\'172.29.0.2\', \\nSOURCE_USER=\'user\', \\nSOURCE_PASSWORD=\'password\', \\nSOURCE_LOG_FILE=\'mysql-bin.000001\', \\nSOURCE_LOG_POS=0, \\nGET_SOURCE_PUBLIC_KEY=1;\\n\\nSTART REPLICA;\\n```\\n\\n### \uc124\uc815 \ud655\uc778\\n\\n```mysql\\nSHOW REPLICA STATUS;\\n\\n+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+\\n| Replica_IO_State | Source_Host | Source_User | Source_Port | Connect_Retry | Source_Log_File | Read_Source_Log_Pos | Relay_Log_File | Relay_Log_Pos | Relay_Source_Log_File | Replica_IO_Running | Replica_SQL_Running | Replicate_Do_DB | Replicate_Ignore_DB | Replicate_Do_Table | Replicate_Ignore_Table | Replicate_Wild_Do_Table | Replicate_Wild_Ignore_Table | Last_Errno | Last_Error | Skip_Counter | Exec_Source_Log_Pos | Relay_Log_Space | Until_Condition | Until_Log_File | Until_Log_Pos | Source_SSL_Allowed | Source_SSL_CA_File | Source_SSL_CA_Path | Source_SSL_Cert | Source_SSL_Cipher | Source_SSL_Key | Seconds_Behind_Source | Source_SSL_Verify_Server_Cert | Last_IO_Errno | Last_IO_Error | Last_SQL_Errno | Last_SQL_Error | Replicate_Ignore_Server_Ids | Source_Server_Id | Source_UUID | Source_Info_File | SQL_Delay | SQL_Remaining_Delay | Replica_SQL_Running_State | Source_Retry_Count | Source_Bind | Last_IO_Error_Timestamp | Last_SQL_Error_Timestamp | Source_SSL_Crl | Source_SSL_Crlpath | Retrieved_Gtid_Set | Executed_Gtid_Set | Auto_Position | Replicate_Rewrite_DB | Channel_Name | Source_TLS_Version | Source_public_key_path | Get_Source_public_key | Network_Namespace |\\n+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+\\n| Waiting for source to send event | 172.25.0.3 | user | 3306 | 60 | mysql-bin.000003 | 1082 | mysql-relay-bin.000002 | 868 | mysql-bin.000003 | Yes | Yes | | | | | | | 0 | | 0 | 1082 | 1078 | None | | 0 | No | | | | | | 0 | No | 0 | | 0 | | | 1 | 5a396b02-41c6-11ee-a56d-0242ac190003 | mysql.slave_master_info | 0 | NULL | Replica has read all relay log; waiting for more updates | 86400 | | | | | | | | 0 | | | | | 1 | |\\n+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+\\n```\\n\\nReplica_IO_Running, Replica_SQL_Running \uac12\uc774 YES\ub77c\uba74 \uc815\uc0c1\uc801\uc73c\ub85c replication \uad6c\uc131\uc774 \uc644\ub8cc\ub41c \uac83\uc774\ub2e4. \\n\\n\uc124\uc815\uc744 \ub9c8\uce5c \ud6c4 source db\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc774 create table \uba85\ub839\uc5b4\ub97c \uc785\ub825\ud55c\ub2e4. \\nreplica db\uc5d0 \ub3d9\uc77c\ud55c member table\uc774 \uc0dd\uc131\ub41c \uac83\uc744 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n```sql\\nCREATE TABLE member\\n(\\n id BIGINT PRIMARY KEY AUTO_INCREMENT,\\n name VARCHAR(255)\\n);\\n```\\n\\n## \uc2a4\ud504\ub9c1 \ubd80\ud2b8\ub85c DB \uc811\uadfc\ud558\uae30\\n\\n\uc77c\ubc18\uc801\uc778 \ud2b8\ub79c\uc7ad\uc158\uc758 \uacbd\uc6b0 source, \uc77d\uae30 \uc804\uc6a9 \ud2b8\ub79c\uc7ad\uc158\uc778 \uacbd\uc6b0 replica\ub85c \uc694\uccad\uc774 \uac00\ub3c4\ub85d \uad6c\uc131\ud574\ubcf4\uc790. \\n\\n### Environment \uc124\uc815\\n\\n\ub2e4\uc74c\uacfc \uac19\uc774 source, replica\ub85c \uad6c\ubd84\ud558\uc5ec \uc124\uc815\ud55c\ub2e4. \\n\\n```yml title=\\"application.yml\\"\\nspring:\\n datasource:\\n source:\\n username: user\\n password: password\\n driver-class-name: com.mysql.cj.jdbc.Driver\\n jdbc-url: jdbc:mysql://localhost:13306/db\\n replica:\\n username: user\\n password: password\\n driver-class-name: com.mysql.cj.jdbc.Driver\\n jdbc-url: jdbc:mysql://localhost:13307/db\\n```\\n\\n### DataSourceType \uc124\uc815\\n\\n\ub2e8\uc21c \ubb38\uc790\uc5f4\ub85c\ub3c4 \uad6c\ubd84\ud560 \uc218 \uc788\uc9c0\ub9cc, enum\uc744 \uc774\uc6a9\ud574\uc11c \ud2b8\ub79c\uc7ad\uc158\uc744 \uad6c\ubd84\ud558\ub3c4\ub85d \uc0dd\uc131\ud55c\ub2e4. \\nKey\ub294 \ucd94\ud6c4\uc5d0 \ube48 \uc124\uc815\uc5d0 \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n```java title=\\"DataSourceType\\"\\npublic enum DataSourceType {\\n SOURCE(SOURCE_NAME),\\n REPLICA(REPLICA_NAME),\\n ;\\n\\n private final String key;\\n\\n DataSourceType(String key) {\\n this.key = key;\\n }\\n\\n public static class Key {\\n public static final String ROUTING_NAME = \\"ROUTING\\";\\n public static final String SOURCE_NAME = \\"SOURCE\\";\\n public static final String REPLICA_NAME = \\"REPLICA\\";\\n }\\n}\\n```\\n\\n### AbstractRoutingDataSource \uc124\uc815\\n\\n\uc2a4\ud504\ub9c1\uc774 \uc9c0\uc6d0\ud574\uc8fc\ub294 AbstractRoutingDataSource\ub97c \uc0c1\uc18d\ubc1b\uc544 \ud2b8\ub79c\uc7ad\uc158\uc758 \uc77d\uae30 \uc5ec\ubd80\uc5d0 \ub530\ub77c \ub2e4\ub978 DataSource\ub97c \ud5a5\ud558\ub3c4\ub85d \uc124\uc815\ud55c\ub2e4. \\n\\n\uc815\uc801 \ud329\ud130\ub9ac \uba54\uc11c\ub4dc\ub294 Map\uc5d0 \ud574\ub2f9\ud558\ub294 \uac12\uc744 \ubc1b\uc544 \ub370\uc774\ud130 \uc18c\uc2a4\ub97c \uc124\uc815\ud55c\ub2e4. \\n- setDefaultTargetDataSource: \uae30\ubcf8 \ub370\uc774\ud130 \uc18c\uc2a4\ub97c \uc124\uc815\ud55c\ub2e4. \\n- setTargetDataSources: \ub9f5 \ud615\ud0dc\ub85c \ubc1b\uc740 \ub370\uc774\ud130 \uc18c\uc2a4 \uac12\ub4e4\uc744 \uc124\uc815\ud55c\ub2e4. \\n\\ndetermineCurrentLookupKey\ub97c \uc624\ubc84\ub77c\uc774\ub529\ud558\uc5ec \ud2b8\ub79c\uc7ad\uc158\uc758 \uc77d\uae30 \uc5ec\ubd80\uc5d0 \ub530\ub77c \ub2e4\ub978 DataSourceType\uc744 \ubc18\ud658\ud558\ub3c4\ub85d \uc124\uc815\ud55c\ub2e4. \\n- isCurrentTransactionReadOnly() \uba54\uc11c\ub4dc\ub97c \ud1b5\ud574 \ud2b8\ub79c\uc7ad\uc158\uc774 \uc77d\uae30 \uc804\uc6a9\uc778\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n- DataSourceType\uc744 \ubc18\ud658\ud558\ub3c4\ub85d \uc124\uc815\ud558\uace0, \ubc18\ud658\ud55c \uac12\uc5d0 \ud574\ub2f9\ud558\ub294 \ub370\uc774\ud130 \uc18c\uc2a4\uac00 \uc0ac\uc6a9\ub41c\ub2e4. \\n\\n```java title=\\"RoutingDataSource\\"\\npublic class RoutingDataSource extends AbstractRoutingDataSource {\\n\\n private final Logger log = LoggerFactory.getLogger(getClass());\\n\\n public static RoutingDataSource from(Map dataSources) {\\n RoutingDataSource routingDataSource = new RoutingDataSource();\\n routingDataSource.setDefaultTargetDataSource(dataSources.get(DataSourceType.SOURCE));\\n routingDataSource.setTargetDataSources(dataSources);\\n return routingDataSource;\\n }\\n\\n @Override\\n protected Object determineCurrentLookupKey() {\\n boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();\\n\\n if (readOnly) {\\n log.info(\\"readOnly = true, request to replica\\");\\n return DataSourceType.REPLICA;\\n }\\n log.info(\\"readOnly = false, request to source\\");\\n return DataSourceType.SOURCE;\\n }\\n}\\n```\\n\\n### DataSource \uc124\uc815\\n\\n\uc704\uc5d0\uc11c\ubd80\ud130 \uc21c\uc11c\ub300\ub85c Source, Replica, RoutingDataSource, LazyConnectionDataSourceProxy \uc124\uc815\uc774\ub2e4. \\n\uc2a4\ud504\ub9c1\uc740 \ud2b8\ub79c\uc7ad\uc158 \uc2dc\uc791\uc2dc\uc5d0 \ucee4\ub125\uc158\uc758 \uc0ac\uc6a9\uc5ec\ubd80\uc640 \uc0c1\uad00\uc5c6\uc774 \ucee4\ub125\uc158\uc744 \ud655\ubcf4\ud55c\ub2e4. \\n\ub530\ub77c\uc11c readOnly \ud2b8\ub79c\uc7ad\uc158\uc774 \uc124\uc815\ub41c \uba54\uc11c\ub4dc\ub97c \uc0ac\uc6a9\ud558\ub354\ub77c\ub3c4 \ubbf8\ub9ac \ud655\ubcf4\ub41c \ucee4\ub125\uc158\uc744 \uc0ac\uc6a9\ud558\uae30 \ub54c\ubb38\uc5d0 replica db\ub85c \uc694\uccad\uc744 \ud558\uc9c0 \uc54a\uace0 setDefaultTargetDataSource\ub85c \uc124\uc815\ud55c source db\ub85c \uc694\uccad\uc744 \ud55c\ub2e4. \\nLazyConnectionDataSourceProxy\ub97c \uc124\uc815\ud558\ub294 \uacbd\uc6b0 \uc2e4\uc81c DataSource\ub97c \uc0ac\uc6a9\ud558\ub294 \uc2dc\uc810\uc5d0 \ucee4\ub125\uc158\uc744 \ud68d\ub4dd\ud574\uc11c \uc0ac\uc6a9\ud558\uae30 \ub54c\ubb38\uc5d0 \uc124\uc815\ud55c\ub300\ub85c replica db\ub85c \uc870\ud68c \uc694\uccad\uc744 \ud55c\ub2e4. \\n\\n```java title=\\"DataSourceConfiguration\\"\\n@Configuration\\npublic class DataSourceConfiguration {\\n\\n @Bean\\n @Qualifier(SOURCE_NAME)\\n @ConfigurationProperties(prefix = \\"spring.datasource.source\\")\\n public DataSource sourceDataSource() {\\n return DataSourceBuilder.create().build();\\n }\\n\\n @Bean\\n @Qualifier(REPLICA_NAME)\\n @ConfigurationProperties(prefix = \\"spring.datasource.replica\\")\\n public DataSource replicaDataSource() {\\n return DataSourceBuilder.create().build();\\n }\\n\\n @Bean\\n @Qualifier(ROUTING_NAME)\\n public DataSource routingDataSource(\\n @Qualifier(SOURCE_NAME) DataSource sourceDataSource,\\n @Qualifier(REPLICA_NAME) DataSource replicaDataSource\\n ) {\\n return RoutingDataSource.from(Map.of(\\n DataSourceType.SOURCE, sourceDataSource,\\n DataSourceType.REPLICA, replicaDataSource\\n ));\\n }\\n\\n @Bean\\n @Primary\\n public DataSource dataSource(\\n @Qualifier(ROUTING_NAME) DataSource routingDataSource\\n ) {\\n return new LazyConnectionDataSourceProxy(routingDataSource);\\n }\\n}\\n```\\n\\n\ucd5c\uc885\uc801\uc73c\ub85c DataSource \ube48\uc740 \ub2e4\uc74c\uacfc \uac19\uc740 \ud615\ud0dc\uac00 \ub41c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n DSP[LazyConnectionDataSourceProxy] --\x3e RDS[RoutingDataSource]\\n\\tRDS --\x3e S[SourceDataSource]\\n\\tRDS --\x3e R[ReplicaDataSource]\\n```\\n\\n### \ub3d9\uc791 \ud655\uc778\\n\\n\uac04\ub2e8\ud558\uac8c \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud574\uc11c \uc124\uc815\ud55c\ub300\ub85c \ub3d9\uc791\uc774 \ub418\ub294\uc9c0 \ud655\uc778\ud574\ubcf4\uc558\ub2e4. \\nsave \uba54\uc11c\ub4dc\uc758 \uacbd\uc6b0 `@Transactional`, findById \uba54\uc11c\ub4dc\uc758 \uacbd\uc6b0 `@Transactional(readOnly = true)`\uac00 \uc124\uc815\ub418\uc5b4\uc788\ub2e4. \\n\ub85c\uadf8\ub97c \ud1b5\ud574 save\uc758 \uacbd\uc6b0 source db\ub85c findById\uc758 \uacbd\uc6b0 replica db\ub85c \uc694\uccad\uc744 \ud558\ub294 \uac83\uc744 \uc54c \uc218 \uc788\ub2e4. \\n\\n```java title=\\"MemberServiceTest\\"\\n@SpringBootTest\\nclass MemberServiceTest {\\n\\n @Autowired\\n private MemberService memberService;\\n\\n @Test\\n void \uc0ac\uc6a9\uc790\ub97c_\uc800\uc7a5\ud55c\ub2e4() {\\n // RoutingDataSource log: readOnly = false\\n memberService.save(\\"bbiac\\");\\n }\\n\\n @Test\\n void \uc0ac\uc6a9\uc790\ub97c_\uc870\ud68c\ud55c\ub2e4() {\\n // RoutingDataSource log: readOnly = true\\n assertThatThrownBy(() -> memberService.findById(MAX_VALUE))\\n .isInstanceOf(NoSuchElementException.class);\\n }\\n}\\n```\\n\\nDB\uc5d0\uc11c\ub294 \ud655\uc778\ud558\ub824\uba74 root \uacc4\uc815\uc73c\ub85c \uc811\uc18d\ud55c \ud6c4 general log\ub97c \ud65c\uc131\ud654 \uc2dc\ud0a8\ub2e4. \\n\\n```sql\\nSET GLOBAL log_output = \'table\';\\nSET GLOBAL general_log = 1;\\n```\\n\\ngeneral log\ub97c \ud65c\uc131\ud654 \ud55c \ud6c4 \uc77d\uae30 \uc804\uc6a9 \uba54\uc11c\ub4dc\ub97c \uc2e4\ud589\ud55c\ub2e4. \\nserver_id, \uc2e4\ud589\ud55c \ucffc\ub9ac\ubb38\uc744 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n```sql\\nSELECT user_host, thread_id, server_id, convert(argument using utf8) FROM mysql.general_log where argument like \'%select%\';\\n\\n+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+\\n| user_host | thread_id | server_id | convert(argument using utf8) |\\n+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+\\n| user[user] @ [172.25.0.1] | 277 | 2 | select m1_0.id,m1_0.name from member m1_0 where m1_0.id=9223372036854775807 |\\n+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+\\n```\\n\\n\ud655\uc778 \ud6c4 general log\ub97c \ube44\ud65c\uc131\ud654 \ud55c \ud6c4 \ube44\ud65c\uc131\ud654 \ub418\uc5c8\ub294\uc9c0 \ud655\uc778\ud55c\ub2e4. \\n```sql\\nSET GLOBAL general_log = 0;\\nSHOW VARIABLES LIKE \'%general%\';\\n\\n+------------------+---------------------------------+\\n| Variable_name | Value |\\n+------------------+---------------------------------+\\n| general_log | OFF |\\n| general_log_file | /var/lib/mysql/4b6b9db98290.log |\\n+------------------+---------------------------------+\\n```\\n\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n16\uc7a5 \ubcf5\uc81c, Real MySQL 8.0 - \ubc31\uc740\ube48, \uc774\uc131\uc6b1 \\n[Replication, MySQL Docs](https://dev.mysql.com/doc/refman/8.1/en/replication.html) \\n[MySql - Master Slave Replication \uad6c\uc870 \ub9cc\ub4e4\uc5b4\ubcf4\uae30](https://huisam.tistory.com/entry/mysql-replication) \\n[Spring \ub808\ud50c\ub9ac\ucf00\uc774\uc158 \ud2b8\ub79c\uc7ad\uc158 \ucc98\ub9ac \ubc29\uc2dd](https://cheese10yun.github.io/spring-transaction/) \\n[replication-datasource](https://github.com/kwon37xi/replication-datasource) \\n[Simplified Guide to MySQL Replication with Docker Compose](https://www.linkedin.com/pulse/simplified-guide-mysql-replication-docker-compose-rakesh-shekhawat/) \\n[Dockerfile\uc5d0\uc11c \uc790\uc8fc \uc4f0\uc774\ub294 \uba85\ub839\uc5b4](https://www.daleseo.com/dockerfile/) \\n[CHANGE REPLICATION SOURCE TO Statement](https://dev.mysql.com/doc/refman/8.1/en/change-replication-source-to.html) \\n[LazyConnectionDataSourceProxy](https://kwonnam.pe.kr/wiki/springframework/lazyconnectiondatasourceproxy) \\n[\ub370\uc774\ud130\ubca0\uc774\uc2a4 \ub808\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \ud1b5\ud55c \ucffc\ub9ac \uc131\ub2a5 \uac1c\uc120 (feat. Mysql, SpringBoot)](https://hudi.blog/database-replication-with-springboot-and-mysql/) \\n[\ubd80\ud558 \ubd84\uc0b0\uc744 \uc704\ud55c MySQL Replication \uad6c\uc131 \ubc0f \ucffc\ub9ac \uc694\uccad \ubd84\uae30](https://chagokx2.tistory.com/100) \\n[Use Docker Compose, Docker](https://docs.docker.com/get-started/08_using_compose/)"},{"id":"woowacourse-level3-retrospective","metadata":{"permalink":"/woowacourse-level3-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-08-19-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca83 \ud68c\uace0/2023-08-19-\ub808\ubca8 3 \ud68c\uace0.mdx","source":"@site/blog/2023-3/2023-08-19-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca83 \ud68c\uace0/2023-08-19-\ub808\ubca8 3 \ud68c\uace0.mdx","title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 3 \ud68c\uace0","description":"\ud68c\uace0","date":"2023-08-19T00:00:00.000Z","formattedDate":"2023\ub144 8\uc6d4 19\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":3.945,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 3 \ud68c\uace0","slug":"woowacourse-level3-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30","permalink":"/db-replication"},"nextItem":{"title":"CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131","permalink":"/cloudwatch"}},"content":"import logo from \'./logo.png\';\\n\\n### \ud68c\uace0\\n\\n\uc9c0\ub09c 8\uc8fc\ub294 \ub808\ubca8 1, 2 \ub54c\ubcf4\ub2e4 5\ubc30 \uc815\ub3c4 \ube60\ub974\uac8c \uc9c0\ub098\uac04 \uac83 \uac19\uc740 \ub290\ub08c\uc774 \ub4e4\uc5c8\ub2e4. \\n\ub808\ubca8 3\uc5d0\ub294 \uae30\uc220\uc801\uc778 \ubd80\ubd84\uc5d0\uc11c\ub3c4, \uae30\uc220 \uc678\uc801\uc778 \ubd80\ubd84\uc5d0\uc11c\ub3c4 \ubd80\uc871\ud568\uc774 \ub9ce\uc774 \ubcf4\uc600\ub358 \uac83 \uac19\ub2e4. \\n\ubd80\uc871\ud55c \ubd80\ubd84\uc744 \uc54c\uc558\uae30\uc5d0, \uc55e\uc73c\ub85c \ub354\uc6b1 \uc131\uc7a5\ud560 \uc218 \uc788\uc744 \uac83 \uac19\ub2e4. \\n\ub0b4\uac00 \ubd80\uc871\ud588\ub358 \ubd80\ubd84\uc744 \ud300\uc6d0\ub4e4\uc774 \uc798 \ubcf4\ucda9\ud574 \uc918\uc11c \ub4e0\ub4e0\ud588\ub2e4. \\n\\n### \uc544\uc26c\uc6b4 \uc810\\n\\n**\ubb38\uc11c\ud654**\\n\\n\uac1c\uc778\uc801\uc73c\ub85c\ub294 \uae30\uc220 \uc678\uc801\uc73c\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\uc744 \uc798 \uc815\ub9ac\ud558\uc9c0 \ubabb\ud588\ub2e4. \\n\ud504\ub85c\uc81d\ud2b8\ub97c \uc9c4\ud589\ud558\uba74\uc11c \ub0b4\uac00 \ud55c \ubd80\ubd84\uc744 \uc870\uae08 \ub354 \uaf3c\uaf3c\ud558\uac8c, \uc774\ud574\ud558\uae30 \uc27d\uac8c \ubb38\uc11c\ud654\ub97c \ud588\ub354\ub77c\uba74 \ud300\uc6d0\ub4e4\uc5d0\uac8c \ub354\uc6b1 \ub3c4\uc6c0\uc774 \ub418\uc5c8\uc744 \ud150\ub370 \uc774 \ubd80\ubd84\uc5d0 \uc2dc\uac04\uc744 \uc870\uae08 \ub354 \ud22c\uc790\ud558\uc9c0 \ubabb\ud588\ub358 \ubd80\ubd84\uc5d0\uc11c \uc544\uc26c\uc6c0\uc774 \ub9ce\uc774 \ub4e4\uc5c8\ub2e4. \\n\ubc29\ud559 \uae30\uac04 \ub3d9\uc548 \ubb38\uc11c\ud654\ub97c \ud558\uc9c0 \ubabb\ud588\ub358 \ubd80\ubd84\uc744 \uac1c\uc778 \ube14\ub85c\uadf8 \uc62c\ub9ac\uba74\uc11c \uc870\uae08 \ub354 \ucc44\uc6cc\ubcf4\ub824\uace0 \ud55c\ub2e4. \\n\\n**\ub0b4\uac00 \ubabb\ud558\ub294 \ubd80\ubd84\uc774\ub77c\uba74 \uc2dc\uac04\uc744 \ub4e4\uc774\uc790**\\n\\n\uc798 \ubabb\ud558\ub294 \ubd80\ubd84\uc774\ub77c\uba74 \uc2dc\uac04\uc744 \ub4e4\uc5ec\uc11c\ub77c\ub3c4 \uc911\uac04\uc740 \uac00\ub3c4\ub85d \ud574\uc57c\uaca0\ub2e4\ub294 \uc0dd\uac01\uc774 \ub9ce\uc774 \ub4e4\uc5c8\ub2e4. \\n\ub9d0\uc744 \ud558\uae30 \uc804\uc5d0 \uc815\ub9ac\ud574\uc11c \uc758\uacac\uc744 \ub0b4\ub294 \uac83, \ubc1c\ud45c \uc900\ube44, \uac10\uc815 \uc870\uc808 \ub4f1\ub4f1\\n\ubabb\ud558\ub294 \ubd80\ubd84\uc744 \uc778\uc9c0\ud558\uace0, \uac1c\uc120\ud558\uc790. \\n\\n**\ucef4\ud3ec\ud2b8 \uc874 \ubc97\uc5b4\ub098\uae30**\\n\\n\uc870\uae08 \ub354 \ub3c4\uc804\uc801\uc73c\ub85c \ubaa9\ud45c\ub97c \uc7a1\uc558\uc73c\uba74 \uc88b\uc558\uc744 \uac83 \uac19\ub2e4. \\n\ub9e4\ubc88 \uadfc\uac70\ub97c \uac00\uc9c0\uace0 \uae30\uc220\uc744 \ub3c4\uc785\ud558\uace0, \ucf54\ub4dc\ub97c \uc791\uc131\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. \\n\ud558\uc9c0\ub9cc \uc9c0\uc18d\uc801\uc73c\ub85c \uac1c\uc120\ud558\ub824\uace0 \ud558\ub294 \ubd80\ubd84\uc774 \ub2e4\uc18c \ubd80\uc871\ud588\ub2e4. \\n\\n### \uc88b\uc558\ub358 \uc810\\n\\n**\uc88b\uc558\ub358 \uc810\ub3c4 \ubb38\uc11c\ud654**\\n\\n[\ud300 \ube14\ub85c\uadf8](https://tripdraw.blog)\ub3c4 \uba3c\uc800 \ub3c4\uc785\ud558\uc790\uace0 \uc81c\uc548\ud558\uace0, \ub0b4\uac00 \ud588\ub358 \ubd80\ubd84\uc740 \ubb38\uc11c\ud654\ub97c \uaf64 \ub9ce\uc774 \ud574\uc11c \ud300\uc6d0\ub4e4\uacfc \uacf5\uc720\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\ubc31\uc5d4\ub4dc \ud06c\ub8e8 4\uba85\uc774\uc11c \uac19\uc774 \ud55c \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c\ub294 \uae30\ub2a5 \uad6c\ud604\ud55c\ub2e4\uace0 \ubb38\uc11c\ud654\uac00 \uc870\uae08 \ubbf8\ud761\ud574\uc11c \ubcf4\ucda9\uc744 \ud574\uc57c\uaca0\ub2e4. \\n\\n**\ub0b4\uac00 \ub514\uc790\uc778\ud55c \ud2b8\ub9bd\ub4dc\ub85c\uc6b0 \ub85c\uace0**\\n\\n\\n\\n\ud2b8\ub9bd\ub4dc\ub85c\uc6b0 \ub85c\uace0\ub97c \ub9cc\ub4e4\uc5c8\ub2e4. \\n\ud300\uc6d0\ub4e4\uc774 \ub300\ud45c \uc0c9\uc0c1(\ud30c\ub780\uc0c9)\uc744 \uc815\ud574\uc92c\uace0, \uc8fc\ub9d0 \ub3d9\uc548 \uc2e0\ub098\uac8c \ub85c\uace0 \ub514\uc790\uc778\uc744 \ud588\ub358 \uac83 \uac19\ub2e4. \\n\uc544\ub798\uc758 D \ubd80\ubd84\uc740 \uc720\ud29c\ube0c \uac15\uc758 \ub4e4\uc73c\uba74\uc11c \uc9c1\uc811 \ub9cc\ub4e4\uc5b4\uc11c \ubfcc\ub4ef\ud558\ub2e4. \\n\\n**\uae30\uc220 \uc120\ud0dd\uc758 \uc774\uc720**\\n\\n\uae30\uc220\uc758 \ud559\uc2b5 \ube44\uc6a9, \ud604\uc7ac \uad6c\uc870\uc5d0 \uc801\ud569\ud55c\uc9c0, \uc2e4\uc81c \uac00\uc9c0\uace0 \uc788\ub294 \ub9ac\uc18c\uc2a4\ub97c \uace0\ub824\ud574\uc11c \uae30\uc220 \uc120\ud0dd\uc744 \ud558\uace0, \ub3c4\uc785\ud588\ub358 \ubd80\ubd84\uc774 \uc88b\uc558\ub2e4. \\n100% \uc88b\uc740 \uc120\ud0dd\uc77c \uc21c \uc5c6\uc9c0\ub9cc, \uadf8\ub798\ub3c4 \uc120\ud0dd\uc5d0 \ub300\ud55c \uadfc\uac70\uac00 \uc874\uc7ac\ud55c\ub2e4\uba74 \ud655\ub960\uc744 \ub192\ud600\uc8fc\ub294 \uac83 \uac19\ub2e4. \\n\\n### \ub9c8\uce58\uba70\\n\\n\ud50c\ub808\uc774\uc2a4\ud1a0\uc5b4\uc5d0 \uc571\uc774 \uc62c\ub77c\uac00 \uc788\ub294 \uac70 \ub108\ubb34 \uc2e0\uae30\ud558\ub2e4. \\n\uc548\ub4dc\ub85c\uc774\ub4dc \ube0c\ub808\uba58 \uc74c\uc545\ub300(\uba67\ub3fc\uc9c0, \uc218\ub2ec, \ud551\uad6c), \uadf8\ub9ac\uace0 \ubc31\uc5d4\ub4dc \ud300\uc6d0\ub4e4(\uccb4\uc778\uc800, \ud6c4\ucd94, \ub9ac\uc624) \ub108\ubb34 \uace0\uc0dd\uc774 \ub9ce\uc558\ub2e4."},{"id":"cloudwatch","metadata":{"permalink":"/cloudwatch","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131.md","source":"@site/blog/2023-3/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131.md","title":"CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131","description":"CloudWatch","date":"2023-08-17T00:00:00.000Z","formattedDate":"2023\ub144 8\uc6d4 17\uc77c","tags":[{"label":"cloudwatch","permalink":"/tags/cloudwatch"},{"label":"log","permalink":"/tags/log"},{"label":"monitoring","permalink":"/tags/monitoring"}],"readingTime":5.35,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131","slug":"cloudwatch","tags":["cloudwatch","log","monitoring"]},"prevItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 3 \ud68c\uace0","permalink":"/woowacourse-level3-retrospective"},"nextItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac","permalink":"/route-image-async-with-event"}},"content":"## CloudWatch\\n\\nAWS \ub9ac\uc18c\uc2a4\uc640 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc758 \uc9c0\ud45c\uc640 \ub85c\uadf8\uc5d0 \ub300\ud55c \ubaa8\ub2c8\ud130\ub9c1\uc744 \uc81c\uacf5\ud558\ub294 \uc11c\ube44\uc2a4\ub2e4. \\n\uc9c0\ud45c\ub97c \uac10\uc2dc\ud558\uc5ec \uc54c\ub9bc\uc744 \ubcf4\ub0b4\ub294 \uae30\ub2a5\ub3c4 \uc81c\uacf5\ud55c\ub2e4. \\n\ud504\ub9ac\ud2f0\uc5b4\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 \uacbd\uc6b0 \ub300\uc2dc\ubcf4\ub4dc\ub2f9 3$/M \uc758 \ube44\uc6a9\uc774 \uccad\uad6c\ub418\uace0, \uc9c0\ud45c\ub098 \ub85c\uadf8\uc758 \uc591\uc5d0 \ub530\ub77c \ube44\uc6a9\uc774 \ucd94\uac00\uc801\uc73c\ub85c \uccad\uad6c\ub41c\ub2e4. \\n\uc694\uae08 \uc815\ubcf4\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \uc815\ubcf4\ub294 [\ub2e4\uc74c \ub9c1\ud06c](https://aws.amazon.com/ko/cloudwatch/pricing/)\uc5d0\uc11c \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n## CloudWatch Metrics\\n\\n\uae30\ubcf8\uc801\uc73c\ub85c 5\ubd84\ub9c8\ub2e4 \uc9c0\ud45c\uc5d0 \ub300\ud55c \uc815\ubcf4\uac00 \uc218\uc9d1\ub41c\ub2e4. \\n\uc138\ubd80 \ubaa8\ub2c8\ud130\ub9c1(Detailed Monitoring)\uc744 \ud65c\uc131\ud654\ud558\uba74 1\ubd84\ub9c8\ub2e4 \uc9c0\ud45c\ub97c \uc218\uc9d1\ud55c\ub2e4. \\n\ub300\uc2dc\ubcf4\ub4dc\uc5d0\uc11c InstanceId\ub85c \uac80\uc0c9\ud558\uc5ec \uc218\uc9d1\ub41c \uc9c0\ud45c\ub97c \ud655\uc778\ud560 \uc218 \uc788\ub2e4.\\n\\n![./cloudwatch1.png](./cloudwatch1.png)\\n\\nCPUUtilization, NetworkIn, NetworkOut\uacfc \uac19\uc740 \uae30\ubcf8\uc801\uc778 \uc9c0\ud45c\ub97c \uc81c\uacf5\ud558\uace0, \uba54\ubaa8\ub9ac, \ub514\uc2a4\ud06c \uacf5\uac04\uacfc \uac19\uc740 \uc9c0\ud45c\ub97c \ud655\uc778\ud558\ub824\uba74 \uc0ac\uc6a9\uc790 \uc9c0\uc815 \uc9c0\ud45c\ub97c \uc124\uc815\ud574\uc57c \ud55c\ub2e4.\\n\\n## CloudWatch Agent \uc124\uce58\\n\\nCloudWatch Agent \uc0ac\uc6a9\uc790 \uc9c0\uc815 \uc9c0\ud45c\uc640 \ub85c\uadf8\ub97c \uc218\uc9d1\ud560 \uc218 \uc788\ub2e4. \\n\\n### IAM \uc5ed\ud560 \uc124\uc815\\n\\n\uae30\ubcf8\uc801\uc73c\ub85c EC2 \uc778\uc2a4\ud134\uc2a4\uac00 CloudWatchAgentServerPolicy\uc5d0 \ub300\ud55c \uad8c\ud55c\uc774 \uc788\uc5b4\uc57c \ud55c\ub2e4. \\nIAM \u2192 \uc5ed\ud560\uc5d0\uc11c \uc5ed\ud560 \uc0dd\uc131\uc744 \ud074\ub9ad\ud55c\ub2e4.\\n\\n![./cloudwatch2.png](./cloudwatch2.png)\\n\\nCloudWatchAgentServerPolicy \uad8c\ud55c \uc815\ucc45\uc744 \uc120\ud0dd\ud558\uace0, \uc801\ub2f9\ud55c \uc5ed\ud560 \uc774\ub984\uc744 \uc785\ub825\ud574\uc11c \uc5ed\ud560\uc744 \uc0dd\uc131\ud55c\ub2e4.\\n\\n![./cloudwatch3.png](./cloudwatch3.png)\\n\\nEC2 \uc778\uc2a4\ud134\uc2a4 \ubaa9\ub85d\uc73c\ub85c \ub4e4\uc5b4\uac00\uc11c, CloudWatch Agent\ub97c \uc124\uce58\ud560 EC2 \uc778\uc2a4\ud134\uc2a4\ub97c \ud074\ub9ad\ud55c\ub2e4. \\n\uc791\uc5c5 \u2192 \ubcf4\uc548 \u2192 IAM \uc5ed\ud560 \uc218\uc815\uc5d0\uc11c \uc774\uc804\uc5d0 \uc0dd\uc131\ud55c \uc5ed\ud560\uc744 \uc9c0\uc815\ud55c\ub2e4.\\n\\n![./cloudwatch4.png](./cloudwatch4.png)\\n\\n### \uc124\uce58\\n\\n\ud658\uacbd\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\\nOS: ubuntu 22.04 \\n\uc778\uc2a4\ud134\uc2a4 \uc720\ud615: t4g.small (ARM64) \\n\\n\uc544\ub798 \uba85\ub839\uc5b4\ub97c \uc785\ub825\ud558\uc5ec \uc124\uce58\ud55c\ub2e4.\\n\\n```bash\\nwget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/arm64/latest/amazon-cloudwatch-agent.deb\\nsudo dpkg -i -E ./amazon-cloudwatch-agent.deb\\n```\\n\\n[\uc0ac\uc6a9 \uc124\uba85\uc11c](https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-commandline-fleet.html)\uc5d0 \uac01 \uc778\uc2a4\ud134\uc2a4 \uc720\ud615\ub9c8\ub2e4 \ub2e4\uc6b4\ub85c\ub4dc \ub9c1\ud06c\uac00 \uc790\uc138\ud558\uac8c \uc548\ub0b4\ub418\uc5b4 \uc788\ub2e4.\\n\\n### Wizard\\n\\nCloudWatch Wizard\ub97c \uc0ac\uc6a9\ud558\uba74 \uac04\ub2e8\ud558\uac8c \uc124\uc815 \ud30c\uc77c \uc0dd\uc131\ud560 \uc218 \uc788\ub2e4. \\n\ub85c\uadf8\ub97c \uc218\uc9d1\ud558\ub3c4\ub85d \uc124\uc815\ud558\ub294 \uacbd\uc6b0 Wizard \uc2e4\ud589 \uba85\ub839\uc5b4 \uc785\ub825 \uc804 log \ud30c\uc77c\uc758 \uc808\ub300 \uacbd\ub85c\ub97c \ubcf5\uc0ac\ud574\ub450\ub294 \uac83\uc774 \uc88b\ub2e4. \\n\uc544\ub798\uc758 \uba85\ub839\uc5b4\ub97c \uc785\ub825\ud558\uc5ec Wizard\ub97c \uc2e4\ud589\ud560 \uc218 \uc788\ub2e4. \\n\\n```bash\\nsudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard\\n```\\n\\n\uc124\uc815\uc744 \uc9c4\ud589\ud558\ub2e4 \ubcf4\uba74 \uc124\uc815 \ud30c\uc77c\uc774 \uc5b4\ub5bb\uac8c \uad6c\uc131\ub420\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\ub85c\uadf8\ub97c \ucd94\uac00\ud560 \uac83\uc774\ub0d0\uace0 \ubb3c\uc5b4\ubcf4\ub294 \uc785\ub825\ucc3d\uc774 \ub098\uc624\uba74 \uc900\ube44\ud574\ub480\ub358 \ub85c\uadf8 \ud30c\uc77c\uc758 \uc808\ub300 \uacbd\ub85c\ub97c \uc785\ub825\ud55c\ub2e4. \\n\\n![./cloudwatch5.png](./cloudwatch5.png)\\n\\n\uc911\uac04\uc5d0 SSM parameter store\uc5d0 \uc124\uc815 \ud30c\uc77c\uc744 \uc800\uc7a5\ud560 \uac83\uc774\ub0d0\uace0 \ubb3c\uc5b4\ubcf4\ub294 \ucc3d\uc774 \ub098\uc628\ub2e4. \\n\\n```bash\\nDo you want to store the config in the SSM parameter store?\\n1. yes\\n2. no\\n```\\n\\n\ucd94\uac00\uc801\uc73c\ub85c \uc124\uc815\ud558\uc9c0 \uc54a\ub294 \uacbd\uc6b0 2\ubc88\uc744 \uc120\ud0dd\ud55c\ub2e4. \\nParameter Store \uad00\ub9ac\uc5d0 \ub300\ud55c \ub0b4\uc6a9\uc740 \ub2e4\uc74c\uc758 [\ubb38\uc11c](https://dev.classmethod.jp/articles/manage-the-cloudwatch-agent-from-the-parameter-store/)\ub97c \ucc38\uace0\ud558\uba74 \uc88b\uc744 \uac70 \uac19\ub2e4. \\n\uc124\uc815\uc774 \uc644\ub8cc\ub418\uba74 `/opt/aws/amazon-cloudwatch-agent/bin/config.json` \uc5d0 \uc124\uc815\uc5d0 \ub300\ud55c \ub0b4\uc6a9\uc774 \uc800\uc7a5\ub41c\ub2e4. \\n\\n### \uc124\uc815 \ud30c\uc77c \uc801\uc6a9\\n\\n\uc544\ub798\uc758 \uba85\ub839\uc5b4\ub97c \uc785\ub825\ud558\uc5ec \uc124\uc815\ud30c\uc77c\uc744 \uc801\uc6a9\ud560 \uc218 \uc788\ub2e4. \\nfile \ub4a4\uc5d0\ub294 \uc124\uc815 \ud30c\uc77c\uc5d0 \ub300\ud55c \uc808\ub300\uacbd\ub85c(\uc544\ub798 \uba85\ub839\uc5b4 \uae30\uc900 \uae30\ubcf8 \uc0dd\uc131 \uc704\uce58)\ub97c \uc785\ub825\ud558\uba74 \ub41c\ub2e4. \\n\\n```bash\\nsudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json\\n```\\n\\n### types.db: no such file or directory \uc5d0\ub7ec\\n\\n\ub2e4\uc74c\uacfc \uac19\uc740 \uc5d0\ub7ec\uac00 \ubc1c\uc0dd\ud55c\ub2e4\uba74 types.db \ud30c\uc77c\uc744 \uc0dd\uc131\ud574\uc11c \ubb38\uc81c\ub97c \ud574\uacb0\ud560 \uc218 \uc788\ub2e4.\\n\\n```bash\\nError running agent: Error loading config file /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.toml: error parsing socket_listener, open /usr/share/collectd/types.db: no such file or directory\\n```\\n\\ntypes.db \ud30c\uc77c \uc0dd\uc131\\n\\n```bash\\nsudo mkdir /usr/share/collectd\\nsudo touch /usr/share/collectd/types.db\\n```\\n\\n### \uc9c0\ud45c \ud655\uc778\\n\\nCloudWatch Metrics\uc5d0 \uac00\ubcf4\uba74 CWAgent\ub77c\ub294 \ub124\uc784\uc2a4\ud398\uc774\uc2a4\uac00 \ucd94\uac00\ub41c \uac83\uc744 \ubcfc \uc218 \uc788\ub2e4. \\n\\n![./cloudwatch6.png](./cloudwatch6.png)\\n\\n\ub2e4\uc74c\uacfc \uac19\uc774 \uc124\uc815 \ud30c\uc77c\uc5d0 \ub124\uc784\uc2a4\ud398\uc774\uc2a4\ub97c \ucd94\uac00\ud558\uc5ec \uc9c0\ud45c\uc5d0 \ub300\ud55c \ub124\uc784\uc2a4\ud398\uc774\uc2a4\ub97c \ubcc0\uacbd\ud560 \uc218 \uc788\ub2e4. \\n\\n```json\\n{\\n \\"metrics\\": {\\n \\"namespace\\": \\"2023-hello-world\\",\\n ......\\n },\\n} \\n```\\n\\n### \ub85c\uadf8\\n\\nCloudWatch \u2192 \ub85c\uadf8 \uadf8\ub8f9\uc73c\ub85c \uac00\uba74 Wizard\ub85c \ucd94\uac00\ud55c \ub85c\uadf8\ub97c \ud655\uc778\ud560 \uc218 \uc788\ub2e4.\\n\\n![./cloudwatch7.png](./cloudwatch7.png)\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n[CloudWatch\ub780 \ubb34\uc5c7\uc785\ub2c8\uae4c?](https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html) \\n[Amazon CloudWatch \uc694\uae08](https://aws.amazon.com/ko/cloudwatch/pricing/) \\n[Linux \uc778\uc2a4\ud134\uc2a4 \uc9c0\ud45c](https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) \\n[\uc11c\ubc84\uc5d0 CloudWatch \uc5d0\uc774\uc804\ud2b8 \uc124\uce58 \ubc0f \uc2e4\ud589](https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-commandline-fleet.html) \\n[CloudWatch Agent\ub97c Parameter Store\uc5d0\uc11c \uad00\ub9ac\ud574 \ubcf4\uae30](https://dev.classmethod.jp/articles/manage-the-cloudwatch-agent-from-the-parameter-store/) \\n[CloudWatch\uc5d0\uc774\uc804\ud2b8 \uad6c\uc131 \ud30c\uc77c](https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/monitoring/CloudWatch-Agent-Configuration-File-Details.html)"},{"id":"route-image-async-with-event","metadata":{"permalink":"/route-image-async-with-event","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac.mdx","source":"@site/blog/2023-3/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac.mdx","title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac","description":"\uc774\uc804 \uae00","date":"2023-08-13T00:00:00.000Z","formattedDate":"2023\ub144 8\uc6d4 13\uc77c","tags":[{"label":"async","permalink":"/tags/async"},{"label":"event","permalink":"/tags/event"}],"readingTime":11.2,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac","slug":"route-image-async-with-event","tags":["async","event"]},"prevItem":{"title":"CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131","permalink":"/cloudwatch"},"nextItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604","permalink":"/route-image-implementation"}},"content":"## \uc774\uc804 \uae00\\n\\n[\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd](./route-image-intro) \\n[\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604](./route-image-implementation)\\n\\n## \uac1c\uc694\\n\\n\ud604\uc7ac \uc5ec\ud589\uc744 \ub9c8\uce58\ub294 \uacbd\uc6b0, \uac10\uc0c1\uc744 \uc0dd\uc131\ud558\ub294 \uacbd\uc6b0 \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc694\uccad\uc774 \uc774\ub8e8\uc5b4\uc9c4\ub2e4. \\n\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc758 \uacbd\uc6b0 \uc704\uce58 \uc815\ubcf4\uc758 \uac1c\uc218\uc5d0 \uc815\ube44\ub840\ud558\uc5ec \uc0dd\uc131 \uc2dc\uac04\uc774 \uc99d\uac00\ud55c\ub2e4. \\n\ub530\ub77c\uc11c \ube44\ub3d9\uae30\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc694\uccad\uc744 \ucc98\ub9ac\ud558\uc5ec \uc0ac\uc6a9\uc790\uc758 \uacbd\ud5d8\uc744 \uac1c\uc120\uc2dc\ud0ac \uc218 \uc788\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\\n### \uc8fc\uae30\ub2a5\uc758 \uc751\ub2f5\uc18d\ub3c4 \uac1c\uc120\\n\\n\uc5ec\ud589 \uc885\ub8cc\uc640 \uac10\uc0c1 \uc0dd\uc131\uc774 \uc8fc\uae30\ub2a5\uc774\uace0, \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uae30\ub2a5\uc740 \ubd80\uae30\ub2a5\uc774\ub2e4. \\n\ud558\uc9c0\ub9cc \ud604\uc7ac \uc5ec\ud589 \uc885\ub8cc\uc640 \uac10\uc0c1 \uc0dd\uc131\uc758 \uc751\ub2f5 \uc18d\ub3c4\uac00 \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc2dc\uac04\uc5d0 \uc601\ud5a5\uc744 \ubc1b\uace0 \uc788\ub2e4. \\n\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc740 \ube44\ub3d9\uae30 \ucc98\ub9ac\ud558\uc5ec\ub3c4 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc0ac\uc6a9\uc5d0 \ubb38\uc81c\uac00 \ub418\uc9c0 \uc54a\ub294\ub2e4. \\n\uc18c\uc694 \uc2dc\uac04\uc774 1\ucd08 \uc774\uc0c1 \uac78\ub9ac\ub294 \uacbd\uc6b0\uac00 \uc874\uc7ac\ud558\uae30\uc5d0 \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc744 \ube44\ub3d9\uae30 \ucc98\ub9ac\ud558\uace0 \uc5ec\ud589 \uc885\ub8cc\uc640 \uac10\uc0c1 \uc0dd\uc131 \uae30\ub2a5\uc758 \uc751\ub2f5 \uc2dc\uac04\uc744 \uac1c\uc120\ud558\ub294 \uac83\uc774 \ub354 \uc911\uc694\ud558\ub2e4. \\n\\n### \ud655\uc7a5\uc131 \ub300\ube44\\n\\n\ud604\uc7ac 10\ubd84 \uac04\uaca9\uc73c\ub85c \uc704\uce58 \uc815\ubcf4\ub97c \uc11c\ubc84\uc5d0 \uc800\uc7a5\ud558\uace0 \uc788\ub2e4. \\n\uc870\uae08 \ub354 \uc9e7\uc740 \uac04\uaca9\uc73c\ub85c \uc704\uce58 \uc815\ubcf4\ub97c \uadf8\ub9ac\ub294 \uacbd\uc6b0 \ud558\ub098\uc758 \uc5ec\ud589\uc5d0 \ub9ce\uc740 \uc704\uce58 \uc815\ubcf4\uac00 \uc800\uc7a5\ub420 \uc218\ubc16\uc5d0 \uc5c6\uace0 \ub530\ub77c\uc11c \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc5d0 \uac78\ub9ac\ub294 \uc2dc\uac04\uc774 \ub354 \uae38\uc5b4\uc9c8 \uc218 \uc788\ub2e4. \\n\ub530\ub77c\uc11c \ucd94\ud6c4\uc5d0 \ub354 \uc9e7\uc740 \uac04\uaca9\uc73c\ub85c \uc704\uce58 \uc815\ubcf4\ub97c \uc800\uc7a5\ud558\ub294 \uacbd\uc6b0\ub97c \ub300\ube44\ud558\uc5ec \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc740 \ube44\ub3d9\uae30\ub85c \ucc98\ub9ac\ud558\ub294 \uac83\uc774 \ud569\ub2f9\ud558\ub2e4. \\n\\n## \ube44\ub3d9\uae30 \ucc98\ub9ac\\n\\n@Async\ub97c \uc0ac\uc6a9\ud558\uba74 \uac04\ub2e8\ud558\uac8c \uba54\uc11c\ub4dc\ub97c \ube44\ub3d9\uae30\ub85c \ub3d9\uc791\ud558\ub3c4\ub85d \ub9cc\ub4e4 \uc218 \uc788\ub2e4. \\n\\n### \ube44\ub3d9\uae30 \uc124\uc815\\n\\n\uc0ac\uc6a9\ud558\uae30 \uc804\uc5d0 \uc124\uc815 \ud30c\uc77c\uc744 \ud558\ub098 \ub9cc\ub4e4\uc5b4\uc11c EnableAsync \uc124\uc815\uc744 \ud574\uc57c\ud55c\ub2e4. \\n\ud574\ub2f9 \uc124\uc815\uc744 \uc801\uc6a9\ud558\uba74 \ube44\ub3d9\uae30\uc801\uc73c\ub85c \uc2e4\ud589\ud558\ub824\ub294 \uba54\uc11c\ub4dc\uc5d0 @Async \uc560\ub108\ud14c\uc774\uc158\uc744 \ubd99\uc5ec\uc8fc\uae30\ub9cc \ud558\uba74 \ube44\ub3d9\uae30\ub85c \ub3d9\uc791\ud55c\ub2e4. \\n\\n```java title=\\"AsyncConfig\\"\\n@EnableAsync\\n@Configuration\\npublic class AsyncConfig {\\n}\\n```\\n\\n\uc2a4\ud504\ub9c1 \ubd80\ud2b8\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 \uacbd\uc6b0 \uae30\ubcf8\uc801\uc73c\ub85c \ube44\ub3d9\uae30 \ucc98\ub9ac\ub97c \ud560 \ub54c \ub9e4\ubc88 \uc0c8\ub85c\uc6b4 \uc2a4\ub808\ub4dc\ub97c \uc0dd\uc131\ud558\uae30 \ub54c\ubb38\uc5d0 \uc2a4\ub808\ub4dc \ud480 \uc124\uc815\uc744 \ub530\ub85c \ud574\uc918\uc57c \ud55c\ub2e4. \ud558\uc9c0\ub9cc \uc2a4\ud504\ub9c1 \ubd80\ud2b8\ub97c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 ThreadPoolTaskExecutor\ub97c \ub530\ub85c \uc124\uc815\ud558\uc9c0 \uc54a\uc544\ub3c4 \uae30\ubcf8\uc801\uc73c\ub85c \uc2a4\ud504\ub9c1 \ubd80\ud2b8\uac00 \uc0dd\uc131\uc744 \ub3c4\uc640\uc900\ub2e4. \\n\\n> In the absence of an Executor bean in the context, Spring Boot auto-configures a ThreadPoolTaskExecutor with sensible defaults that can be automatically associated to asynchronous task execution (@EnableAsync) and Spring MVC asynchronous request processing.\\n> 7.7. Task Execution and Scheduling, Spring Boot Docs\\n\\n### @Async \uc801\uc6a9\\n\\n\uc774\ubbf8\uc9c0 \uc0dd\uc131\uae30\uc5d0 Async \uc560\ub108\ud14c\uc774\uc158\uc744 \ubd99\uc5ec \ube44\ub3d9\uae30\ub85c \ub3d9\uc791\ud558\ub3c4\ub85d \ud55c\ub2e4. \\n\\n```java title=\\"RouteImageGenerator\\"\\n@Async\\npublic void generate(\\n List latitudes,\\n List longitudes,\\n List pointedLatitudes,\\n List pointedLongitudes,\\n Long tripId\\n) {\\n // \uc774\ubbf8\uc9c0 \uc0dd\uc131\\n RouteImageDrawer routeImageDrawer = RouteImageDrawer.from(IMAGE_SIZE);\\n Coordinates coordinates = Coordinates.of(latitudes, longitudes);\\n Coordinates pointedCoordinates = Coordinates.of(pointedLatitudes, pointedLongitudes);\\n drawImage(coordinates, routeImageDrawer, pointedCoordinates);\\n\\n // \uc774\ubbf8\uc9c0 \uc800\uc7a5\\n String imageName = routeImageUploader.upload(routeImageDrawer.bufferedImage());\\n\\n // \uc790\uc6d0 \ud560\ub2f9 \ud574\uc81c\\n routeImageDrawer.dispose();\\n\\n // \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uac12 \ubcc0\uacbd\\n Trip trip = tripRepository.findById(tripId)\\n .orElseThrow();\\n trip.changeRouteImageUrl(imageUrl);\\n tripRepository.save(trip);\\n}\\n```\\n\\n### \ube44\ub3d9\uae30 \ucc98\ub9ac\uc2dc \ubb38\uc81c\uc810\\n\\n\ud604\uc7ac \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc744 \ud558\uace0 \uc800\uc7a5 \ud6c4, \uc800\uc7a5 \uacbd\ub85c\ub97c DB\uc5d0 \ubc18\uc601\ud574\uc57c \ud55c\ub2e4. \\n\ub530\ub77c\uc11c \ud328\ud0a4\uc9c0 \uac04 \uc21c\ud658 \ucc38\uc870 \ud615\ud0dc\uac00 \ub418\uba70 \uc758\uc874\uc131 \ubc29\ud5a5\uc774 \ubb38\uc81c\uac00 \uc0dd\uae34\ub2e4. \\n\\n```mermaid\\ngraph LR\\n trip[trip: \uc5ec\ud589 \uad00\ub828 \ud328\ud0a4\uc9c0] --\x3e draw[draw: \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uae30\ub2a5\uc744 \ud3ec\ud568\ud558\uace0 \uc788\ub294 \ud328\ud0a4\uc9c0]\\n draw --\x3e trip\\n```\\n\\n\uc774\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud574\uc11c\ub294 \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uacfc \uc774\ubca4\ud2b8\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc774 \uc788\ub2e4. \\n\uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud55c\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uad6c\uc870\uac00 \ub41c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n\\tsubgraph draw\\n\\t\\tdirection LR\\n\\t\\tRG[RouteImageGenerator] -- DB \ubc18\uc601 \uc694\uccad --\x3e ILR[ImageLinkTripRepository]\\n\\tend\\n subgraph trip\\n\\t\\tdirection LR\\n\\t\\tTS[TripService] -- \uc774\ubbf8\uc9c0 \uc0dd\uc131 --\x3e RG\\n\\t\\tILRI[ImageLinkTripRepositoryImpl] -- \uad6c\ud604 --\x3e ILR\\n\\tend\\n\\n\\ttrip --\x3e draw\\n```\\n\\n\ud328\ud0a4\uc9c0 \uac04 \uc758\uc874\uc131\uc740 \ud574\uacb0\ub418\uc5c8\uc9c0\ub9cc, \uc774\ubbf8\uc9c0 \uacbd\ub85c \uc800\uc7a5\uc744 \uc704\ud574 tripId\ub97c \ubc1b\uc544\uc57c\ud558\ub294 \ub4f1\uc758 \ub17c\ub9ac\uc801\uc778 \uc758\uc874\uc131\uc740 \uc544\uc9c1 \ud574\uacb0\ub418\uc9c0 \uc54a\uc558\ub2e4. \\n\ub530\ub77c\uc11c \uc774\ubca4\ud2b8\ub97c \uc0ac\uc6a9\ud558\uae30\ub85c \ud588\ub2e4. \\n\\n## \uc774\ubca4\ud2b8 \uc0ac\uc6a9\\n\\n\uc2a4\ud504\ub9c1\uc758 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc774\ubca4\ud2b8\ub97c \uc0ac\uc6a9\ud558\uba74 \ube44\uc988\ub2c8\uc2a4 \ub85c\uc9c1\uc758 \ube44\uad00\uc2ec\uc0ac(ex. \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131)\uc744 \ud6a8\uc728\uc801\uc778 \ubc29\ubc95\uc73c\ub85c \ucc98\ub9ac\ud560 \uc218 \uc788\ub2e4.\\n\\n### \uc774\ubca4\ud2b8 \ubc1c\ud589\\n\\n\uc774\ubca4\ud2b8\ub97c \uc0ac\uc6a9\ud558\ub824\uba74 \uba3c\uc800 \uc774\ubca4\ud2b8\ub97c \ubc1c\ud589\ud574\uc57c \ud55c\ub2e4. \\n\uc2a4\ud504\ub9c1\uc5d0\uc11c\ub294 ApplicationEventPublisher \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc774\ubca4\ud2b8\ub97c \ubc1c\ud589\ud560 \uc218 \uc788\ub2e4. \\n\ud574\ub2f9 \uc778\ud130\ud398\uc774\uc2a4\ub294 \ub0b4\ubd80\uc801\uc73c\ub85c ApplicationContext\uac00 \uad6c\ud604\ud558\uc5ec \uc774\ubca4\ud2b8\ub97c \ubc1c\ud589\ud55c\ub2e4. \\n\\n```java title=\\"TripService & TripUpdateEvent\\"\\npublic void updateTripById(LoginUser loginUser, Long tripId, TripUpdateRequest tripUpdateRequest) {\\n ...\\n\\n // \uc774\ubca4\ud2b8 \ubc1c\ud589\\n applicationEventPublisher.publishEvent(new TripUpdateEvent(trip.id()));\\n}\\n\\npublic record TripUpdateEvent(Long tripId) {\\n}\\n```\\n\\n\uc774\ubca4\ud2b8\ub97c \ubc1c\ud589\ud560 \ub54c \ubc1c\ud589\ud558\ub294 \uc774\ubca4\ud2b8\uba85\uc774 \uc911\uc694\ud558\ub2e4. \\n\uc774\ubca4\ud2b8\ub97c \uad6c\ub3c5\ud558\ub294 \ub3c4\uba54\uc778\uc758 \ud589\uc704\ub97c \ub2f4\uace0 \uc788\ub294 \uc774\ubca4\ud2b8\ub97c \ubc1c\ud589(ex. RouteImageGenerateEvent)\ud55c\ub2e4\uba74 \ub17c\ub9ac\uc801\uc778 \uc758\uc874 \uad00\uacc4\uac00 \ub0a8\uc544\uc788\uae30\uc5d0 \uc774\ubca4\ud2b8\ub97c \uc801\uc808\ud788 \uc0ac\uc6a9\ud588\ub2e4\uace0 \ubcf4\uae30 \uc5b4\ub835\ub2e4. \\n\ubc1c\ud589\ud558\ub294 \uc774\ubca4\ud2b8\uba85\uc740 \uc8fc\uae30\ub2a5\uc774 \uc5b4\ub5a4 \ud589\uc704(ex. TripUpdateEvent)\ub97c \ud588\ub294\uc9c0\uc5d0 \ub300\ud55c \uc815\ubcf4\uac00 \ub2f4\uaca8\uc788\ub294 \uc774\ubca4\ud2b8\uba85\uc73c\ub85c \ubc1c\ud589\ud558\ub294 \uac83\uc774 \uc911\uc694\ud558\ub2e4. \\n\\n### \uc774\ubca4\ud2b8 \uad6c\ub3c5\\n\\n\uc774\ubca4\ud2b8\ub97c \uad6c\ub3c5\ud558\uc5ec \uc2e4\ud589\ud558\ub294 \uba54\uc11c\ub4dc\ub294 \ube44\ub3d9\uae30\ub85c \ucc98\ub9ac\ud558\uae30 \uc704\ud558\uc5ec `@Async` \uc560\ub108\ud14c\uc774\uc158\uc744 \uc801\uc6a9\ud588\ub2e4. \\n\uc774\ubca4\ud2b8\uc758 \uad6c\ub3c5\uc740 \uc5ec\ud589\uc774 \uc815\uc0c1\uc801\uc73c\ub85c \uc885\ub8cc\ub420 \ub54c \uc5ec\ud589\uc5d0 \ub300\ud55c \uc815\ubcf4\ub97c \uac00\uc9c0\uace0 \uacbd\ub85c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\uae30 \uc704\ud574 `@TransactionalEventListener`\ub97c \uc0ac\uc6a9\ud588\ub2e4. \\n\\n:::note TransactionPhase \uc124\uc815\\nTransactionPhase\uc744 \uc0ac\uc6a9\ud558\uc5ec \ud2b8\ub79c\uc7ad\uc158 \uc774\ubca4\ud2b8\ub97c \uc5b4\ub5a4 \ub2e8\uacc4\uc5d0\uc11c \uc218\uc2e0\ud558\uace0 \ucc98\ub9ac\ud560\uc9c0\ub97c \uc9c0\uc815\ud560 \uc218 \uc788\ub2e4.\\n\\nAFTER_COMMIT(\uae30\ubcf8\uac12): \ud2b8\ub79c\uc7ad\uc158\uc774 \uc815\uc0c1\uc801\uc73c\ub85c \ucee4\ubc0b \ub418\ub294 \uacbd\uc6b0 \uc774\ubca4\ud2b8 \uc2e4\ud589 \\nAFTER_ROLLBACK: \ud2b8\ub79c\uc7ad\uc158\uc774 \ub864\ubc31\ub418\ub294 \uacbd\uc6b0 \uc774\ubca4\ud2b8 \uc2e4\ud589 \\nAFTER_COMPLETION: \ud2b8\ub79c\uc7ad\uc158\uc774 \ucee4\ubc0b \ub610\ub294 \ub864\ubc31 \ub418\uc5c8\uc744 \uacbd\uc6b0 \uc774\ubca4\ud2b8 \uc2e4\ud589 \\nBEFORE_COMMIT: \ud2b8\ub79c\uc7ad\uc158\uc774 \ucee4\ubc0b \ub418\uae30 \uc804 \uc774\ubca4\ud2b8 \uc2e4\ud589 \\n:::\\n\\n\uc774\ubbf8\uc9c0 \uc0dd\uc131\uc758 \uacbd\uc6b0 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \uc81c\uc678\ud558\uae30 \uc704\ud574 @Transactional \uc560\ub108\ud14c\uc774\uc158\uc744 \uc0ac\uc6a9\ud558\uc9c0 \uc54a\uc558\ub2e4. \\n\\n```java title=\\"TripUpdateEventHandler\\"\\n@Component\\npublic class TripUpdateEventHandler {\\n\\n private final RouteImageGenerator routeImageGenerator;\\n private final TripRepository tripRepository;\\n\\n public TripUpdateEventHandler(RouteImageGenerator routeImageGenerator, TripRepository tripRepository) {\\n this.routeImageGenerator = routeImageGenerator;\\n this.tripRepository = tripRepository;\\n }\\n\\n @Async\\n @TransactionalEventListener(phase = AFTER_COMMIT)\\n public void handle(TripUpdateEvent tripUpdateEvent) {\\n Trip trip = tripRepository.getTripWithPoints(tripUpdateEvent.tripId());\\n\\n String imageUrl = routeImageGenerator.generate(\\n trip.getLatitudes(),\\n trip.getLongitudes(),\\n trip.getPointedLatitudes(),\\n trip.getPointedLongitudes()\\n );\\n\\n trip.changeRouteImageUrl(imageUrl);\\n tripRepository.save(trip);\\n }\\n}\\n```\\n\\n\uc774\ubca4\ud2b8\ub97c \uc0ac\uc6a9\ud568\uc73c\ub85c\uc368 \ud328\ud0a4\uc9c0 \uac04 \uc21c\ud658 \ucc38\uc870 \ubb38\uc81c\uac00 \ub2e4\uc74c\uacfc \uac19\uc774 \ud574\uacb0\ub418\uc5c8\ub2e4. \\n\ub610\ud55c \uc8fc\uae30\ub2a5\uacfc \ubd80\uae30\ub2a5\uc744 \ubd84\ub9ac\ud568\uc73c\ub85c\uc368 \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uae30\ub2a5\uc5d0 \ub300\ud55c \uc804\uccb4\uc801\uc778 \uacb0\ud569\ub3c4\ub97c \ub0ae\ucd94\uc5c8\ub2e4.\\n\\n```mermaid\\ngraph LR\\n subgraph trip\\n TripServcie -- \ubc1c\ud589 --\x3e TripUpdateEvent\\n TripRepository\\n end\\n\\n subgraph draw\\n TripUpdateEventHandler -- \uad6c\ub3c5 \ud6c4 \uc774\ubbf8\uc9c0 \uc0dd\uc131 --\x3e TripUpdateEvent\\n TripUpdateEventHandler -- \uc0dd\uc131\ub41c \uc774\ubbf8\uc9c0 \uacbd\ub85c \uc800\uc7a5 --\x3e TripRepository\\n end\\n```\\n\\n### \ud14c\uc2a4\ud2b8\\n\\n\ube44\ub3d9\uae30\ub85c \ub3d9\uc791\ud558\ub294 \uba54\uc11c\ub4dc\ub97c \ud14c\uc2a4\ud2b8\ud558\uae30 \uc704\ud574\uc11c\ub294 \uc544\ub798\uc640 \uac19\uc740 \ubc29\ubc95\uc774 \uc788\ub2e4. \\n\\nimport Tabs from \\"@theme/Tabs\\";\\nimport TabItem from \\"@theme/TabItem\\";\\n\\n\\n\\n\\n```java\\n@SpringBootTest\\npublic class TripUpdateEventHandlerIntegrationTest {\\n\\n ...\\n\\n @Test\\n void \uc5ec\ud589\uc218\uc815_\uc774\ubca4\ud2b8\ub97c_\ubc1c\uc0dd\uc2dc\ud0a4\uba74_\uc774\ubbf8\uc9c0\ub97c_\uc0dd\uc131_\uc694\uccad\uc744_\ud55c\ub2e4() {\\n // given\\n TripUpdateEvent tripUpdateEvent = new TripUpdateEvent(1L);\\n given(tripRepository.getTripWithPoints(tripUpdateEvent.tripId()))\\n .willReturn(\uc5ec\ud589());\\n\\n // when\\n transactionTemplate.executeWithoutResult(action -> applicationEventPublisher.publishEvent(tripUpdateEvent));\\n\\n // then\\n then(routeImageGenerator)\\n .should(Mockito.timeout(5000).times(1))\\n .generate(any(), any(), any(), any());\\n }\\n}\\n```\\n\\n\\n\\n\\n\\n```java\\n@ContextConfiguration(classes = TestSyncConfig.class)\\n@SpringBootTest\\npublic class TripUpdateEventHandlerIntegrationTest {\\n\\n ...\\n\\n @Test\\n void \uc5ec\ud589\uc218\uc815_\uc774\ubca4\ud2b8\ub97c_\ubc1c\uc0dd\uc2dc\ud0a4\uba74_\uc774\ubbf8\uc9c0\ub97c_\uc0dd\uc131_\uc694\uccad\uc744_\ud55c\ub2e4() {\\n // given\\n TripUpdateEvent tripUpdateEvent = new TripUpdateEvent(1L);\\n given(tripRepository.getTripWithPoints(tripUpdateEvent.tripId()))\\n .willReturn(\uc5ec\ud589());\\n\\n // when\\n transactionTemplate.executeWithoutResult(action -> applicationEventPublisher.publishEvent(tripUpdateEvent));\\n\\n // then\\n then(routeImageGenerator)\\n .should(times(1))\\n .generate(any(), any(), any(), any());\\n }\\n}\\n```\\n\\n\\n\\n\\n\ucc98\uc74c\uc5d0\ub294 \ud14c\uc2a4\ud2b8\uc5d0\uc11c\ub9cc \ub3d9\uae30\ub85c \uc124\uc815 \ud6c4 \uac80\uc99d\ud558\ub824\uace0 \ud588\ub2e4. \\n\ud1b5\ud569 \ud14c\uc2a4\ud2b8\uc5d0\uc120 `\ud2b8\ub79c\uc7ad\uc158\uc774 \uc815\uc0c1 \uc885\ub8cc\ub418\uc5c8\uc744 \ub54c \ube44\ub3d9\uae30\ub85c \uc774\ubca4\ud2b8\ub97c \uad6c\ub3c5\ud558\uc5ec \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uba54\uc11c\ub4dc\ub97c \ud638\ucd9c\ud558\ub294\uc9c0` \uac80\uc99d\uc774 \ud544\uc694\ud588\uae30 \ub54c\ubb38\uc5d0 \ucd5c\uc885\uc801\uc73c\ub85c `Mockito.timeout` \uba54\uc11c\ub4dc\ub97c \uc0ac\uc6a9\ud558\uc5ec \ube44\ub3d9\uae30 \uba54\uc11c\ub4dc\uac00 \ud1b5\uacfc\ub420 \ub54c\uae4c\uc9c0 \ub300\uae30\ud558\ub294 \ubc29\ud5a5\uc73c\ub85c \ubcc0\uacbd\ud588\ub2e4. \\n\\n## \uacb0\uacfc\\n\\n![./time.png](./time.png)\\n\\n\uc704 \uc751\ub2f5 \uc2dc\uac04\uc740 \uc704\uce58 \uc815\ubcf4 1000\uac1c\ub97c \uae30\uc900\uc73c\ub85c \ud14c\uc2a4\ud2b8\ud55c \uac12\uc774\ub2e4. \\n\uc751\ub2f5 \uc2dc\uac04\uc5d0 \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc2dc\uac04\uc774 \ud3ec\ud568\ub418\uc9c0 \uc54a\uc544\uc11c \uc131\ub2a5\uc774 \uac1c\uc120\ub41c \uac83\uc744 \ubcfc \uc218 \uc788\ub2e4. \\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n[7.7. Task Execution and Scheduling, Spring Boot Docs](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.task-execution-and-scheduling) \\n[Spring Events, Baeldung](https://www.baeldung.com/spring-events) \\n[\ud68c\uc6d0\uc2dc\uc2a4\ud15c \uc774\ubca4\ud2b8\uae30\ubc18 \uc544\ud0a4\ud14d\ucc98 \uad6c\ucd95\ud558\uae30](https://techblog.woowahan.com/7835/)"},{"id":"route-image-implementation","metadata":{"permalink":"/route-image-implementation","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604.mdx","source":"@site/blog/2023-3/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604.mdx","title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604","description":"\uac1c\uc694","date":"2023-08-02T00:00:00.000Z","formattedDate":"2023\ub144 8\uc6d4 2\uc77c","tags":[{"label":"image","permalink":"/tags/image"},{"label":"awt","permalink":"/tags/awt"}],"readingTime":11.665,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604","slug":"route-image-implementation","tags":["image","awt"]},"prevItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac","permalink":"/route-image-async-with-event"},"nextItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c","permalink":"/route-image-python"}},"content":"## \uac1c\uc694\\n\\n\uc5ec\ud589\uc5d0 \ub300\ud55c \uacbd\ub85c\ub97c \ubcf4\uc5ec\uc8fc\uae30 \uc704\ud574 \uacbd\ub85c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\ub294 \uae30\ub2a5\uc744 \ucd94\uac00\ud588\ub2e4. \\n\uacbd\ub85c \uc774\ubbf8\uc9c0\uc5d0 \ub300\ud55c \uc694\uad6c\uc0ac\ud56d \ubc0f \uae30\uc220 \uc120\ud0dd\uc5d0 \ub300\ud55c \ub0b4\uc6a9\uc740 [\ub9c1\ud06c](./route-image-intro)\uc5d0 \uc788\ub2e4.\\n\\n### \uad6c\ud604 \uacb0\uacfc\\n\\n![./result.png](./result.png)\\n\\n\uc608\uc2dc \ub370\uc774\ud130\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n**\uc11c\uc6b8\uc5ed(\uc810)** \u2192 \uc2e0\uc0ac\uc5ed \u2192 \ub178\ub7c9\uc9c4\uc5ed \u2192 \ud64d\ub300\uc785\uad6c\uc5ed \u2192 \uc885\ub85c3\uac00\uc5ed \u2192 \uc625\uc218\uc5ed \u2192 **\uad6c\ub85c\uc5ed(\uc810)** \u2192 \uc2e0\ub9bc\uc5ed \u2192 \ubc1c\uc0b0\uc5ed\\n\\n```java title=\\"\uc608\uc2dc \ub370\uc774\ud130\\"\\nList x = List.of(\\n 126.97094933811682, 127.02154822802501, 126.94218991864345, 126.92402556641424,\\n 126.99265358592287, 127.01779856076462, 126.88474839801178, 126.92900751277035, 126.83930056313639\\n);\\nList y = List.of(\\n 37.55302829553499, 37.51619698970427, 37.51294119442773, 37.5565933969331,\\n 37.57034879708931, 37.54027238225762, 37.50129417536773, 37.48258811529137, 37.557607696911184\\n);\\nList xPoints = List.of(126.97094933811682, 126.88474839801178);\\nList yPoints = List.of(37.55302829553499, 37.50129417536773);\\n```\\n\\n### IMAGE_SIZE & ROUTE_SIZE\\n\\n```java title=\\"RouteImageGenerator.java\\"\\nprivate static final int IMAGE_SIZE = 800;\\nprivate static final int ROUTE_SIZE = 600;\\n```\\n\\n\ucf54\ub4dc\ub97c \ubcf4\uba74 IMAGE_SIZE\uc640 ROUTE_SIZE\uac00 \uc788\ub2e4. \\nIMAGE_SIZE\ub294 \ub9d0 \uadf8\ub300\ub85c \uc774\ubbf8\uc9c0\uc758 width\uc640 height\ub97c \uc758\ubbf8\ud55c\ub2e4. \\nROUTE_SIZE\uc758 \uacbd\uc6b0 \uc0c1\ud558\uc88c\uc6b0 100px \ub9cc\ud07c\uc758 \uac04\uaca9\uc744 \uc704\ud574 \uc874\uc7ac\ud55c\ub2e4. \\n\ub530\ub77c\uc11c \uc2e4\uc81c \uacbd\ub85c \uc774\ubbf8\uc9c0\uc758 \ud06c\uae30\ub294 600 * 600 \uc0ac\uc774\uc988\ub85c \uc0dd\uc131\ub41c\ub2e4. \\n\\n![./600.png](./600.png)\\n\\n**\uc0ac\uc774\uc988 \ubcc0\uacbd\uc758 \uc774\uc720**\\n\\n255 * 255 \uc815\ub3c4\uc758 \uc791\uc740 \uc0ac\uc774\uc988\ub85c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud574\ubcf4\ub824\uace0 \ud588\ub294\ub370, \uc774\ubbf8\uc9c0\uc758 \uc120\uba85\ub3c4\uac00 \uc88b\uc9c0 \uc54a\uc544 800 * 800 \uc0ac\uc774\uc988\ub85c \ubcc0\uacbd\ud588\ub2e4.\\n\\n## \uc8fc\uc694 \ud074\ub798\uc2a4\\n\\n### \uc694\uc57d\\n\\n| \ud074\ub798\uc2a4\uba85 | \uc124\uba85 | \ud2b9\uc774\uc0ac\ud56d |\\n| --- | --- | --- |\\n| Coordinate | \uc704\ub3c4, \uacbd\ub3c4\ub85c \uc774\ub8e8\uc5b4\uc9c4 \uc704\uce58 \uac12 | \uc88c\ud45c\ub97c \ub73b\ud558\uc9c0\ub9cc \uc5ec\ud589 \ub3c4\uba54\uc778\uc5d0 \ud3ec\ud568\ub41c Point \ud074\ub798\uc2a4\uc640 \uad6c\ubd84\ud558\uae30 \uc704\ud574 longitude, latitude\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\uace0 x, y \uc0ac\uc6a9 |\\n| Coordinates | Coordinate\uc758 \uc77c\uae09 \uceec\ub809\uc158 | - |\\n| Position | \uc2e4\uc81c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc5d0 \uc0ac\uc6a9\ud560 \uc704\uce58 \uac12 | Integer \ud0c0\uc785\uc758 x, y \uc0ac\uc6a9 |\\n| Positions | Positions\uc758 \uc77c\uae09 \uceec\ub809\uc158 | - |\\n| RouteImageDrawer | \uc2e4\uc81c \uc774\ubbf8\uc9c0\uc5d0 \uacbd\ub85c\ub97c \uadf8\ub824\uc8fc\ub294 \ud074\ub798\uc2a4 BufferedImage, Graphics2D\ub97c \uac00\uc9c0\uace0 \uc788\uc74c | \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc5d0 \ud544\uc694\ud55c \uc0c1\uc218\uac00 \uc815\uc758\ub418\uc5b4 \uc788\uc74c |\\n| RouteImageUploader | BufferedImage\ub97c \ubc1b\uc544 \uc11c\ubc84\uc5d0 \uc5c5\ub85c\ub4dc \ud558\ub294 \ud074\ub798\uc2a4 | \ud604\uc7ac \uc5c5\ub85c\ub4dc \uc704\uce58\uac00 \uc815\ud574\uc9c0\uc9c0 \uc54a\uc544 \uc77c\ub2e8 \uae30\ubcf8(\ud504\ub85c\uc81d\ud2b8 \ub8e8\ud2b8) \uc704\uce58\uc5d0 \uc0dd\uc131 |\\n| RouteImageGenerator | \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\uace0 \uc5c5\ub85c\ub4dc\ud558\ub294 \uc11c\ube44\uc2a4 | \uc5ec\ud589 \uc885\ub8cc, \uac10\uc0c1 \uc800\uc7a5\uc2dc \ud574\ub2f9 \ud074\ub798\uc2a4\ub97c \ud1b5\ud574 \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc694\uccad |\\n| BufferedImage(AWT) | \uc774\ubbf8\uc9c0 \ub370\uc774\ud130\ub97c \ucc98\ub9ac\ud558\uace0 \uc870\uc791\ud558\ub294 \ub370 \uc0ac\uc6a9 | \uc67c\ucabd \uc0c1\ub2e8\uc758 \uc88c\ud45c\uac00 (0, 0) |\\n| Graphics2D(AWT) | \uc120 \uadf8\ub9ac\uae30, \uc0c9\uc0c1 \uad00\ub9ac \ub4f1\uc744 \uc9c0\uc6d0\ud558\ub294 \ud074\ub798\uc2a4 \uc2e4\uc81c \ud574\ub2f9 \ud074\ub798\uc2a4\uc758 draw \uba54\uc11c\ub4dc\ub97c \uacbd\ub85c\ub97c \uadf8\ub9bc | JDK 1.2 \uc774\ud6c4\uc5d0 \ucd94\uac00\ub428, 2D(\ud3c9\uba74) \uadf8\ub798\ud53d \ud658\uacbd\uc744 \uc9c0\uc6d0, bufferedImage.createGraphics \uba54\uc11c\ub4dc\ub97c \ud1b5\ud574 \uc0dd\uc131 |\\n\\n### \uc758\uc874\uad00\uacc4\\n\\n```mermaid\\ngraph TD\\n C1[Coordinates] --\x3e C[Coordinate]\\n P1[Positions] --\x3e P[Position]\\n\\n\\tRID[RouteImageDrawer] -- \\"\uc911\uc559 \uc815\ub82c\ub41c Positions\ub97c \ubc1b\uc544 \uc774\ubbf8\uc9c0 \uc0dd\uc131\\" --\x3e P1\\n\\tRID --\x3e B[BufferedImage]\\n\\tRID --\x3e G[Graphics2D]\\n\\n\\tC1 -- \\"calculatePositions \uc2e4\uc81c \uc774\ubbf8\uc9c0\uc5d0 \uc0dd\uc131\uc5d0 \ud544\uc694\ud55c \uc704\uce58 \uacc4\uc0b0\\" --\x3e P1\\n\\n\\tRIU[RouteImageUploader] --\x3e B\\n\\tRIG[RouteImageGenerator] --\x3e RID\\n\\tRIG --\x3e RIU\\n\\tRIG --\x3e C1\\n\\tRIG --\x3e P1\\n```\\n\\n### Coordinates(\uc704\ub3c4, \uacbd\ub3c4\uc758 \uc77c\uae09 \uceec\ub809\uc158)\\n\\n`List` 2\uac1c(\uc704\ub3c4, \uacbd\ub3c4)\uc778 \ud615\ud0dc\ub85c \uad00\ub9ac\ud558\ub294 \ubc29\ubc95\uc774 \uc788\uc5c8\uc9c0\ub9cc, \uc704\uce58 \uc810\uc744 \uc5ec\ub7ec\uac1c \ucc0d\ub294 \ubd80\ubd84\uc5d0\uc11c \ub85c\uc9c1\uc774 \ubcf5\uc7a1\ud574 \uc9c8 \uac83 \uac19\uc544\uc11c Coordinate(x, y)\uc640 \uc77c\uae09 \uceec\ub809\uc158\uc778 Coordinates\ub85c \uad00\ub9ac\ud558\uae30\ub85c \ud588\ub2e4. \\nCoordinates \ud074\ub798\uc2a4\uc5d0\ub294 \ub2e4\uc74c \ub450 \uac1c\uc758 \uc778\ud130\ud398\uc774\uc2a4\uac00 \uc874\uc7ac\ud55c\ub2e4.\\n\\n- calculatePositions: \uacbd\ub85c \uc774\ubbf8\uc9c0\uc758 \ud06c\uae30\ub97c \ubc1b\uc544 \uc2e4\uc81c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc2dc \uc0ac\uc6a9\ub420 Positions\ub97c \ubc18\ud658\\n- indexOf: \ub2e4\ub978 Coordinates\ub97c \ubc1b\uc544 \ub3d9\uc77c\ud55c \uc704\uce58\uc810\uc5d0 \ud574\ub2f9\ud558\ub294 \uc778\ub371\uc2a4\ub97c \ubc18\ud658\ud558\ub294 \\n\\nPositions \uacc4\uc0b0 \ub85c\uc9c1\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\uc704\ub3c4, \uacbd\ub3c4 \uac01\uac01\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc2dc \ud544\uc694\ud55c \uac12\uc73c\ub85c \ubcc0\ud658\ud55c\ub2e4.\\n\\n```java title=\\"Coordinates.java\\"\\n// \ud638\ucd9c\\n// List xPositions = toPositions(xValues, maxDifference, routeImageSize);\\n// List yPositions = toPositions(yValues, maxDifference, routeImageSize);\\n\\nprivate List toPositions(List values, Double maxDifference, Integer routeImageSize) {\\n Double minValue = Collections.min(values);\\n return values.stream()\\n .map(value -> normalizeCoordinate(value, maxDifference, minValue))\\n .map(value -> mapToPosition(value, routeImageSize))\\n .toList();\\n}\\n\\nprivate double normalizeCoordinate(Double coordinate, Double maxDifference, Double minValue) {\\n return (coordinate - minValue) / maxDifference;\\n}\\n\\nprivate int mapToPosition(Double coordinate, Integer routeImageSize) {\\n return (int) (coordinate * routeImageSize);\\n}\\n```\\n\\n\uc704\ub3c4\ub85c \uc608\uc2dc\ub4e0 \ub0b4\uc6a9\uc774\ub2e4.\\n\\n1. Collections.min(values) \u2192 \uc704\ub3c4 \ub9ac\uc2a4\ud2b8\uc758 \ucd5c\uc18c\uac12\uc744 \uad6c\ud55c\ub2e4.\\n2. normalizeCoordinate \u2192 \uac01\uac01\uc758 \uc704\ub3c4 \uac12\uc5d0\uc11c \ucd5c\uc18c\uac12\uc744 \ube7c\uace0 0 ~ 1 \uc0ac\uc774 \uac12\uc73c\ub85c \ubcc0\ud658 \ud6c4 **\uc704\uacbd\ub3c4\uc758 \ucd5c\ub300 \ucc28\uc774**\ub85c \ub098\ub208\ub2e4.\\n3. mapToPosition \u2192 \uadf8\ub798\ud504 \ud06c\uae30\ub97c \ubc1b\uc544 0 ~ 1 \uc0ac\uc774 \uac12\uc744 \uc2e4\uc81c \uc774\ubbf8\uc9c0\ub97c \uc704\ud55c \uc704\uce58\uac12\uc73c\ub85c \ubcc0\ud658\ud55c\ub2e4.\\n\\n### Positions(\uc2e4\uc81c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc5d0 \uc0ac\uc6a9\ud560 \uc704\uce58)\\n\\nPositions \ud074\ub798\uc2a4\uc5d0\ub294 \ub2e4\uc74c \ub2e4\uc12f \uac1c\uc758 \uc778\ud130\ud398\uc774\uc2a4\uac00 \uc874\uc7ac\ud55c\ub2e4.\\n\\n- align: \uc774\ubbf8\uc9c0 \uc0ac\uc774\uc988\uc640 \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0ac\uc774\uc988\ub97c \ubc1b\uc544 Position \uac12\ub4e4\uc744 \uc911\uc559 \uc815\ub82c\ud55c\ub2e4.\\n- getPositionsByIndexes: \uc778\ub371\uc2a4 \ub9ac\uc2a4\ud2b8\ub97c \ubc1b\uc544 \uc785\ub825\ubc1b\uc740 \uc778\ub371\uc2a4\uc5d0 \ud574\ub2f9\ud558\ub294 \uac12\ub4e4\uc744 \ubc18\ud658\ud55c\ub2e4.\\n- size: \ud06c\uae30\ub97c \ubc18\ud658\ud55c\ub2e4.\\n- xPositions: x \uac12\ub4e4\uc744 \ubc18\ud658\ud55c\ub2e4.\\n- yPositions: y \uac12\ub4e4\uc744 \ubc18\ud658\ud55c\ub2e4.\\n\\n\uc911\uc559 \uc815\ub82c \ub85c\uc9c1\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\\n```java title=\\"Positions.java\\"\\npublic Positions align(int imageSize, int routeSize) {\\n int xOffset = calculateOffset(Position::x, imageSize);\\n int yOffset = calculateOffset(Position::y, imageSize);\\n\\n return items.stream()\\n .map(item -> new Position(item.x() + xOffset, imageSize - (item.y() + yOffset)))\\n .collect(collectingAndThen(toList(), Positions::new));\\n}\\n\\nprivate int calculateOffset(ToIntFunction positionToInteger, int imageSize) {\\n List positions = items.stream()\\n .mapToInt(positionToInteger)\\n .boxed()\\n .toList();\\n\\n int midValue = (Collections.min(positions) + Collections.max(positions)) / 2;\\n return imageSize / 2 - midValue;\\n}\\n```\\n\\n\uc0c1\ud558\uc88c\uc6b0 \uc5ec\ubc31\uc744 \ub3d9\uc77c\ud558\uac8c \uc8fc\uae30 \uc704\ud574\uc11c offset \uac12\uc744 \uad6c\ud574\uc11c x, y \uac12\uc5d0 \uac01\uac01 \ub354\ud558\ub294 \ud615\ud0dc\ub85c \uc911\uc559 \uc815\ub82c\uc744 \uc218\ud589\ud588\ub2e4. \\nBufferedImage\ub97c \uc0ac\uc6a9\ud560 \ub54c \uc67c\ucabd \uc0c1\ub2e8\uc758 \uc88c\ud45c (0, 0) \uae30\uc900\uc73c\ub85c \uc544\ub798\ub85c \ub0b4\ub824\uac08\uc218\ub85d y \uac12\uc774 \ucee4\uc9c0\uace0, \uc624\ub978\ucabd\uc73c\ub85c \uac08 \uc218\ub85d x \uac12\uc774 \ucee4\uc9c4\ub2e4. \\n\\n![./800.png](./800.png)\\n\\n\ub530\ub77c\uc11c \ucd5c\uc885\uc801\uc73c\ub85c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\uae30 \uc704\ud55c \uac12\uc744 \ub2e4\uc74c\uacfc \uac19\uc774 \uad6c\ud588\ub2e4.\\n\\nx \uac12 \u2192 \uacc4\uc0b0\ud55c offset \uadf8\ub300\ub85c \ub354\ud55c\ub2e4. \\ny \uac12 \u2192 imageSize(800)\uc5d0\uc11c y + offset \uac12\uc744 \ube80\ub2e4. \\n\\n### RouteImageDrawer(\uc2e4\uc81c \uc774\ubbf8\uc9c0\uc5d0 \uacbd\ub85c\ub97c \uadf8\ub824\uc8fc\ub294 \ud074\ub798\uc2a4)\\n\\nBufferedImage, Graphics2D\ub97c \ud544\ub4dc\ub85c \uac00\uc9c0\uace0 \uc788\ub294 \ud074\ub798\uc2a4\ub2e4. \\n\uadf8\ub9bc\uc744 \uadf8\ub9ac\uae30 \uc704\ud574 \uc124\uc815\ud55c \uc0c1\uc218\ub4e4\uc774 \uc874\uc7ac\ud55c\ub2e4.\\n\\n```java title=\\"RouteImageDrawer.java\\"\\n// RGB\uc5d0 \uac01\uac01 8\ube44\ud2b8\uc529 \ud560\ub2f9\ud55c \uac12\uc744 24\ube44\ud2b8 \ud2b8\ub8e8\uceec\ub7ec\ub77c \ubd80\ub978\ub2e4.\\n// \ud574\ub2f9 \uc124\uc815\uc740 24\ube44\ud2b8 + 8\ube44\ud2b8(alpha, \ud22c\uba85\ub3c4)\ub97c \ucd94\uac00\ud55c 32\ube44\ud2b8 \uc774\ubbf8\uc9c0 \ud0c0\uc785\uc774\ub2e4.\\n// \uc774\ub97c RGBA\ub77c\uace0 \ubd80\ub978\ub2e4.\\nprivate static final int IMAGE_TYPE = BufferedImage.TYPE_INT_ARGB;\\n// \ubc30\uacbd \ud22c\uba85\uc0c9\\nprivate static final Color TRANSPARENT = new Color(0, 0, 0, 0);\\n// \uacbd\ub85c\ub97c \uc704\ud55c STROKE\\nprivate static final int LINE_STROKE_WIDTH = 7;\\nprivate static final Stroke LINE_STROKE = new BasicStroke(LINE_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);\\n// \uc704\uce58 \uc810\uc744 \uc704\ud55c STROKE\\nprivate static final int POINT_STROKE_WIDTH = 20;\\nprivate static final Stroke POINT_STROKE = new BasicStroke(POINT_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);\\n// \uc548\ud2f0\uc568\ub9ac\uc5b4\uc2f1 \ub4f1 \ud654\uc9c8 \uac1c\uc120\uc744 \uc704\ud55c \uc124\uc815\\nprivate static final Map renderingHints = Map.of(\\n RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON,\\n RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY,\\n RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC\\n);\\n```\\n\\nRouteImageDrawer \ud074\ub798\uc2a4\uc5d0\ub294 \ub2e4\uc74c \uc138 \uac1c\uc758 \uc778\ud130\ud398\uc774\uc2a4\uac00 \uc874\uc7ac\ud55c\ub2e4.\\n\\n- drawLine: \uc120\uc744 \uadf8\ub9b0\ub2e4.\\n- drawPoint: \uc810\uc744 \ucc0d\ub294\ub2e4.\\n- dispose: \uc790\uc6d0 \ud560\ub2f9\uc744 \ud574\uc81c\ud55c\ub2e4. \\n\\ndispose\uc758 \uacbd\uc6b0 \ub0b4\ubd80\uc5d0\uc11c \uc0dd\uc131\ub41c graphics2D\uc5d0 \ub300\ud55c \uc790\uc6d0 \ud560\ub2f9\uc744 \ud574\uc81c\ud558\ub294 \uba54\uc11c\ub4dc\uc778 graphics2D.dispose\ub97c \ud638\ucd9c\ud55c\ub2e4.\\n\\n## \uc774\ubbf8\uc9c0 \uc0dd\uc131 Flow\\n\\n### 1. \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc900\ube44\\n\\n```mermaid\\nsequenceDiagram\\n \uc678\ubd80 \ud074\ub798\uc2a4 ->> RouteImageGenerator: \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc694\uccad(\uc704\uacbd\ub3c4, \uc704\uce58 \uc810\uc744 \ucc0d\uc744 \uac12 \uc804\ub2ec)\\n RouteImageGenerator->>RouteImageDrawer: ImageSize\ub97c \uc804\ub2ec\ud558\uc5ec \uac1d\uccb4 \uc0dd\uc131, \ub0b4\ubd80\uc5d0\uc11c BufferedImage, Graphincs2D \uc0dd\uc131\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): \uc704\uacbd\ub3c4 \uc774\uc6a9\ud558\uc5ec Coordinates1 \uc0dd\uc131\\n RouteImageGenerator->>Coordinates2(\uc704\uce58\uc810): \uc704\uce58\uc810 \uc774\uc6a9\ud558\uc5ec Coordinates2 \uc0dd\uc131\\n\\n```\\n\\n### 2. \uc120 \uadf8\ub9ac\uae30 \uc694\uccad\\n\\n```mermaid\\nsequenceDiagram\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): \uc815\ub82c\ub41c Positions\ub97c \uc0dd\uc131 \uc694\uccad\\n Coordinates1(\uc704\uacbd\ub3c4)->>RouteImageGenerator: \uc815\ub82c\ub41c Positions \ubc18\ud658\\n RouteImageGenerator->>RouteImageDrawer: \uc815\ub82c\ub41c Positions\ub97c \uacbd\ub85c \uadf8\ub9ac\uae30 \uc694\uccad\\n```\\n\\n### 3. \uc704\uce58 \uc810 \uadf8\ub9ac\uae30 \uc694\uccad\\n\\n```mermaid\\nsequenceDiagram\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): Coordinate2(\uc704\uce58\uc810)\ub97c \uc804\ub2ec\ud558\uace0 \ud574\ub2f9 \uc704\uce58\uc810\uacfc \uc77c\uce58\ud558\ub294 Coordinate\uc758 \uc778\ub371\uc2a4 \uc0dd\uc131 \uc694\uccad\\n Coordinates1(\uc704\uacbd\ub3c4)->>RouteImageGenerator: \uc704\uce58\uc810\uc5d0 \ud574\ub2f9\ud558\ub294 \uc778\ub371\uc2a4(List) \ubc18\ud658\\n RouteImageGenerator->>\uc815\ub82c\ub41c Positions: \uc778\ub371\uc2a4(List)\ub97c \uc804\ub2ec\ud558\uc5ec \uc778\ub371\uc2a4\uc5d0 \ud574\ub2f9\ud558\ub294 Positions \uc0dd\uc131 \uc694\uccad\\n \uc815\ub82c\ub41c Positions->>RouteImageGenerator: \uc704\uce58\uc810\uc5d0 \ud574\ub2f9\ud558\ub294 Positions \ubc18\ud658(pointPositions)\\n\\n RouteImageGenerator->>RouteImageDrawer: pointPositions\ub97c \uc804\ub2ec\ud558\uc5ec \uc704\uce58 \uc810 \uadf8\ub9ac\uae30 \uc694\uccad\\n```\\n\\n### 4. \uc5c5\ub85c\ub4dc \uc694\uccad\\n\\n```mermaid\\nsequenceDiagram\\n \\tRouteImageGenerator->>RouteImageUploader: bufferedImage(RouteImageDrawer\uc5d0\uc11c getter \uc0ac\uc6a9)\ub97c \uc804\ub2ec\ud558\uc5ec \uc774\ubbf8\uc9c0 \uc800\uc7a5 \uc694\uccad\\n \\tRouteImageUploader->>RouteImageGenerator: \uc800\uc7a5 \ud6c4 \uc800\uc7a5\ub41c \uc774\ubbf8\uc9c0\uba85(\ub610\ub294 url) \ubc18\ud658\\n \\tRouteImageGenerator->>\uc678\ubd80 \ud074\ub798\uc2a4: \uc800\uc7a5\ub41c \uc774\ubbf8\uc9c0\uba85(\ub610\ub294 url) \ubc18\ud658\\n```\\n\\n### \uc804\uccb4 Flow\\n\\n```mermaid\\nsequenceDiagram\\n \uc678\ubd80 \ud074\ub798\uc2a4 ->> RouteImageGenerator: \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc694\uccad(\uc704\uacbd\ub3c4, \uc704\uce58 \uc810\uc744 \ucc0d\uc744 \uac12 \uc804\ub2ec)\\n RouteImageGenerator->>RouteImageDrawer: ImageSize\ub97c \uc804\ub2ec\ud558\uc5ec \uac1d\uccb4 \uc0dd\uc131, \ub0b4\ubd80\uc5d0\uc11c BufferedImage, Graphincs2D \uc0dd\uc131\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): \uc704\uacbd\ub3c4 \uc774\uc6a9\ud558\uc5ec Coordinates1 \uc0dd\uc131\\n RouteImageGenerator->>Coordinates2(\uc704\uce58\uc810): \uc704\uce58\uc810 \uc774\uc6a9\ud558\uc5ec Coordinates2 \uc0dd\uc131\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): \uc815\ub82c\ub41c Positions\ub97c \uc0dd\uc131 \uc694\uccad\\n Coordinates1(\uc704\uacbd\ub3c4)->>RouteImageGenerator: \uc815\ub82c\ub41c Positions \ubc18\ud658\\n RouteImageGenerator->>RouteImageDrawer: \uc815\ub82c\ub41c Positions\ub97c \uacbd\ub85c \uadf8\ub9ac\uae30 \uc694\uccad\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): Coordinate2(\uc704\uce58\uc810)\ub97c \uc804\ub2ec\ud558\uace0 \ud574\ub2f9 \uc704\uce58\uc810\uacfc \uc77c\uce58\ud558\ub294 Coordinate\uc758 \uc778\ub371\uc2a4 \uc0dd\uc131 \uc694\uccad\\n Coordinates1(\uc704\uacbd\ub3c4)->>RouteImageGenerator: \uc704\uce58\uc810\uc5d0 \ud574\ub2f9\ud558\ub294 \uc778\ub371\uc2a4(List) \ubc18\ud658\\n RouteImageGenerator->>\uc815\ub82c\ub41c Positions: \uc778\ub371\uc2a4(List)\ub97c \uc804\ub2ec\ud558\uc5ec \uc778\ub371\uc2a4\uc5d0 \ud574\ub2f9\ud558\ub294 Positions \uc0dd\uc131 \uc694\uccad\\n \uc815\ub82c\ub41c Positions->>RouteImageGenerator: \uc704\uce58\uc810\uc5d0 \ud574\ub2f9\ud558\ub294 Positions \ubc18\ud658(pointPositions)\\n\\n RouteImageGenerator->>RouteImageDrawer: pointPositions\ub97c \uc804\ub2ec\ud558\uc5ec \uc704\uce58 \uc810 \uadf8\ub9ac\uae30 \uc694\uccad\\n RouteImageGenerator->>RouteImageUploader: bufferedImage(RouteImageDrawer\uc5d0\uc11c getter \uc0ac\uc6a9)\ub97c \uc804\ub2ec\ud558\uc5ec \uc774\ubbf8\uc9c0 \uc800\uc7a5 \uc694\uccad\\n RouteImageUploader->>RouteImageGenerator: \uc800\uc7a5 \ud6c4 \uc800\uc7a5\ub41c \uc774\ubbf8\uc9c0\uba85(\ub610\ub294 url) \ubc18\ud658\\n RouteImageGenerator->>\uc678\ubd80 \ud074\ub798\uc2a4: \uc800\uc7a5\ub41c \uc774\ubbf8\uc9c0\uba85(\ub610\ub294 url) \ubc18\ud658\\n\\t\\n```"},{"id":"route-image-python","metadata":{"permalink":"/route-image-python","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c.mdx","source":"@site/blog/2023-3/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c.mdx","title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c","description":"\uac1c\uc694","date":"2023-07-31T00:00:00.000Z","formattedDate":"2023\ub144 7\uc6d4 31\uc77c","tags":[{"label":"Image","permalink":"/tags/image"},{"label":"Python","permalink":"/tags/python"}],"readingTime":6.185,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c","slug":"route-image-python","tags":["Image","Python"]},"prevItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604","permalink":"/route-image-implementation"},"nextItem":{"title":"Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30","permalink":"/mock-static-method"}},"content":"### \uac1c\uc694\\n\\n\uc774\uc804\uc5d0 \uae30\uc220 \uad6c\ud604 \uac00\ub2a5 \uc5ec\ubd80\ub97c \uc870\uc0ac\ud558\uba74\uc11c \ud30c\uc774\uc36c\uc744 \uc0ac\uc6a9\ud55c \ub0b4\uc6a9\uc744 \uc815\ub9ac\ud55c \ub0b4\uc6a9\uc774\ub2e4. \\n\\n### \uc0ac\uc6a9 \uae30\uc220\\n\\n\uc5b8\uc5b4: Python 3.10 \\n\uc774\ubbf8\uc9c0 \uc0dd\uc131: matplotlib \\n\uc11c\ube44\uc2a4: AWS Lambda, AWS API Gateway \\n\uc774\ubbf8\uc9c0 \uc800\uc7a5 \ubc0f URL: AWS S3, AWS CloudFront \\n\\n\ud50c\ub85c\uc6b0\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\\n```mermaid\\ngraph LR\\n Server -- \uc0dd\uc131 \uc694\uccad --\x3e AG[API Gateway] --\x3e Lambda --\x3e S3\\n Client --\x3e CloudFront --\x3e S3\\n```\\n\\n### \uc694\uad6c\uc0ac\ud56d\\n\\n![./route.png](./route.png)\\n\\n\uc6b0\uce21 \uc0c1\ub2e8\uc758 \uacbd\ub85c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\ub824\uace0 \ud55c\ub2e4. \\n\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc5d0 \ub300\ud55c \uc694\uad6c\uc0ac\ud56d\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4.\\n\\n- \uc704\ub3c4, \uacbd\ub3c4\ub85c \uc774\ub8e8\uc5b4\uc9c4 \ubc30\uc5f4\uc744 \uc785\ub825\ubc1b\ub294\ub2e4. \\n- \uc774\ubbf8\uc9c0 \uc0dd\uc131\\n- \uc120\uacfc \uc810 \ud45c\ud604\\n- \ud22c\uba85\ud55c \ubc30\uacbd\uc0c9\\n- \uc704\uacbd\ub3c4 \ucc28\uc774\uac00 \ud06c\ub4e0 \uc791\ub4e0 \uc81c\uacf5\ud558\ub294 \uc774\ubbf8\uc9c0 \ub0b4\uc5d0 \uacbd\ub85c\uac00 \ub2e4 \ud3ec\ud568\ub418\uc5b4 \uc788\uc5b4\uc57c \ud55c\ub2e4. \\n\\n### \uc774\ubbf8\uc9c0 \ucd9c\ub825 \ubc29\uc2dd\\n\\n1. \uc704\uacbd\ub3c4\ub97c \ucc98\ub9ac\ud55c \uac12\uc73c\ub85c \uc9c1\uc811 \uacbd\ub85c\ub97c \uadf8\ub9b0 \ub2e4\uc74c \uc774\ubbf8\uc9c0 \ud615\ud0dc\ub85c \uc800\uc7a5\\n2. \ud50c\ub86f\uc744 \uadf8\ub824\uc8fc\ub294 \ub77c\uc774\ube0c\ub7ec\ub9ac \uc0ac\uc6a9\ud558\uc5ec \uc774\ubbf8\uc9c0 \ud615\ud0dc\ub85c \uc800\uc7a5\\n\\n\uc774\ubbf8\uc9c0 \ucd9c\ub825 \ubc29\uc2dd\uc758 \uacbd\uc6b0 1\ubc88\uacfc 2\ubc88\uc744 \uace0\ubbfc\ud588\uc5c8\ub2e4. \\n\ud30c\uc774\uc36c\uc73c\ub85c\ub294 \ud50c\ub86f\uc744 \uadf8\ub824\uc8fc\ub294 \ub77c\uc774\ube0c\ub7ec\ub9ac\uc778 matplotlib\uc744 \uc0ac\uc6a9\ud588\ub2e4. \\n\\n### \ub85c\uceec\uc5d0\uc11c \uae30\ub2a5 \uad6c\ud604\\n\\n```python\\nimport time\\n\\nimport matplotlib.pyplot as plt\\n\\n\\ndef draw(point):\\n start = time.time()\\n x, y = zip(*point)\\n pixel_x, pixel_y = convert_to_pixel_values(x, y)\\n draw_lines(pixel_x, pixel_y)\\n end = time.time()\\n print(end - start)\\n \\ndef convert_to_pixel_values(x, y):\\n max_diff = max(max(x) - min(x), max(y) - min(y))\\n return scale_to_pixel_values(x, max_diff), scale_to_pixel_values(y, max_diff)\\n\\n\\ndef scale_to_pixel_values(points, max_diff):\\n min_value = min(points)\\n scaled_coordinates = [(p - min_value) / max_diff for p in points]\\n return scaled_coordinates\\n\\n\\ndef draw_lines(x, y):\\n figure = plt.gcf()\\n figure.set_size_inches(5, 5)\\n plt.plot(x, y, c = \'w\',linewidth=5)\\n plt.scatter(x[3],y[3], c = \'w\', s = 125)\\n plt.axis(\'off\')\\n plt.savefig(\'name.png\', transparent=True, format=\'png\')\\n\\npoint = [\\n [126.96352960597338, 37.590841000217125],\\n [126.96987292787792, 37.58435564234159],\\n [126.98128481452298, 37.58594375113966],\\n [126.99360339342958, 37.58248524741927],\\n [126.99867565340067, 37.56778118088622],\\n [127.001935378366117, 37.55985240444085],\\n [126.9831048919687, 37.548030119488665],\\n [126.97189273528845, 37.5119879225856],\\n [127.02689859997221, 37.48488593333883]\\n]\\n\\ndraw(point)\\n```\\n\\n\uc0dd\uc131 \uacb0\uacfc\ub294 \uc544\ub798\uc640 \uac19\ub2e4. (\uc608\uc2dc\ub97c \uc704\ud574 \uac80\uc740\uc0c9\uc73c\ub85c \ucd9c\ub825)\\n\\n![./routeImage.png](./routeImage.png)\\n\\n### AWS Lambda\\n\\n\uc378\ub124\uc77c \uc0dd\uc131 \uc11c\ubc84\ub97c \ub530\ub85c \ub450\uae30\ub294 \uae30\ub2a5 \ub300\ube44 \ube44\uc6a9\uc774 \ub108\ubb34 \ud074 \uac83\uc774\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\ub530\ub77c\uc11c \uc11c\ubc84\ub9ac\uc2a4\ub85c \ud30c\uc77c\uc744 \ucc98\ub9ac\ud588\ub2e4. \\n\ucd94\uac00\ub85c s3 \uc811\uadfc\uc740 boto3\ub97c \uc0ac\uc6a9\ud588\ub2e4. \\n\\n### \ub78c\ub2e4 S3 \uc811\uadfc\uc744 \uc704\ud55c IAM \uc0dd\uc131\\n\\nAmazonS3FullAccess, AmazonS3ObjectLambdaExecutionRolePolicy \ub450\uac00\uc9c0\ub97c \ucd94\uac00\ud574\uc11c Lambda \uc804\uc6a9 \uc5ed\ud560\uc744 \ub9cc\ub4e4\uc5b4 \uc0ac\uc6a9\ud588\ub2e4. \\n\\n### \ub78c\ub2e4 \ubc30\ud3ec\uc6a9 \ucf54\ub4dc\\n\\n\uae30\uc220 \uad6c\ud604 \uac00\ub2a5 \uc5ec\ubd80\ub97c \ud655\uc778\ud560 \ub550 \uc704\uce58 \uc810\uc744 \ucc0d\ub294 \uae30\ub2a5\uc744 \ub78c\ub2e4\uc5d0 \ubc30\ud3ec\ud558\uc9c0 \uc54a\uc558\ub2e4. \\n\\n```python\\n\\nimport io\\nimport uuid\\n\\nimport boto3\\nimport matplotlib.pyplot as plt\\n\\nPIXEL = 255\\nBUCKET_NAME = \'image-plot\'\\nS3 = \'s3\'\\n\\ndef lambda_handler(event, context):\\n x = event[\'x\']\\n y = event[\'y\']\\n image_name = str(uuid.uuid4())\\n\\n img_data = draw(x, y)\\n s3 = boto3.client(S3)\\n s3.put_object(Body=img_data.getvalue(), ContentType=\'image/png\', Bucket=BUCKET_NAME, Key=image_name)\\n url = f\'https://{BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com/{image_name}\'\\n\\n return {\\n \'statusCode\': 200,\\n \'body\': url\\n }\\n\\ndef draw(x, y):\\n pixel_x, pixel_y = convert_to_pixel_values(x, y)\\n img_data = draw_lines(pixel_x, pixel_y)\\n plt.close()\\n return img_data\\n\\ndef convert_to_pixel_values(x, y):\\n max_diff = max(max(x) - min(x), max(y) - min(y))\\n return scale_to_pixel_values(x, max_diff), scale_to_pixel_values(y, max_diff)\\n\\ndef scale_to_pixel_values(points, max_diff):\\n min_value = min(points)\\n scaled_coordinates = [(p - min_value) / max_diff for p in points]\\n pixel_values = [int(p * PIXEL) for p in scaled_coordinates]\\n return pixel_values\\n\\ndef draw_lines(x, y):\\n plt.plot(x, y, \'k-\', linewidth=10)\\n plt.axis(\'off\')\\n img_data = io.BytesIO()\\n plt.savefig(img_data, transparent=True, format=\'png\')\\n img_data.seek(0)\\n return img_data\\n\\n```\\n\\n### Layer \ucd94\uac00\ub97c \uc704\ud55c zip \ud30c\uc77c \uc0dd\uc131\\n\\nmatplotlib\uc758 \uacbd\uc6b0 \uc678\ubd80 \ub77c\uc774\ube0c\ub7ec\ub9ac\uae30 \ub54c\ubb38\uc5d0 \ub530\ub85c Layer\ub97c \ucd94\uac00\ud574\uc57c \ud55c\ub2e4. \\nzip \ud30c\uc77c\uc744 \ub9cc\ub4e4\uc5b4\uc11c \uc5c5\ub85c\ub4dc\ud574\uc57c\ud55c\ub2e4. \\n\uc774\ub54c python\uc758 Lambda \ub7f0\ud0c0\uc784\uc5d0 \ub300\ud55c \uacc4\uce35 \uacbd\ub85c\ub294 python\uc774\ub2e4. \\n\ub530\ub77c\uc11c \uc555\ucd95\ud55c zip \ud30c\uc77c\uc740 \ub2e4\uc74c\uacfc \uac19\uc740 \uad6c\uc870\ub97c \ub744\uc5b4\uc57c \ud55c\ub2e4. \\n\\n```\\npillow.zip\\n\u2502 python/PIL\\n\u2514 python/Pillow-5.3.0.dist-info\\n```\\n\\nUbuntu \uae30\uc900 \ub2e4\uc74c \uba85\ub839\uc5b4\ub97c \uc785\ub825\ud558\uc5ec \uc0dd\uc131\uc744 \uc9c4\ud589\ud588\ub2e4. \\n\\n```\\nsudo apt update\\nsudo apt install zip\\nsudo apt install python3-pip\\n\\nmkdir python\\npip3 install matplotlib -t python # pip3 install \uc124\uce58\ud560_\ud328\ud0a4\uc9c0 -t \uc124\uce58_\uacbd\ub85c\\nzip -r my_layer.zip python # zip -r \uc555\ucd95_\ud30c\uc77c\uba85 \uc555\ucd95_\ud30c\uc77c\uc774_\uc874\uc7ac\ud558\ub294_\uacbd\ub85c\\n```\\n\\n### `No module named \'numpy.core._multiarray_umath\'` \uc5d0\ub7ec\\n\\nLayer \ucd94\uac00 \ud6c4 \ub78c\ub2e4 \uc2e4\ud589 \uc2dc \ubc1c\uc0dd\ud55c \uc5d0\ub7ec\uc600\ub2e4. \\n\ucc98\uc74c\uc5d0 mac\uc5d0\uc11c zip \ud30c\uc77c\uc744 \uc0dd\uc131\ud574\uc11c \uc5c5\ub85c\ub4dc\ud588\ub294\ub370 \ud574\ub2f9 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud588\ub2e4. \\n\uc774\ub294 lambda\uac00 \ub3cc\uc544\uac00\ub294 \ub3d9\uc77c\ud55c \ud658\uacbd\uc5d0\uc11c layer\ub97c \uc704\ud55c zip \ud30c\uc77c\uc744 \ub9cc\ub4e4\uc9c0 \uc54a\uc544\uc11c \ubc1c\uc0dd\ud558\ub294 \ubb38\uc81c\ub2e4. \\n\uac04\ub2e8\ud558\uac8c ec2 \uc778\uc2a4\ud134\uc2a4\ub97c \ud558\ub098 \ub9cc\ub4e4\uc5b4\uc11c \ub530\ub85c Layer\ub97c \uc0dd\uc131\ud558\uba74 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud558\uc9c0 \uc54a\ub294\ub2e4. \\n\\n### \uc801\uc815\uae30\uc220\uc5d0 \ub300\ud55c \uc0dd\uac01\\n\\n\ud504\ub85c\uc81d\ud2b8\uc5d0 Lambda\uc640 Python\uc744 \uc0ac\uc6a9\ud558\ub824\uace0 \ud588\uc9c0\ub9cc \uc544\uc27d\uac8c\ub3c4 \ubc18\ub824\ub2f9\ud588\ub2e4. \\nAWS Lambda\ub97c \uc0ac\uc6a9\ud558\ub294 \uac83\uc740 \uc778\uc2a4\ud134\uc2a4\uc5d0 \ud574\ub2f9 \ucf54\ub4dc\ub97c \ubc30\ud3ec\ud558\ub294 \uac83\ubcf4\ub2e4 \ub354 \ud6a8\uc728\uc801\uc778 \ubc29\ubc95\uc77c \uc218 \uc788\ub2e4. \\n\ud558\uc9c0\ub9cc \ud604\uc7ac \ud504\ub85c\uc81d\ud2b8\uc5d0\uc11c \uac00\uc6a9 \uac00\ub2a5\ud55c \uc790\uc6d0, \uae30\uc220\uc758 \ub09c\uc774\ub3c4, \uc0ac\uc6a9\ud558\ub294 \ud300\uc6d0\uc744 \uace0\ub824\ud55c\ub2e4\uba74 Lambda\ub294 \uc801\uc815\uae30\uc220\uc774 \uc544\ub2d0 \uc218 \uc788\ub2e4. \\n\ub530\ub77c\uc11c \ud574\ub2f9 \uc774\ubbf8\uc9c0 \uc0dd\uc131\uae30\ub97c \uc5b4\ub5bb\uac8c \uc801\uc6a9\ud560\uc9c0 \uc870\uae08 \ub354 \uace0\ub824\ub97c \ud574\uc57c \ub420 \uac83\uc73c\ub85c \ubcf4\uc778\ub2e4. \\n\\n**\ucd5c\uc885\uc801\uc73c\ub85c Java AWT\ub97c \uc0ac\uc6a9\ud558\uae30\ub85c \uacb0\uc815\ud588\ub2e4.**\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n[AWS Lambda](https://aws.amazon.com/ko/lambda/) \\n[Lambda Layer](https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/configuration-layers.html) \\n[Python Lambda \ud568\uc218\uc5d0 \ub300\ud55c .zip \ud30c\uc77c \uc544\uce74\uc774\ube0c \uc791\uc5c5](https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/python-package.html) \\n[No module named \'numpy.core._multiarray_umath\'](https://gist.github.com/ksmin23/0f3f243408a8497f766b43cf589fea7b) \\n[\uc0ac\ub840\ubcc4\ub85c \uc54c\uc544\ubcf8 \uc548\uc804\ud55c S3 \uc0ac\uc6a9 \uac00\uc774\ub4dc](https://techblog.woowahan.com/6217/)"},{"id":"mock-static-method","metadata":{"permalink":"/mock-static-method","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-07-30-Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30.mdx","source":"@site/blog/2023-3/2023-07-30-Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30.mdx","title":"Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30","description":"\uac1c\uc694","date":"2023-07-30T00:00:00.000Z","formattedDate":"2023\ub144 7\uc6d4 30\uc77c","tags":[{"label":"Mockito","permalink":"/tags/mockito"},{"label":"static","permalink":"/tags/static"}],"readingTime":2.635,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30","slug":"mock-static-method","tags":["Mockito","static"]},"prevItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c","permalink":"/route-image-python"},"nextItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd","permalink":"/route-image-intro"}},"content":"### \uac1c\uc694\\n\\n\uc815\uc801 \ud329\ud130\ub9ac \uba54\uc11c\ub4dc\ub97c \ubaa8\ud0b9\ud55c\ub2e4\ub294 \uac83\uc740 \uac1d\uccb4\uc9c0\ud5a5\uc801\uc778 \uad00\uc810\uc5d0\uc11c \ubcfc \ub54c \uc548\ud2f0\ud328\ud134\uc774\ub2e4. \\n\ud558\uc9c0\ub9cc \ud2b9\uc218\ud55c \uacbd\uc6b0\uc5d0\ub294 \uc815\uc801 \uba54\uc11c\ub4dc\ub97c \ubaa8\ud0b9\ud558\ub294 \uac83\uc774 \ud544\uc694\ud560 \uc218 \uc788\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\\n\uc608\ub97c \ub4e4\uc5b4 \ub808\uac70\uc2dc \ucf54\ub4dc\ub97c \ud14c\uc2a4\ud2b8 \ud55c\ub2e4\ub358\uc9c0, IO \uad00\ub828\ud55c \ubd80\ubd84\uc744 \ud14c\uc2a4\ud2b8 \ud560 \ub54c \uc815\ub9d0 \ud544\uc694\ud55c \ubd80\ubd84\uc5d0\ub9cc \uc801\uc6a9\ud560 \uc218 \uc788\uc744 \uac83\uc774\ub2e4. \\n\\n\ud504\ub85c\uc81d\ud2b8\ub97c \uc9c4\ud589\ud558\uba70 ImageIo.write \uba54\uc11c\ub4dc\uac00 \ud638\ucd9c\ub418\ub294 \uc9c0 \uac80\uc99d\uc774 \ud544\uc694\ud588\ub2e4. \\n\ud574\ub2f9 static \uba54\uc11c\ub4dc\ub97c \ud638\ucd9c\ud558\ub294 \ubd80\ubd84\uc744 \ub530\ub85c RouteImageUploader \ud074\ub798\uc2a4\ub85c \ucd5c\ub300\ud55c \ubd84\ub9ac\ud588\ub2e4. \\n\uc774\ubbf8\uc9c0 \uc800\uc7a5 \uae30\ub2a5 \uc790\uccb4\uac00 \uc678\ubd80\ub85c \ub098\uac00\ub294 \uc0c1\ud638\uc791\uc6a9\uc774\uace0, \ud638\ucd9c \ud69f\uc218\ub97c \uac80\uc0ac\ud558\ub294\ub370\ub294 mock\uc744 \uc0ac\uc6a9\ud558\ub294\uac8c \uc801\uc808\ud558\ub2e4\uace0 \ud310\ub2e8\ud588\ub2e4. \\n\\n```java\\npublic void upload(BufferedImage bufferedImage) {\\n File file = new File(\ud30c\uc77c\uacbd\ub85c);\\n try {\\n ImageIO.write(bufferedImage, ROUTE_IMAGE_FORMAT, file);\\n } catch (IOException e) {\\n throw new DrawException(IMAGE_SAVE_FAIL);\\n }\\n}\\n```\\n\\n### Mocking static methods\\n\\nMockito 3.4.0 \uc774\ud6c4\uc5d0\ub294 static method\ub97c \ubaa8\ud0b9\ud560 \uc218 \uc788\ub294 Mockito.mockStatic \uba54\uc11c\ub4dc\ub97c \uc9c0\uc6d0\ud55c\ub2e4. \\nmockStatic\uc744 \uc0ac\uc6a9\ud558\uba74 `MockedStatic`\uc774 \ubc18\ud658\ub418\ub294\ub370 \uc0ac\uc6a9 \ud6c4 \uaf2d close\ub97c \ud574\uc918\uc57c \ud55c\ub2e4. \\n\\nJUnit\uc758 @BeforeAll\ub85c \uc124\uc815\ud558\uace0 @AfterAll \uba54\uc11c\ub4dc\ub85c \uc885\ub8cc\ud558\ub294 \ubc29\ubc95\ub3c4 \uc788\uc9c0\ub9cc `MockedStatic`\uc758 \uc0c1\uc704 \uc778\ud130\ud398\uc774\uc2a4\uc778 ScopedMock\uc774 AutoCloseable\uc744 \uad6c\ud604\ud558\uace0 \uc788\uae30\uc5d0 try-with-resources\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc774 \ub354\uc6b1 \uc88b\uc740 \uac83 \uac19\ub2e4. \\n\\n```java\\n// given\\nBufferedImage bufferedImage = new BufferedImage(800, 800, BufferedImage.TYPE_INT_ARGB);\\nRouteImageUploader routeImageUploader = new RouteImageUploader();\\n\\n// expect\\ntry (MockedStatic imageIO = Mockito.mockStatic(ImageIO.class)) {\\n routeImageUploader.upload(bufferedImage);\\n imageIO.verify(\\n () -> ImageIO.write(any(BufferedImage.class), any(String.class), any(File.class)),\\n times(1)\\n );\\n}\\n```\\n\\n### \ub9c8\uce58\uba70\\n\\n\uc815\uc801 \uba54\uc11c\ub4dc\ub97c \ubaa8\ud0b9\ud558\ub294 \uac83\uc740 \uc548\ud2f0\ud328\ud134\uc774\uc73c\ub85c \uc801\uc808\ud55c \ucd94\uc0c1\ud654\ub97c \uc774\uc6a9\ud574 \ud14c\uc2a4\ud2b8 \ud558\uae30 \uc88b\uc740 \ucf54\ub4dc\ub97c \ub9cc\ub4dc\ub294 \uc5f0\uc2b5\uc744 \ud558\uc790. \\n\ud558\uc9c0\ub9cc \ucd94\uc0c1\ud654\ub97c \ud558\uba74 \ud560 \uc218\ub85d \ucf54\ub4dc\uc758 \ubcf5\uc7a1\ub3c4\ub294 \uc99d\uac00\ud55c\ub2e4. \\n\ud56d\uc0c1 \uc0c1\ud669\uc744 \uace0\ub824\ud558\uace0 \uac04\uacb0\ud568\uc744 \ud3ec\uae30\ud560 \ub9cc\ud07c \uc911\uc694\ud55c \ubd80\ubd84\uc778\uc9c0 \uc801\uc808\ud55c \ud2b8\ub808\uc774\ub4dc\uc624\ud504\ub97c \uace0\ub824\ud558\uc790. \\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n[Mocking static methods](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#static_mocks) \\n[Mockito mock static methods](https://www.baeldung.com/mockito-mock-static-methods) \\n[Enable mocking static methods in Mockito](https://github.com/mockito/mockito/issues/1013)"},{"id":"route-image-intro","metadata":{"permalink":"/route-image-intro","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd.mdx","source":"@site/blog/2023-3/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd.mdx","title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd","description":"./route.png","date":"2023-07-27T00:00:00.000Z","formattedDate":"2023\ub144 7\uc6d4 27\uc77c","tags":[{"label":"image","permalink":"/tags/image"},{"label":"awt","permalink":"/tags/awt"}],"readingTime":5.865,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd","slug":"route-image-intro","tags":["image","awt"]},"prevItem":{"title":"Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30","permalink":"/mock-static-method"},"nextItem":{"title":"\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1","permalink":"/java-spring-springboot"}},"content":"![./route.png](./route.png)\\n\\n### \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc758 \ucc45\uc784\\n\\n\uc704 \uc640\uc774\uc5b4 \ud504\ub808\uc784\uc5d0\uc11c `\uc5ec\ud589 \ud788\uc2a4\ud1a0\ub9ac`\uc640 `\uc5ec\ud589\uc5d0 \ub300\ud55c \uac10\uc0c1\uc744 \uc704\ud55c \uacbd\ub85c \uc774\ubbf8\uc9c0`\uc758 \uacbd\uc6b0 \ub124\uc774\ubc84 \uc9c0\ub3c4\ub97c \uc0ac\uc6a9\ud558\uc5ec \ud574\ub2f9 \uae30\ub2a5\uc744 \uad6c\ud604\ud560 \uc218 \uc5c6\uc73c\ub2c8 \ub2f9\uc5f0\ud788 \ub9f5 API\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \ub3c4\ud615 \uadf8\ub9ac\uae30 API(\ub124\uc774\ubc84 \ub9f5 API \uae30\uc900 Polyline)\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\ub2e4. \\n\ub530\ub77c\uc11c \uc774\ubbf8\uc9c0\ub97c \uc9c1\uc811 \uc0dd\uc131\ud558\uac70\ub098, \ud074\ub77c\uc774\uc5b8\ud2b8\uc5d0\uc11c \uc9c1\uc811 \uc704\uacbd\ub3c4\ub97c \uc774\uc6a9\ud558\uc5ec \uadf8\ub824\uc57c \ud55c\ub2e4.\\n\\n\ud574\ub2f9 \uc694\uad6c\uc0ac\ud56d\uc744 \ud574\uacb0\ud558\uae30 \uc704\ud574\uc11c\ub294 \ub2e4\uc74c\uacfc \uac19\uc740 \uae30\ub2a5\uc744 \uac00\uc9c4 \ub77c\uc774\ube0c\ub7ec\ub9ac\uac00 \ud544\uc694\ud558\ub2e4.\\n\\n- \uc774\ubbf8\uc9c0 \uc0dd\uc131\\n- \uc120\uacfc \uc810 \ud45c\ud604\\n- \ud22c\uba85\ud55c \ubc30\uacbd\uc0c9\\n\\n\ud604\uc7ac \ud074\ub77c\uc774\uc5b8\ud2b8\uc758 \ubc14\uc05c \uc77c\uc815\uacfc \uae30\ub2a5 \uad6c\ud604\uc5d0 \uc788\uc5b4 \uc57d\uac04\uc758 \uc5f0\uc0b0\uc774 \ub4e4\uc5b4\uac04\ub2e4\ub294 \ubd80\ubd84\uc744 \uace0\ub824\ud558\uc5ec \ubc31\uc5d4\ub4dc\uc5d0\uc11c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\uae30\ub85c \uacb0\uc815\uc744 \ub0b4\ub838\ub2e4.\\n\\n### \uace0\ub824\ud55c \uae30\uc220\\n\\n\ubc31\uc5d4\ub4dc\uc5d0\uc11c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc744 \ud558\uae30 \uc704\ud574 \ub2e4\uc74c\uacfc \uac19\uc740 \ub77c\uc774\ube0c\ub7ec\ub9ac \ub610\ub294 \uae30\uc220\ub4e4\uc744 \ud655\uc778\ud574 \ubcf4\uc558\ub2e4. \\n\\n- Python\uc758 Matplotlib\\n- **AWT(Abstract Window Toolkit) [\ucd5c\uc885 \uc120\ud0dd]**\\n- \uc774\ubbf8\uc9c0 \ucc98\ub9ac \ub77c\uc774\ube0c\ub7ec\ub9ac \ubc0f Java\uc5d0\uc11c \ub0b4\ubd80\uc801\uc73c\ub85c Matplotlib \uc0ac\uc6a9\ud560 \uc218 \uc788\ub294 \ub77c\uc774\ube0c\ub7ec\ub9ac (\uc6d0\ud558\ub294 \uae30\ub2a5 \uc5c6\uc74c)\\n- Java Swing, Java FX (\ub2e8\uc21c\ud55c \uc120 \uadf8\ub9ac\uae30 + \uc810 \ucc0d\uae30\ub77c \ubd88\ud544\uc694)\\n\\n## Python & Matplotlib\\n\\n\ub370\uc774\ud130 \uc2dc\uac01\ud654 \ub77c\uc774\ube0c\ub7ec\ub9ac \\n\uc774\ubbf8\uc9c0 \uc0dd\uc131 \ubc0f \ub85c\uceec\uc5d0 \uc800\uc7a5\uae4c\uc9c0 \uac78\ub9ac\ub294 \uc2dc\uac04: 0.2\ucd08 \\n\\n- \ucf54\ub4dc\uac00 \uac04\ub2e8\ud574\uc11c \uc720\uc9c0 \ubcf4\uc218\uc131\uc774 \uc88b\ub2e4. \\n- AWS Lambda \uac19\uc740 \uc11c\ubc84\ub9ac\uc2a4 \ucef4\ud4e8\ud305 \uc11c\ube44\uc2a4\ub098 FastAPI\uc640 \uac19\uc740 \uc6f9 \ud504\ub808\uc784\uc6cc\ud06c\ub85c \ucd94\uac00\uc801\uc778 API\ub97c \uad6c\ud604\ud574\uc57c \ud55c\ub2e4.\\n- Spring Boot\uc5d0\uc11c \ucd94\uac00\uc801\uc778 API \ud638\ucd9c\uc744 \ud574\uc57c\ud558\uace0, \ud655\uc7a5\uc131\uacfc \ube44\ub3d9\uae30 \ucc98\ub9ac \ub4f1 \uace0\ub824 \ud574\uc57c \ud560 \ubd80\ubd84\uc774 \ub9ce\ub2e4.\\n\\n### Java AWT \uc774\uc678\uc758 \ub77c\uc774\ube0c\ub7ec\ub9ac\\n\\nPython\uc774 \uc544\ub2cc Java\uc5d0\uc11c\uc758 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub3c4 \uace0\ub824\ub97c \ud574\ubd24\uc9c0\ub9cc \uc694\uad6c\uc0ac\ud56d\uc5d0 \uc801\ud569\ud558\uc9c0 \uc54a\uac70\ub098, \uc801\uc740 \uc694\uad6c\uc0ac\ud56d\uc5d0 \ube44\ud574 \ubb34\uac70\uc6b4 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub4e4\uc774 \ub9ce\uc544\uc11c \uc81c\uc678\ud588\ub2e4.\\n\\n\ub77c\uc774\ube0c\ub7ec\ub9ac | \uc124\uba85 | \uc81c\uc678 \uc774\uc720\\n-- | -- | --\\nSwing | AWT \uc774\ud6c4\uc5d0 \ub098\uc628 GUI \ub77c\uc774\ube0c\ub7ec\ub9ac, \ub124\uc774\ud2f0\ube0c UI\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\uace0 \ubaa8\ub4e0 \uc6b4\uc601\uccb4\uc81c \uc0c1\uc5d0\uc11c \ub3d9\uc77c\ud55c UI\ub97c \uac00\uc9c0\ub3c4\ub85d \ud568 | \uc694\uad6c\uc0ac\ud56d\uc5d0 \ube44\ud574 \ubb34\uac81\uace0 \ubcf5\uc7a1\ub3c4\uac00 \ub192\uc74c\\nJavaFX | Swing \uc774\ud6c4\uc5d0 \ub098\uc628 GUI \ub77c\uc774\ube0c\ub7ec\ub9ac, 3\ucc28\uc6d0 \uadf8\ub798\ud53d\uc744 \uc9c0\uc6d0\ud568 | \uc694\uad6c\uc0ac\ud56d\uc5d0 \ube44\ud574 \ubb34\uac81\uace0 \ubcf5\uc7a1\ub3c4\uac00 \ub192\uc74c\\n[simple-java-plot](https://github.com/yuriy-g/simple-java-plot) | AWT\ub85c \uad6c\ud604\ub41c \ud50c\ub85c\ud305 \ub77c\uc774\ube0c\ub7ec\ub9ac | AWT \uae30\ubc18\uc774\uae34 \ud558\uc9c0\ub9cc \uc9c1\uc811 AWT\ub97c \uc0ac\uc6a9\ud558\ub294 \uac83\uc5d0 \ube44\ud574 \uba54\ub9ac\ud2b8\uac00 \uc5c6\uc74c, \ucee4\uc2a4\ud140 \uc124\uc815 \uae30\ub2a5\uc774 \uc5c6\uc74c\\n[matplotlib4j](https://github.com/sh0nk/matplotlib4j) | Matplotlib\ub97c Java\uc5d0\uc11c \uc0ac\uc6a9\ud560 \uc218 \uc788\uac8c \ud558\ub294 \ub77c\uc774\ube0c\ub7ec\ub9ac | \ub0b4\ubd80\uc801\uc73c\ub85c \ud30c\uc774\uc36c \uc0ac\uc6a9\ud558\uae30\uc5d0 \ubb34\uac70\uc6c0, \ubc30\uacbd \ud22c\uba85\ud654 \uae30\ub2a5 \uc5c6\uc74c\\n\\n### Java & AWT(Abstract Window Toolkit)\\n\\n\uadf8\ub798\ud53d\uacfc \uc774\ubbf8\uc9c0\ub97c \uadf8\ub9ac\uae30 \uc704\ud55c \ub3c4\uad6c \\n\uc774\ubbf8\uc9c0 \uc0dd\uc131 \ubc0f \ub85c\uceec\uc5d0 \uc800\uc7a5\uae4c\uc9c0 \uac78\ub9ac\ub294 \uc2dc\uac04: 1.75\ucd08 \\n\\n- \ud50c\ub85c\ud305 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \uc0ac\uc6a9\ud558\ub294 \uac83\ubcf4\ub2e4 \uad6c\ud604\uc758 \ub09c\uc774\ub3c4\uac00 \ub2e4\uc18c \uc874\uc7ac\ud55c\ub2e4.\\n- \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc2dc\uac04\uc774 \ub2e4\uc18c \uc18c\uc694\ub418\uae30 \ub54c\ubb38\uc5d0 \ube60\ub978 \uc751\ub2f5 \ubc18\ud658\uc744 \uc704\ud574 \ube44\ub3d9\uae30 \ucc98\ub9ac\ub97c \uace0\ub824\ud560 \uc218 \uc788\uc744 \uac83 \uac19\ub2e4.\\n- \ucd94\uac00\uc801\uc778 api \ud638\ucd9c\uc744 \ud558\uc9c0 \uc54a\uc544\ub3c4 \ub41c\ub2e4.\\n\\n### \uae30\uc220 \uc120\ud0dd\\n\\nAWT\uc758 \uacbd\uc6b0 Matplotlib\uc5d0 \ube44\ud574 \uad6c\ud604\uc758 \ub09c\uc774\ub3c4\uac00 \ub2e4\uc18c \uc788\uace0, \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc2dc\uac04\uc774 \ub354 \ub9ce\uc774 \uac78\ub9ac\ub294 \ub2e8\uc810\uc774 \uc788\ub2e4. \\n\ud558\uc9c0\ub9cc \ucd94\uac00\uc801\uc778 api \ud638\ucd9c\uc744 \ud558\uc9c0 \uc54a\uc544\ub3c4 \ub418\ub294 \ubd80\ubd84, Python\uc744 \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \ucd94\uac00\uc801\uc778 \uc6f9 \ud504\ub808\uc784\uc6cc\ud06c\uc758 \ud559\uc2b5 \ube44\uc6a9\uc744 \uace0\ub824\ud558\uc5ec AWT\ub97c \uc0ac\uc6a9\ud558\uae30\ub85c \uacb0\uc815\ud588\ub2e4.\\n\\n### \uc720\uc9c0 \ubcf4\uc218\\n\\nAWT\ub77c\ub294 \uc0dd\uc18c\ud55c \uae30\uc220\uc744 \uc0ac\uc6a9\ud558\uae30 \ub54c\ubb38\uc5d0 \uc720\uc9c0 \ubcf4\uc218\uc131\uc744 \uc704\ud574 \ud300\uc6d0\ub4e4\uacfc \uacf5\uc720\ud558\ub294 \uac83\uc774 \uc911\uc694\ud558\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\ub530\ub77c\uc11c \ub2e4\uc74c\uacfc \uac19\uc740 \ubc29\ubc95\uc73c\ub85c \uacf5\uc720\ud558\uae30\ub85c \ud588\ub2e4. \\n\\n1. \ucf54\ub4dc \ub9ac\ubdf0\uc640 PR\uc744 \ud1b5\ud574 \uc791\uc131\ud55c AWT \ucf54\ub4dc\uc5d0 \ub300\ud55c \uc124\uba85 \ubc0f \ub9ac\ubdf0 \ubc1b\ub294\ub2e4. \\n2. AWT\ub97c \uc0ac\uc6a9\ud55c \ubd80\ubd84\uc744 \ubb38\uc11c\ud654\ud558\uc5ec \uacf5\uc720\ud55c\ub2e4.\\n\\n### \ub808\ubca8 3\ub97c \ub9c8\ubb34\ub9ac\ud558\uba70 \ub0b4\uc6a9 \ucd94\uac00\\n\\n\uae30\uc220 \uc120\ud0dd\uc744 \ud558\uae30 \uc704\ud55c \uc2e4\ud589 \uc2dc\uac04 \uce21\uc815\uc5d0 \uc624\ub958\uac00 \uc788\uc5c8\ub2e4. \\nAWT\ub97c \uc0ac\uc6a9\ud558\ub294 \ubd80\ubd84\uc5d0\uc11c \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc2e4\ud589 \uc2dc\uac04\uc744 \uc81c\uc678\ud558\uba74 \ud30c\uc774\uc36c\uacfc \ube44\uc2b7\ud55c \uc2dc\uac04\uc548\uc5d0 \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud560 \uc218 \uc788\uc5c8\ub2e4."},{"id":"java-spring-springboot","metadata":{"permalink":"/java-spring-springboot","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-07-24-\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1.mdx","source":"@site/blog/2023-3/2023-07-24-\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1.mdx","title":"\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1","description":"\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1","date":"2023-07-24T00:00:00.000Z","formattedDate":"2023\ub144 7\uc6d4 24\uc77c","tags":[{"label":"Java","permalink":"/tags/java"},{"label":"Spring Boot","permalink":"/tags/spring-boot"},{"label":"Spring","permalink":"/tags/spring"}],"readingTime":4.725,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1","slug":"java-spring-springboot","tags":["Java","Spring Boot","Spring"]},"prevItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd","permalink":"/route-image-intro"},"nextItem":{"title":"\uc6f9\uc18c\ucf13","permalink":"/websocket"}},"content":"## \uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1\\n\\n\ud300 \ud504\ub85c\uc81d\ud2b8\ub97c \uc9c4\ud589\ud558\uba74\uc11c \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1\uc744 \uc0ac\uc6a9\ud558\uac8c \ub418\uc5c8\ub2e4. \\n2.7 \ubc84\uc804\uc744 \uc0ac\uc6a9\ud560 \uc218\ub3c4 \uc788\uc5c8\uc9c0\ub9cc LTS \uae30\uac04\uacfc \ucde8\uc57d\uc810 \ud328\uce58\ub85c \uc778\ud55c \ubc84\uc804\uc5c5 \ub4f1\uc744 \uace0\ub824\ud588\uc744 \ub54c 3.1\uacfc \uc790\ubc14 17\uc744 \uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \ub354 \ud6a8\uc728\uc801\uc774\ub77c\uace0 \ud310\ub2e8\ud588\ub2e4.\\n\\n## \uc790\ubc14 \ubcc0\uacbd \uc0ac\ud56d\\n\\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 2\uae4c\uc9c0\ub294 \uc790\ubc14 11\uc744 \uc0ac\uc6a9\ud588\uc5c8\ub2e4. \\n\ub530\ub77c\uc11c \uc790\ubc14 11\ubd80\ud130 \uc790\ubc14 17\uae4c\uc9c0\uc758 \ubcc0\uacbd\uc0ac\ud56d\uc744 \uc815\uc2dd \ub9b4\ub9ac\uc988 \uae30\uc900\uc73c\ub85c \uc815\ub9ac\ud574\ubcf4\ub824\uace0 \ud55c\ub2e4.\\n\\n### Switch Expressions(Java 14)\\n\\nJava 14\uc5d0\uc11c\ub294 \uae30\uc874\uc758 Switch \ubb38\uc744 \uac04\uacb0\ud558\uac8c \uc791\uc131\ud560 \uc218 \uc788\ub294 Switch \uc2dd\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\n\\n```java\\nenum RESULT {\\n WIN, LOSE, DRAW\\n}\\n\\nRESULT result = RESULT.WIN;\\n\\nint prize = switch (result) {\\n case WIN -> 10_000_000;\\n case LOSE, DRAW -> 5_000_000;\\n\\tdefault -> 0;\\n};\\n```\\n\\n\uc8fc\uc694 \ud2b9\uc9d5\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4.\\n\\n- `->` \uc5f0\uc0b0\uc790\ub97c \uc774\uc6a9\ud558\uc5ec \uac01 case\uc5d0 \ub300\ud55c \uacb0\uacfc\ub97c \ubc14\ub85c \ubc18\ud658\ud560 \uc218 \uc788\ub2e4.\\n- case\ub97c \ucf64\ub9c8(`,`)\ub85c \uc5f0\uacb0\ud558\uc5ec \ud558\ub098\uc758 case\uc5d0 \uc5ec\ub7ec \uac12\uc744 \uc9c0\uc815\ud560 \uc218 \uc788\ub2e4.\\n- break \ubb38\uc774 \ud544\uc694 \uc5c6\ub2e4.\\n- default \ube14\ub85d\uc744 \ud1b5\ud574 \uae30\ubcf8 \uac12\uc744 \uc9c0\uc815\ud560 \uc218 \uc788\ub2e4.\\n\\n### Text Block(Java 15)\\n\\nJava 15\uc5d0\ub294 \uc0c8\ub85c\uc6b4 \ubb38\uc790\uc5f4 \ud45c\ud604\ubc29\uc2dd\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\n\uae34 \ubb38\uc790\uc5f4\uc744 + \uc5f0\uc0b0\uc790\uc758 \ub3c4\uc6c0 \uc5c6\uc774 \uac00\ub3c5\uc131\uc788\uac8c \uc791\uc131\ud560 \uc218 \uc788\ub2e4.\\n\\n```java\\n@Repository\\npublic interface PostRepository extends JpaRepository {\\n @Query(\\"\\"\\"\\n SELECT p FROM Post p\\n WHERE p.title LIKE %:keyword%\\n OR p.content LIKE %:keyword%\\n \\"\\"\\")\\n List findPostsByTitleOrContentContainingKeyword(String keyword);\\n}\\n```\\n\\n### NPE \uba54\uc2dc\uc9c0(Java 15)\\n\\n```java\\nString name = null;\\nname.chars();\\n\\n/** \\n# before\\njava.lang.NullPointerException\\n\\tat com.example.DiscountPolicyTest.test(NullPointerExceptionTest.java:61)\\n\\n# after\\nCannot invoke \\"String.chars()\\" because \\"name\\" is null\\njava.lang.NullPointerException: Cannot invoke \\"String.chars()\\" because \\"name\\" is null\\n*/\\n```\\n\\n### Record(Java 16)\\n\\nLombok\uc758 `@Data`, kotlin\uc758 data \ud074\ub798\uc2a4\uc640 \uc720\uc0ac\ud55c \uae30\ub2a5\uc744 \uc81c\uacf5\ud55c\ub2e4. \\nRecord\ub97c \uc120\uc5b8\ud558\ub294 \uacbd\uc6b0 \uc811\uadfc\uc790, \uc0dd\uc131\uc790, equals & hashcode, toString\uc774 \uc81c\uacf5\ub41c\ub2e4. \\n\ub370\uc774\ud130 \uc804\uc1a1 \uc6a9\ub3c4\ub85c \uc801\ud569\ud574 \ubcf4\uc778\ub2e4. \\n\\n```java\\npublic record PostDto(String title, String content) {\\n}\\n```\\n\\n### \ucd94\uac00\uc801\uc778 \ubcc0\uacbd\uc0ac\ud56d\\n\\n\uc774\uc678\uc5d0\ub3c4 stream\uc758 toList, \uc778\uc2a4\ud134\uc2a4\uc758 \ud0c0\uc785\uc744 \uac04\ud3b8\ud558\uac8c \uccb4\ud06c\ud558\ub294 Pattern Matching Instanceof, Sealed class \ub4f1\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\n\\n## \uc2a4\ud504\ub9c1, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 \ubcc0\uacbd \uc0ac\ud56d\\n\\n\uc2a4\ud504\ub9c1\uacfc \uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\ub3c4 \ub9ce\uc740 \ubcc0\uacbd \uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4. \\n\ub530\ub77c\uc11c \ud544\uc694\ud574\ubcf4\uc774\ub294 \uba87\uac1c \uc815\ub3c4\ub9cc \uc815\ub9ac\ud588\ub2e4. \\n\\n### \uc2a4\ud504\ub9c1 \uc694\uad6c\uc0ac\ud56d\\n\\nJava 17, Jakarta EE 9 \uc774\uc0c1\uc774\uc5b4\uc57c \ud55c\ub2e4.\\n\\n### \ub124\uc784\uc2a4\ud398\uc774\uc2a4 \ubcc0\uacbd\\n\\nJakarta EE 9\uac00 \uc801\uc6a9\ub418\uba74\uc11c \ub124\uc784\uc2a4\ud398\uc774\uc2a4\ub3c4 \uc804\ubc18\uc801\uc73c\ub85c javax -> jakarta\ub85c \ubcc0\uacbd\ub418\uc5c8\ub2e4. \\n\\n### PathPatternParser - trailing slash \ud5c8\uc6a9\ud558\uc9c0 \uc54a\uc74c\\n\\n6.0 \uc774\uc804\uc758 \uacbd\uc6b0 \uae30\ubcf8 \uc124\uc815 \uae30\uc900\uc73c\ub85c `@GetMapping(\\"/hello\\")`\uc640 `@GetMapping(\\"/hello/\\")`\uac00 \ub3d9\uc77c\ud588\ub2e4. \\n6.0 \uc774\ud6c4\uc758 PathPatternParser\uac00 \uae30\ubcf8\uc73c\ub85c \uc0ac\uc6a9\ub418\uace0, `/hello`\uc640 `/hello/`\ub294 \uc11c\ub85c \ub2e4\ub978 URL\ub85c \ub9e4\uce6d\ub41c\ub2e4. \\n\\n> PathPatternParser used by default (with the ability to opt into PathMatcher). \\n\\n### HTTP interface client\\n\\n\uc790\ubc14 \uc778\ud130\ud398\uc774\uc2a4\uc640 \uc5b4\ub178\ud14c\uc774\uc158\uc744 \uc774\uc6a9\ud558\uc5ec HTTP \uc694\uccad\uc744 \uc704\ud55c \uc11c\ube44\uc2a4\ub97c \uc815\uc758\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\n\uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [\ud1a0\ube44\ub2d8\uc758 \uac15\uc758](https://www.youtube.com/watch?v=Kb37Q5GCyZs)\ub97c \ucc38\uace0\ud558\uba74 \uc88b\uc744 \uac83 \uac19\ub2e4.\\n\\n### \uc2a4\ud504\ub9c1 \ubd80\ud2b8 \ucd5c\uc18c \uc694\uad6c\uc0ac\ud56d\\n\\nGradle 7.3, Java 17, Kotlin 1.6, Jakarta EE 9, Spring Framework 6 \\n\uc774\uc678\uc5d0\ub3c4 \uc11c\ub4dc\ud30c\ud2f0\ub4e4\uc758 \ucd5c\uc2e0 \ub9b4\ub9ac\uc988 \ubc84\uc804\uc744 \uc0ac\uc6a9\ud568\uc73c\ub85c, \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 \ud574\ub2f9 \ubc84\uc804\uc5d0 \ub9de\ub294 \ub9b4\ub9ac\uc988 \ub178\ud2b8\ub97c \ucc38\uace0\ud560 \uc218 \uc788\uc744 \uac83 \uac19\ub2e4. \\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n[\uc5b4\ub290\xa0\uc6d4\uae09\uc7c1\uc774\uac1c\ubc1c\uc790\xa0\uc758 \uc2a4\ud504\ub9c1 \ubd80\ud2b8 \ub530\ub77c\uc7a1\uae30](https://www.youtube.com/watch?v=1WT6oxchM9M) \\n[\uc790\ubc14 9-16 \uc8fc\uc694 \ud2b9\uc9d5 \ubcf5\uc2b5\ud558\uae30](https://www.youtube.com/watch?v=7SlDdzVk6GE) \\n[Java EE\uc5d0\uc11c Jakarta EE\ub85c\uc758 \uc804\ud658](https://www.samsungsds.com/kr/insights/java_jakarta.html) \\n[Spring 6\uc758 \uc0c8\ub85c\uc6b4 HTTP Interface\uc640 3 \uac00\uc9c0 REST Clients \ub77c\uc774\ube0c \ucf54\ub529](https://www.youtube.com/watch?v=Kb37Q5GCyZs) \\n[What\'s New in Spring Framework 6.x](https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-Spring-Framework-6.x) \\n[Spring Boot 3.0 Release Notes](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes) \\n[Spring Boot 3.1 Release Notes](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.1-Release-Notes)"},{"id":"websocket","metadata":{"permalink":"/websocket","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-26-WebSocket.mdx","source":"@site/blog/2023-2/2023-06-26-WebSocket.mdx","title":"\uc6f9\uc18c\ucf13","description":"\uc6f9\uc18c\ucf13","date":"2023-06-26T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 26\uc77c","tags":[{"label":"WebSocket","permalink":"/tags/web-socket"}],"readingTime":4.165,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6f9\uc18c\ucf13","slug":"websocket","tags":["WebSocket"]},"prevItem":{"title":"\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1","permalink":"/java-spring-springboot"},"nextItem":{"title":"Docusaurus","permalink":"/docusaurus"}},"content":"### \uc6f9\uc18c\ucf13\\n\\n\ub2e8\uc77c TCP \uc5f0\uacb0\uc744 \ud1b5\ud574 \ud074\ub77c\uc774\uc5b8\ud2b8\uc640 \uc11c\ubc84 \uac04 \uc804\uc774\uc911 \uc591\ubc29\ud5a5 \ud1b5\uc2e0\uc744 \uc9c0\uc6d0\ud558\ub294 \ud504\ub85c\ud1a0\ucf5c \\n\uc6f9 \ud658\uacbd\uc5d0\uc11c \uc5f0\uc18d\ub41c \ub370\uc774\ud130\ub97c \uc2e4\uc2dc\uac04\uc73c\ub85c \ucc98\ub9ac\ud560 \uc218 \uc788\ub2e4. \\n\\n\uc6f9\uc18c\ucf13\uc740 HTTP\uc758 \ud3ec\ud2b8\ub97c \uadf8\ub300\ub85c \uc0ac\uc6a9\ud558\uace0 \uac01\uac01 \ud3ec\ud2b8 80\uacfc \ud3ec\ud2b8 443\uc744 \uc0ac\uc6a9\ud558\uc5ec HTTP(ws://) \ubc0f HTTPS(wss://)\ub85c \uc11c\ubc84\uc5d0 \uc5f0\uacb0\ud55c\ub2e4. \\n\\n### \uc6f9\uc18c\ucf13 \ub4f1\uc7a5 \ubc30\uacbd\\n\\n\uc6f9\uc18c\ucf13\uc774 \ub4f1\uc7a5\ud558\uae30 \uc774\uc804, \uc2e4\uc2dc\uac04\uc131\uc744 \ubcf4\uc7a5\ud558\uae30 \uc704\ud574 Polling, Long polling, Streaming \uac19\uc740 \uae30\uc220\uc744 \uc0ac\uc6a9\ud588\uc5b4\uc57c \ud588\ub2e4. \\n\uc774\ub294 \uc2e4\uc2dc\uac04\uc131\uc774\ub098 \uc591\ubc29\ud5a5\uc131\uc744 \ub9cc\uc871\uc2dc\ud0a4\uc9c0 \ubabb\ud588\uace0, HTTP\ub97c \uc774\uc6a9\ud558\uae30 \ub54c\ubb38\uc5d0 \uacfc\ub3c4\ud55c \uc624\ubc84\ud5e4\ub4dc\uac00 \ubc1c\uc0dd\ud588\ub2e4. \\n\\n:::note polling, long polling, streaming\\n\\nPolling: \uc8fc\uae30\uc801\uc73c\ub85c \uc11c\ubc84\uc5d0 \uc694\uccad\uc744 \ubcf4\ub0b4 \uc218\uc2e0\ud560 \uc815\ubcf4\uac00 \uc788\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ubc29\ubc95\\n- \uc11c\ubc84\uc5d0\uc11c \ubcf4\ub0bc \ub0b4\uc6a9\uc774 \uc5c6\uc5b4\ub3c4 \ud074\ub77c\uc774\uc5b8\ud2b8\ub294 \uc54c \uc218 \uc5c6\ub2e4. \\n- \uacc4\uc18d\ud574\uc11c \uc694\uccad\uc744 \ubcf4\ub0b4 \ud655\uc778\uc744 \ud574\uc57c\ud558\uae30 \ub54c\ubb38\uc5d0 \uc11c\ubc84\uc5d0 \ubd88\ud544\uc694\ud55c \ubd80\ud558\ub97c \uc8fc\uc5b4\uc57c \ud55c\ub2e4. \\n\\nLong Polling: \ud074\ub77c\uc774\uc5b8\ud2b8\uc758 \uc694\uccad\uc5d0 \ub300\ud574 \uc751\ub2f5\uc744 \ubcf4\ub0b4\uc9c0 \uc54a\uace0 \uc788\ub2e4\uac00 \uc774\ubca4\ud2b8\uac00 \ubc1c\uc0dd\ud588\uc744\ub54c \uc751\ub2f5\ud558\ub294 \ubc29\ubc95\\n- \ud3f4\ub9c1 \ubc29\uc2dd\ubcf4\ub2e4 \uc11c\ubc84\uc5d0 \uc801\uc740 \ubd80\ud558\ub97c \uc904 \uc218 \uc788\uc9c0\ub9cc, \uc694\uccad\uc758 \uc8fc\uae30\uac00 \uc9e7\uc73c\uba74 \ud3f4\ub9c1\uacfc \ucc28\uc774\uac00 \uc5c6\uc5b4\uc9c4\ub2e4.\\n\\nStreaming: \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 request\ub97c \ubcf4\ub0b4\uba74 \ucee4\ub125\uc158\uc744 \ub9fa\uace0, \uc774 \ucee4\ub125\uc158\uc744 \uc720\uc9c0\ud558\uba74\uc11c \uc11c\ubc84\uac00 \uacc4\uc18d \ub370\uc774\ud130\ub97c \ubcf4\ub0b4\ub294 \ubc29\ubc95\\n- \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc11c\ubc84\uc5d0 \uc694\uccad\uc744 \ud558\uace0 \uc2f6\ub2e4\uba74 \uc0c8\ub85c\uc6b4 \ucee4\ub125\uc158\uc744 \ub9fa\uc5b4\uc57c \ud55c\ub2e4. \\n:::\\n\\n### \uc6f9\uc18c\ucf13\uc758 \ub3d9\uc791\\n\\n```mermaid\\nsequenceDiagram\\n participant Client\\n participant Server\\n Client->>Server: Handshake - Upgrade\ub97c \uc774\uc6a9\ud55c WebSocket \uc804\ud658 \uc694\uccad\\n Server->>Client: Handshake - HttpStatus 101(Switching Protocols)\\n\\n Client->>Server: \uc591\ubc29\ud5a5 \ud1b5\uc2e0\\n Server->>Client: \\n\\n Client->>Server: \uc885\ub8cc\\n Server->>Client: \\n```\\n\\n### 1. Upgrade \uc694\uccad\\n\\nWebSocket \ud504\ub85c\ud1a0\ucf5c\ub85c \uc804\ud658\ud558\ub294 HTTP \uc694\uccad\uc744 \ubcf4\ub0b8\ub2e4. \\n\uc774\ub294 HTTP\uc640 \uac19\uc774 80, 443 \ud3ec\ud2b8\ub97c \uc0ac\uc6a9\ud55c\ub2e4. \\n\uc6f9\uc18c\ucf13\uc73c\ub85c \uc804\ud658\ud558\uae30 \uc704\ud574\uc11c\ub294 Upgrade: websocket, Connection: Upgrade \ud5e4\ub354\uac00 \ud544\uc694\ud558\ub2e4. \\nSec-WebSocket-Key\ub294 \uc11c\ubc84\uc5d0\uc11c Sec-WebSocket-Accept\ub97c \uacc4\uc0b0\ud558\uc5ec \uc751\ub2f5\ud558\uace0 \uc774 \uac12\uc774 \uc608\uc0c1\ud55c \uac12\uacfc \ub2e4\ub974\uba74 \uc5f0\uacb0\uc774 \uc218\ub9bd\ub418\uc9c0 \uc54a\ub294\ub2e4. \\nSec-WebSocket-Protocol\uc758 \uacbd\uc6b0 \uc11c\ube0c\ud504\ub85c\ud1a0\ucf5c\uc758 \ubaa9\ub85d\uc73c\ub85c \uc11c\ubc84 \uce21\uc5d0\uc11c\ub294 \ud574\ub2f9 \ubaa9\ub85d \uc911 \ud558\ub098\ub97c \uc120\ud0dd\ud558\uc5ec \ubc18\ud658\ud574\uc57c \ud55c\ub2e4. \\n\ub9cc\uc57d \uc11c\ubc84\uce21\uc5d0\uc11c \uc5ec\ub7ec \uac1c \uc9c0\uc6d0\uc774 \uac00\ub2a5\ud55c \uacbd\uc6b0 \uc9c0\uc6d0 \uac00\ub2a5\ud55c \ud504\ub85c\ud1a0\ucf5c \uc911 \uccab\ubc88\uc9f8 \ud504\ub85c\ud1a0\ucf5c\uc744 \ud074\ub77c\uc774\uc5b8\ud2b8\uce21\uc73c\ub85c \ubcf4\ub0b8\ub2e4. \\n\\n```\\nGET /chats HTTP/1.1\\nHost: localhost:8080\\nUpgrade: websocket\\nConnection: Upgrade\\nSec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==\\nSec-WebSocket-Protocol: v10.stomp, v11.stomp\\nSec-WebSocket-Version: 13\\nOrigin: http://localhost:8080\\n```\\n\\n### 2. Switching Protocols\\n\\n\uc11c\ubc84\ub294 101 Switching Protocols \uc751\ub2f5\uc744 \ubc18\ud658\ud55c\ub2e4. \\nSec-WebSocket-Accept\uc740 Sec-WebSocket-Key \ub4a4\uc5d0 `258EAFA5-E914-47DA-95CA-C5AB0DC85B11`\ub97c \ubd99\uc774\uace0 SHA1\ub85c \ud574\uc2f1 \ud6c4 Base64\ub85c \uc778\ucf54\ub529\ud558\uc5ec \ubc18\ud658\ud55c\ub2e4. \\n\uc774\ub294 \uc11c\ubc84 \uc6f9\uc18c\ucf13 \ud504\ub85c\ud1a0\ucf5c\uc758 \uc9c0\uc6d0 \uc5ec\ubd80\ub97c \ud074\ub77c\uc774\uc5b8\ud2b8\uc5d0\uac8c \uba85\ud655\ud788 \uc54c\ub9ac\uae30 \uc704\ud574 \uc874\uc7ac\ud55c\ub2e4. \\n\\n```\\nHTTP/1.1 101 Switching Protocols \\nUpgrade: websocket\\nConnection: Upgrade\\nSec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=\\nSec-WebSocket-Protocol: v10.stomp\\n```\\n\\n### 3. \ud1b5\uc2e0 \ud6c4 \uc885\ub8cc\\n\\n\uc5f0\uacb0\uc774 \uc218\ub9bd\ub418\uba74 \uc6f9\uc18c\ucf13 \ud504\ub808\uc784 \ub2e8\uc704\ub85c \uc591\ubc29\ud5a5 \ud1b5\uc2e0\uc744 \ud55c\ub2e4. \\n\uc5f0\uacb0 \uc885\ub8cc\ub97c \uc6d0\ud558\ub294 \uacbd\uc6b0 \ud074\ub77c\uc774\uc5b8\ud2b8, \uc11c\ubc84 \ubaa8\ub450 \uc5f0\uacb0 \uc885\ub8cc\ub97c \uc694\uccad\ud560 \uc218 \uc788\ub2e4. \\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\nhttps://datatracker.ietf.org/doc/html/rfc6455 \\nhttps://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications \\nhttps://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_servers \\nhttps://docs.spring.io/spring-framework/reference/web/websocket.html"},{"id":"docusaurus","metadata":{"permalink":"/docusaurus","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-18-Docusaurus/2023-06-18-Docusaurus.mdx","source":"@site/blog/2023-2/2023-06-18-Docusaurus/2023-06-18-Docusaurus.mdx","title":"Docusaurus","description":"\ud300 \ube14\ub85c\uadf8 \ub610\ub294 \ubb38\uc11c\ud654\ub97c \uc704\ud574 Docusaurus\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc744 \uc815\ub9ac\ud558\ub824\uace0 \ud55c\ub2e4.","date":"2023-06-18T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 18\uc77c","tags":[{"label":"Documentation","permalink":"/tags/documentation"}],"readingTime":10.095,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"Docusaurus","slug":"docusaurus","tags":["Documentation"]},"prevItem":{"title":"\uc6f9\uc18c\ucf13","permalink":"/websocket"},"nextItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 2 \ud68c\uace0","permalink":"/woowacourse-level2-retrospective"}},"content":"\ud300 \ube14\ub85c\uadf8 \ub610\ub294 \ubb38\uc11c\ud654\ub97c \uc704\ud574 Docusaurus\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc744 \uc815\ub9ac\ud558\ub824\uace0 \ud55c\ub2e4. \\n\\n## \uc124\uce58\\n\\n[\uacf5\uc2dd \ud648\ud398\uc774\uc9c0](https://docusaurus.io/docs/installation)\uc5d0 \ub4e4\uc5b4\uac00\uc11c \ucd5c\uc2e0 \ubc84\uc804\uc744 \uc124\uce58\ud55c\ub2e4. \\n\\n```bash\\nyarn create docusaurus\\n````\\n\\n## \ubc30\ud3ec\\n\\n[\ubc30\ud3ec \uc548\ub0b4 \ubb38\uc11c](https://docusaurus.io/docs/next/deployment#deploying-to-github-pages) \\nnetlify\ub098 vercel \uac19\uc740 \uc11c\ubc84\ub9ac\uc2a4 \ud50c\ub7ab\ud3fc\uc744 \ucd94\ucc9c\ud558\uace0 \uc788\uace0, \uac04\ub2e8\ud558\uace0, \ube60\ub978 \uc2dc\uac04 \uc548\uc5d0 \ubc30\ud3ec\ub97c \ud560 \uc218 \uc788\ub2e4. \\n\uc774 \uae00\uc5d0\uc11c\ub294 github pages\ub97c \uc774\uc6a9\ud574\uc11c \ubc30\ud3ec\ud558\ub294 \ubc29\ubc95\uc744 \uc124\uba85\ud55c\ub2e4.\\n\\n### \ub808\ud3ec\uc9c0\ud1a0\ub9ac \uc0dd\uc131\\n\\ngithub pages\ub97c \uc774\uc6a9\ud558\ub824\uba74 [\uc608\uc2dc](https://github.com/greeng00se/greeng00se.github.io)\uc640 \uac19\uc774 `username.github.io` \ud615\ud0dc\uc758 \ub808\ud3ec\uc9c0\ud1a0\ub9ac\ub97c \uc0dd\uc131\ud574\uc57c \ud55c\ub2e4. \\n\uc774\ub54c organization\uc744 \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 `organization.github.io` \ud615\ud0dc\uc758 \ub808\ud3ec\uc9c0\ud1a0\ub9ac\ub97c \uc0dd\uc131\ud574\uc11c \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n### \uc124\uc815 \ud30c\uc77c \uc218\uc815\\n\\n```js title=\\"docusaurus.config\\"\\nmodule.exports = {\\n // ...\\n url: \'https://greeng00se.github.io\',\\n baseUrl: \'/\',\\n projectName: \'greeng00se.github.io\',\\n organizationName: \'greeng00se\',\\n trailingSlash: false,\\n // ...\\n};\\n```\\n\\n### \ud1a0\ud070 \uc124\uc815\\n\\ngithub action\uc744 \uc704\ud574 \ubc30\ud3ec\uc6a9 \ud1a0\ud070\uc744 \ud558\ub098 \uc0dd\uc131\ud558\uc5ec \uc0dd\uc131\ud55c \ub808\ud3ec\uc9c0\ud1a0\ub9ac\uc5d0 Repository secrets\uc73c\ub85c \uc124\uc815\ud55c\ub2e4. \\n\uc774 \uae00\uc5d0\uc11c\ub294 \ud1a0\ud070\uc744 \ud074\ub798\uc2dd \ubc29\uc2dd\uc73c\ub85c \uc0dd\uc131\ud588\uace0 \uc2a4\ucf54\ud504\ub294 [repo, user, workflow] \uc744 \uc124\uc815\ud588\ub2e4. \\n\\n![github](./github.png)\\n\\n### \ube0c\ub79c\uce58 \uc0dd\uc131\\n\\ngithub\uc5d0\uc11c gh-pages \ube0c\ub79c\uce58\ub97c \ud558\ub098 \uc0dd\uc131\ud55c\ub2e4. \\nrepository -> settings -> pages -> branch\uc5d0\uc11c \uc0dd\uc131\ud55c gh-pages\ub85c \ube0c\ub79c\uce58\ub97c \ubcc0\uacbd\ud55c\ub2e4. \\n\uc124\uc815\ud55c \ube0c\ub79c\uce58\uac00 \ubc30\ud3ec \ube0c\ub79c\uce58\uac00 \ub418\uba70, \ud574\ub2f9 \ube0c\ub79c\uce58\uc5d0 \uc788\ub294 \ud30c\uc77c\ub4e4\uc744 \uc774\uc6a9\ud574\uc11c \uc815\uc801 \uc6f9\uc0ac\uc774\ud2b8\ub97c \uc81c\uacf5\ud55c\ub2e4. \\n\\n### \uc6cc\ud06c\ud50c\ub85c \uc791\uc131\\n\\nDocusaurus 2.0 \uae30\uc900 Node.js 16.14 \uc774\uc0c1\uc758 \ubc84\uc804\uc744 \uc0ac\uc6a9\ud574\uc57c \ud569\ub2c8\ub2e4. \\n\ubc30\ud3ec\uc2dc\uc5d0\ub294 Repository secrets\uc73c\ub85c \uc124\uc815\ud55c DEPLOY_TOKEN \uc744 \uc774\uc6a9\ud569\ub2c8\ub2e4. \\n\\n```yml title=\\".github/workflows/deploy.yml\\"\\nname: blog\\n\\non:\\n push:\\n branches: [main]\\n\\njobs:\\n deploy:\\n name: Deploy to GitHub Pages\\n runs-on: ubuntu-latest\\n steps:\\n - uses: actions/checkout@v2\\n - uses: actions/setup-node@v3\\n with:\\n node-version: 18\\n cache: yarn\\n\\n - name: Install dependencies\\n run: yarn install --frozen-lockfile\\n - name: Build website\\n run: yarn build\\n\\n - name: Deploy to GitHub Pages\\n uses: peaceiris/actions-gh-pages@v3\\n with:\\n github_token: ${{ secrets.DEPLOY_TOKEN }}\\n publish_dir: ./build\\n user_name: github-actions[bot]\\n user_email: 41898282+github-actions[bot]@users.noreply.github.com\\n```\\n\\n## \ub313\uae00 \uae30\ub2a5\\n\\ngiscus\ub97c \uc774\uc6a9\ud558\uc5ec \ub313\uae00 \uae30\ub2a5\uc744 \ucd94\uac00\ud55c\ub2e4. \\n\\n### giscus \uc124\uc815\\n\\n1. \uacf5\uac1c \uc800\uc7a5\uc18c\uc5ec\uc57c \ud55c\ub2e4.\\n2. giscus \uc571\uc774 \uc124\uce58\ub418\uc5b4 \uc788\uc5b4\uc57c \ud55c\ub2e4.\\n3. Discussions \uae30\ub2a5\uc774 \ud574\ub2f9 \uc800\uc7a5\uc18c\uc5d0\uc11c \ud65c\uc131\ud654\ub418\uc5b4 \uc788\uc5b4\uc57c \ud55c\ub2e4.\\n\\n\uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [giscus](https://giscus.app/ko)\ub97c \ud655\uc778\ud558\uc790.\\n\\n### docusaurus \uc124\uc815\\n\\n[swizzling](https://docusaurus.io/ko/docs/next/swizzling)\uc744 \uc774\uc6a9\ud558\uc5ec \ucef4\ud3ec\ub10c\ud2b8\ub97c \uac10\uc2fc\ub2e4. \\n\uae30\uc874\uc5d0 \uac8c\uc2dc\ubb3c\uc744 giscus\uac00 \ud3ec\ud568\ub41c \ub9ac\uc561\ud2b8 \ucef4\ud3ec\ub10c\ud2b8\ub85c \uac10\uc2f8\ub294 \ud615\ud0dc\uac00 \ub41c\ub2e4. \\n\uc544\ub798 \uba85\ub839\uc5b4\ub97c \uc774\uc6a9\ud558\uc5ec BlogPostItem\uc744 \ucd94\ucd9c\ud560 \uc218 \uc788\ub2e4. \\n\\n```bash\\nyarn run swizzle @docusaurus/theme-classic BlogPostItem -- --wrap\\n```\\n\\n\uba85\ub839\uc5b4\ub97c \uc785\ub825\ud558\uba74 `/src/theme/BlogPostItem/index.js` \uc704\uce58\uc5d0 \ud30c\uc77c\uc774 \uc0dd\uc131\ub41c\ub2e4. \\n\ud30c\uc77c\uc758 \ub0b4\uc6a9\uc744 \uc544\ub798\uc640 \uac19\uc774 \uc218\uc815\ud558\uace0, \uc774\ub54c setAttribute \ubd80\ubd84\uc740 \uc801\uc808\ud558\uac8c \uc790\uc2e0\uc758 giscus \uc124\uc815\uc744 \uc774\uc6a9\ud55c\ub2e4. \\n\\n```js title=\\"/src/theme/BlogPostItem/index.js\\"\\nimport OriginalBlogPostItem from \\"@theme-original/BlogPostItem\\";\\nimport React, { useEffect, useRef } from \\"react\\";\\n// @ts-expect-error internal code\\nimport { useColorMode } from \\"@docusaurus/theme-common\\";\\nimport { useBlogPost } from \\"@docusaurus/theme-common/internal\\";\\n\\nconst giscusSelector = \\"iframe.giscus-frame\\";\\n\\nfunction BlogPostItem(props) {\\n const { colorMode } = useColorMode();\\n const { isBlogPostPage } = useBlogPost();\\n const giscusTheme = colorMode === \\"dark\\" ? \\"dark\\" : \\"light\\";\\n const containerRef = useRef(null);\\n\\n useEffect(() => {\\n if (!isBlogPostPage) return;\\n\\n const giscusEl = containerRef.current.querySelector(giscusSelector);\\n\\n const createGiscusEl = () => {\\n const script = document.createElement(\\"script\\");\\n\\n script.src = \\"https://giscus.app/client.js\\";\\n script.setAttribute(\\"data-repo\\", \\"teco-chat/teco-chat.github.io\\");\\n script.setAttribute(\\"data-repo-id\\", \\"R_kgDOJZ5j0Q\\");\\n script.setAttribute(\\"data-category\\", \\"Announcements\\");\\n script.setAttribute(\\"data-category-id\\", \\"DIC_kwDOJZ5j0c4CXS_Q\\");\\n script.setAttribute(\\"data-mapping\\", \\"pathname\\");\\n script.setAttribute(\\"data-strict\\", \\"0\\");\\n script.setAttribute(\\"data-reactions-enabled\\", \\"1\\");\\n script.setAttribute(\\"data-emit-metadata\\", \\"0\\");\\n script.setAttribute(\\"data-input-position\\", \\"bottom\\");\\n script.setAttribute(\\"data-theme\\", giscusTheme);\\n script.setAttribute(\\"data-lang\\", \\"ko\\");\\n script.crossOrigin = \\"anonymous\\";\\n script.async = true;\\n \\n containerRef.current.appendChild(script);\\n };\\n\\n const postThemeMessage = () => {\\n const message = {\\n setConfig: {\\n theme: giscusTheme,\\n }\\n };\\n\\n giscusEl.contentWindow.postMessage({ giscus: message }, \\"https://giscus.app\\");\\n };\\n\\n giscusEl ? postThemeMessage() : createGiscusEl();\\n }, [giscusTheme]);\\n\\n return (\\n <>\\n \\n {isBlogPostPage &&

}\\n \\n );\\n}\\n\\nexport default BlogPostItem;\\n```\\n\\n## \uc54c\uace0\ub9ac\uc544 \uc124\uc815 \ubc0f \uc9c1\uc811 \uad00\ub9ac\ud558\uae30\\n\\n\uc54c\uace0\ub9ac\uc544\ub97c \uc0ac\uc6a9\ud558\uba74 \uac80\uc0c9 \uae30\ub2a5\uc744 \ucd94\uac00\ud560 \uc218 \uc788\ub2e4. \\n\uc720\ub8cc \ud50c\ub79c\uc774\ub098 netlify\ub97c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \ud06c\ub864\ub7ec\ub97c \ub530\ub85c \uc81c\uacf5\ud574 \uc8fc\ub294 \uac83 \uac19\ub2e4. \\n\\n\ubb34\ub8cc \ud50c\ub79c\uc740 \uc9c1\uc811 \uc778\ub371\uc2a4\ub97c \uc218\uc9d1\ud558\ub294 \ubc29\ubc95\uacfc, [docsearch](https://docsearch.algolia.com/)\ub97c \uc774\uc6a9\ud558\ub294 \ubc29\ubc95\uc774 \uc788\ub2e4. \\ndocsearch\uc5d0 \ub4f1\ub85d\ud55c\ub2e4\uba74 \uc77c\uc8fc\uc77c\uc5d0 \ud55c \ubc88\uc529 \ud06c\ub864\ub9c1\uc774 \uc9c4\ud589\ub41c\ub2e4. \\n\uc774 \uae00\uc5d0\uc11c\ub294 \uc9c1\uc811 \uc778\ub371\uc2a4\ub97c \uc218\uc9d1\ud558\ub294 \ubc29\ubc95\uc744 \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n- [\uc9c1\uc811 \uc778\ub371\uc2a4 \uc218\uc9d1](https://docsearch.algolia.com/docs/legacy/run-your-own/) \\n- [\uc124\uc815 \ud30c\uc77c](https://docsearch.algolia.com/docs/legacy/config-file)\\n\\n### \uc54c\uace0\ub9ac\uc544 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc0dd\uc131 \ubc0f \ud0a4 \ud655\uc778\\n\\n\ud68c\uc6d0\uac00\uc785\uc744 \ud558\uace0 \uc0c8\ub85c\uc6b4 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc0dd\uc131\uc744 \ub204\ub978\ub2e4. \\n\uc0dd\uc131\uc744 \ub2e4 \ub9c8\uce58\uba74 \ub2e4\uc74c\uacfc \uac19\uc774 api \ud0a4\ub97c \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n![algolia](./algolia.png)\\n\\n### \ud0a4 \uc0dd\uc131\\n\\n\uc9c1\uc811 \uc778\ub371\uc2a4\ub97c \uc218\uc9d1\ud558\uae30 \uc704\ud55c \ud0a4\ub97c \uc0dd\uc131\ud55c\ub2e4. \\naddObject, editSettings, deleteIndex acl(\uc811\uadfc \uc81c\uc5b4 \ubaa9\ub85d)\uc774 \uc788\uc73c\uba74 \ub41c\ub2e4. \\n\\n![key](./key.png)\\n\\n### .env \ud30c\uc77c \uc0dd\uc131\\n\\n\ud504\ub85c\uc81d\ud2b8 \ud3f4\ub354 \uc0c1\ub2e8\uc5d0 .env \ud30c\uc77c\uc744 \uc0dd\uc131\ud55c\ub2e4. \\n\\n```bash title=\\".env\\"\\nAPPLICATION_ID=MVIU5UEMOM\\nAPI_KEY=\uc778\ub371\uc2a4_\uc0dd\uc131\uc6a9_\ud0a4\\n```\\n\\n### config \ud30c\uc77c \uc0dd\uc131\\n\\n\ub9c8\ucc2c\uac00\uc9c0\ub85c \ucd5c\uc0c1\ub2e8\uc5d0 config.json \ud30c\uc77c\uc744 \uc0dd\uc131\ud55c\ub2e4.\\n\uc124\uc815 \ud30c\uc77c\uc740 \ud574\ub2f9 [\ub9c1\ud06c](https://docsearch.algolia.com/docs/legacy/config-file)\ub97c \ucc38\uace0\ud55c\ub2e4. \\n\ub610\ub294 Docusaurus\uc758 [\uc124\uc815 \ud30c\uc77c](https://github.com/algolia/docsearch-configs/blob/master/configs/docusaurus-2.json)\uc744 \ucc38\uace0\ud55c\ub2e4.\\n\\n```json title=\\"config.json\\"\\n{\\n \\"index_name\\": \\"teco\\",\\n \\"start_urls\\": [\\n \\"https://teco-chat.github.io/\\"\\n ],\\n \\"sitemap_urls\\": [\\n \\"https://teco-chat.github.io/sitemap.xml\\"\\n ],\\n \\"sitemap_alternate_links\\": true,\\n \\"stop_urls\\": [\\n \\"/tests\\"\\n ],\\n \\"selectors\\": {\\n \\"lvl0\\": {\\n \\"selector\\": \\"(//ul[contains(@class,\'menu__list\')]//a[contains(@class, \'menu__link menu__link--sublist menu__link--active\')]/text() | //nav[contains(@class, \'navbar\')]//a[contains(@class, \'navbar__link--active\')]/text())[last()]\\",\\n \\"type\\": \\"xpath\\",\\n \\"global\\": true,\\n \\"default_value\\": \\"Documentation\\"\\n },\\n \\"lvl1\\": \\"header h1\\",\\n \\"lvl2\\": \\"article h2\\",\\n \\"lvl3\\": \\"article h3\\",\\n \\"lvl4\\": \\"article h4\\",\\n \\"lvl5\\": \\"article h5, article td:first-child\\",\\n \\"lvl6\\": \\"article h6\\",\\n \\"text\\": \\"article p, article li, article td:last-child\\"\\n },\\n \\"strip_chars\\": \\" .,;:#\\",\\n \\"custom_settings\\": {\\n \\"separatorsToIndex\\": \\"_\\",\\n \\"attributesForFaceting\\": [\\n \\"language\\",\\n \\"version\\",\\n \\"type\\",\\n \\"docusaurus_tag\\"\\n ],\\n \\"attributesToRetrieve\\": [\\n \\"hierarchy\\",\\n \\"content\\",\\n \\"anchor\\",\\n \\"url\\",\\n \\"url_without_anchor\\",\\n \\"type\\"\\n ]\\n },\\n \\"conversation_id\\": [\\n \\"833762294\\"\\n ],\\n \\"nb_hits\\": 46250\\n}\\n```\\n\\n### docker \uc774\uc6a9\ud558\uc5ec \ud06c\ub864\ub9c1\\n\\ndocker\uc640 jq\uac00 \ud544\uc694\ud558\ub2e4. \\njq\uac00 \uc124\uce58\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc73c\uba74 mac \uae30\uc900 brew\ub97c \uc774\uc6a9\ud574\uc11c \uc124\uce58\ud560 \uc218 \uc788\ub2e4. \\n\\n```bash\\nbrew install jq\\n```\\n\\n\ub2e4\uc74c \uba85\ub839\uc5b4\ub97c \uc774\uc6a9\ud558\uc5ec .env\uc640 config.json\uc744 \uc774\uc6a9\ud558\uc5ec \ud06c\ub864\ub9c1\uc744 \ud55c\ub2e4. \\n\\n```bash\\ndocker run -it --env-file=.env -e \\"CONFIG=$(cat ./config.json | jq -r tostring)\\" algolia/docsearch-scraper\\n```\\n\\n### docusaurus \uc124\uc815\\n\\n\uc804\uc5d0 \ud655\uc778\ud55c APP ID, Search-Only API KEY, IndexName\uc744 \uc774\uc6a9\ud558\uc5ec docusaurus.config \ud30c\uc77c\uc5d0 \uc124\uc815\ud55c\ub2e4. \\n\\n```js title=\\"docusaurus.config\\"\\nthemeConfig:\\n /** @type {import(\'@docusaurus/preset-classic\').ThemeConfig} */\\n ({\\n ...\\n algolia: {\\n appId: \'MVIU5UEMOM\', // Application ID\\n apiKey: \'b68f378013817d9a190df88cdde226a0\', // Search-Only API Key\\n indexName: \'teco\', // config.json\uc5d0 \uc124\uc815\ud55c \uc778\ub371\uc2a4\uba85\\n contextualSearch: true,\\n },\\n })\\n```\\n\\n## \ubd80\uac00 \uc124\uc815\\n\\n### \ud654\uba74 \uc0c1\ub2e8 Github Icon\\n\\n\ud30c\uc77c \ucd5c\ud558\ub2e8\uc5d0 \uc544\ub798 css \uad6c\ubb38\uc744 \ucd94\uac00\ud55c\ub2e4.\\n\\n```css title=\\"/src/css/custom.css\\"\\n.header-github-link:hover {\\n opacity: 0.6;\\n}\\n\\n.header-github-link:before {\\n content: \'\';\\n width: 24px;\\n height: 24px;\\n display: flex;\\n background: url(\\"data:image/svg+xml,%3Csvg viewBox=\'0 0 24 24\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cpath d=\'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\'/%3E%3C/svg%3E\\")\\n no-repeat;\\n}\\n\\nhtml[data-theme=\'dark\'] .header-github-link:before {\\n background: url(\\"data:image/svg+xml,%3Csvg viewBox=\'0 0 24 24\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cpath fill=\'white\' d=\'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\'/%3E%3C/svg%3E\\")\\n no-repeat;\\n}\\n```\\n\\nthemeconfig -> navbar\uc5d0 github link\ub97c \uc124\uc815\ud55c\ub2e4. \\n\\n```js title=\\"docusaurus.config\\"\\nnavbar: {\\n title: \'HELLO\',\\n items: [\\n {\\n href: \'https://github.com/greeng00se\',\\n position: \'right\',\\n className: \'header-github-link\',\\n \'aria-label\': \'GitHub repository\',\\n },\\n ],\\n},\\n```\\n\\n### \ucf54\ub4dc\ube14\ub7ed\\n\\njava\ub098 kotlin\uc758 \uacbd\uc6b0 \uae30\ubcf8\uc801\uc73c\ub85c \ud558\uc774\ub77c\uc774\ud305\uc744 \uc9c0\uc6d0\ud574 \uc8fc\uc9c0 \uc54a\ub294\ub2e4. \\nprism \uc124\uc815\uc744 \uc544\ub798\uc640 \uac19\uc774 \ubcc0\uacbd\ud574 \uc900\ub2e4. \\n\\n```js title=\\"docusaurus.config\\"\\nprism: {\\n theme: lightCodeTheme,\\n darkTheme: darkCodeTheme,\\n additionalLanguages: [\'java\', \'kotlin\'],\\n}\\n```\\n\\n### mermaid\\n\\nmermaid\ub97c \uc0ac\uc6a9\ud558\ub824\uba74 `@docusaurus/theme-mermaid` \ub97c \uc124\uce58\ud574\uc57c \ud55c\ub2e4.\\n\\n```bash\\nyarn add @docusaurus/theme-mermaid\\n```\\n\\n\uc124\uce58 \ud6c4 \uc544\ub798\uc640 \uac19\uc774 \uc124\uc815\uc744 \ucd94\uac00\ud55c\ub2e4.\\n\\n```js title=\\"docusaurus.config\\"\\nconst config = {\\n ...\\n markdown: {\\n mermaid: true,\\n },\\n themes: [\\n \'@docusaurus/theme-mermaid\'\\n ],\\n};\\n```\\n\\nthemeConfig\uc5d0\uc11c mermaid\uc758 \ud14c\ub9c8\ub97c \uc9c0\uc815\ud560 \uc218 \uc788\ub2e4. \\n\\n```js title=\\"docusaurus.config\\"\\nthemeConfig:\\n /** @type {import(\'@docusaurus/preset-classic\').ThemeConfig} */\\n ({\\n ...\\n mermaid: {\\n theme: {\\n light: \'neutral\', \\n dark: \'dark\'\\n },\\n },\\n }),\\n```\\n\\n### \uad6d\uc81c\ud654 \uc124\uc815\\n\\n\uad6d\uc81c\ud654 \uc124\uc815\uc744 \ud55c\ub2e4\uba74 `Older Entries` \ud615\ud0dc\uc758 \uc124\uba85\uc774 `\ub2e4\uc74c \ud398\uc774\uc9c0` \ub85c \ubcc0\uacbd\ub41c\ub2e4. \\n\uc124\uc815\ud30c\uc77c\uc5d0\uc11c i18n\uc5d0 \uc788\ub294 \ub85c\ucf00\uc77c \uc124\uc815\uc744 ko\ub85c \ubcc0\uacbd\ud558\uba74 \ub41c\ub2e4. \\n\\n```js title=\\"docusaurus.config\\"\\ni18n: {\\n defaultLocale: \\"ko\\",\\n locales: [\\"ko\\"],\\n},\\n```\\n\\n### \ube14\ub85c\uadf8 \uae00 author\\n\\n\ud300\uc6d0 \ubcc4\ub85c \ubb38\uc11c\ub97c \uad00\ub9ac\ud55c\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc774 \uc5b4\ub5a4 \ud300\uc6d0\uc774 \uae00\uc744 \uc791\uc131\ud588\ub294\uc9c0 \uc124\uc815\ud574\uc57c \ud55c\ub2e4. \\n\\n![author](./author.png)\\n\\n`authors.yml` \ud30c\uc77c\uc744 \uc774\uc6a9\ud558\uc5ec \uc0ac\uc6a9\uc790\uc5d0 \ub300\ud55c \uae30\ubcf8 \uc124\uc815\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\\n```yml title=\\"/blog/authors.yml\\"\\nherb:\\n name: \ud5c8\ube0c\\n title: Backend\\n url: https://github.com/greeng00se\\n image_url: https://github.com/greeng00se.png\\n\\nmallang:\\n name: \ub9d0\ub791\\n title: Backend\\n url: https://github.com/shin-mallang\\n image_url: https://github.com/shin-mallang.png\\n```\\n\\n\ube14\ub85c\uadf8 \uae00\uc744 \uc791\uc131\ud560 \ub54c \ub2e4\uc74c\uacfc \uac19\uc774 authors\uc5d0 \ub123\uc5b4\uc8fc\uae30\ub9cc \ud558\uba74 \ub41c\ub2e4. \\n\\n```mdx\\n---\\nslug: 1\\ntitle: Hello World\\nauthors: [herb, mallang]\\ntags: [hello, docusaurus]\\n---\\n\\n\uccab \ubc88\uc9f8 \ubb38\uc11c \ub0b4\uc6a9\\n```"},{"id":"woowacourse-level2-retrospective","metadata":{"permalink":"/woowacourse-level2-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-11-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca82 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-06-11-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca82 \ud68c\uace0.mdx","title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 2 \ud68c\uace0","description":"23\ub144\uc758 6\uc6d4\uc774 \uc624\uace0, \ub808\ubca8 2\uac00 \ub05d\ub0ac\ub2e4.","date":"2023-06-11T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 11\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":2.545,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 2 \ud68c\uace0","slug":"woowacourse-level2-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"Docusaurus","permalink":"/docusaurus"},"nextItem":{"title":"\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0","permalink":"/level2-interview-retrospective"}},"content":"23\ub144\uc758 6\uc6d4\uc774 \uc624\uace0, \ub808\ubca8 2\uac00 \ub05d\ub0ac\ub2e4. \\n\ube60\ub974\uac8c \uc9c0\ub098\uac00\uc11c \uc870\uae08 \uc544\uc27d\ub2e4. \\n\\n### \ud559\uc2b5\\n\\n\ud68c\uace0\ub97c \uc791\uc131\ud558\uae30 \uc804\uc5d0 \ub808\ubca8 2 \ub3d9\uc548 \ubcf4\ub0c8\ub358 PR\uacfc \ud68c\uace0\ub97c \ucb49 \uc77d\uc5b4\ubd24\ub2e4. \\n\ud56d\uc0c1 \uc544\uc26c\uc6b4 \uacf3\uc740 \uc788\uae30 \ub9c8\ub828\uc774\uc9c0\ub9cc, \uc798 \ud559\uc2b5\ud55c \uac83 \uac19\ub2e4. \\n\ubbf8\uc158\uc744 \ud558\uba74\uc11c \uae30\uc220\uc744 \uc5b4\ub5bb\uac8c \uc120\ud0dd\ud558\uace0, \uc801\uc6a9\ud560 \uac83\uc778\uc9c0 \uace0\ubbfc\ud558\ub294 \uacfc\uc815\uc5d0\uc11c \uaf64\ub098 \ub9ce\uc740 \uc131\uc7a5\uc744 \ud55c \uac83 \uac19\ub2e4. \\n\\n\uace0\ubbfc\uc740 \uae4a\uc5c8\uc9c0\ub9cc \uc774\ub860\uc801\uc778 \ud559\uc2b5\uc774 \ubd80\uc871\ud55c \ub808\ubca8 2\uc600\ub2e4. \\n\ubc29\ud559 \uadf8\ub9ac\uace0 \ub808\ubca8 3 \ub54c\ub294 \uc870\uae08 \ub354 \uc774\ub860\uc801\uc778 \ubd80\ubd84\uc744 \ud559\uc2b5\ud558\ub294\ub370 \uc9d1\uc911\ud574\uc57c\uaca0\ub2e4. \\n\\n\uc810\ucc28 \ud559\uc2b5 \ubc94\uc704\uac00 \ub113\uc5b4\uc9c0\uba74\uc11c \uc790\uc5f0\uc2a4\ub7fd\uac8c \ubaa8\ub974\ub294 \ub0b4\uc6a9\uc774 \uc313\uc5ec\uac04\ub2e4. \\n\ud544\uc694\ud55c \ub0b4\uc6a9\uc740 \uc55e\uc73c\ub85c \ucc9c\ucc9c\ud788 \ud559\uc2b5\ud558\uba74 \ub418\ub2c8\uae4c \uc870\uae09\ud574\uc9c0\uc9c0 \ub9d0\uc544\uc57c\uaca0\ub2e4. \\n\\n### \uc218\uba74\\n\\n\ub808\ubca8 2\ub97c \uc9c4\ud589\ud558\ub294 \ub3d9\uc548 \uc218\uba74\uc774 \ub9ce\uc774 \ubd80\uc871\ud588\uc5c8\uace0, \uacb0\uacfc\uc801\uc73c\ub85c\ub294 \uadf8\ub0a0\uc758 \ucee8\ub514\uc158\uc744 \ub9ce\uc774 \uc88c\uc6b0\ud588\ub358 \uac83 \uac19\ub2e4. \\n\uc55e\uc73c\ub85c \uc218\uba74 \uc2dc\uac04\uc744 \ub298\ub9ac\uace0, \uc88b\uc740 \uc218\uba74 \uc2b5\uad00\uc744 \uac00\uc9c0\ub3c4\ub85d \ub178\ub825\ud574\uc57c\uaca0\ub2e4. \\n\\n### \ud611\uc5c5\\n\\n\ub808\ubca8 2 \ub9c8\uc9c0\ub9c9\uc5d0 \ud611\uc5c5 \ubbf8\uc158\uc774 \uc788\uc5c8\ub2e4. \\n\uc9c0\uae08\uae4c\uc9c0\ub294 \ubc31\uc5d4\ub4dc \ud06c\ub8e8\ub4e4\uacfc \ud398\uc5b4 \ud504\ub85c\uadf8\ub798\ubc0d\uc744 \ud558\uba74\uc11c \ud611\uc5c5\uc744 \uacbd\ud5d8\ud588\ub2e4. \\n\uc774\ubc88\uc5d0\ub294 \ud504\ub7f0\ud2b8\uc5d4\ub4dc \ud06c\ub8e8\uc640 \ud611\uc5c5\uc744 \ud588\ub2e4. \uc18c\ud1b5\uc740 \uc798 \ub41c \uac83 \uac19\uc9c0\ub9cc API \uba85\uc138\ub97c \uc815\ud558\ub294 \ubd80\ubd84\uc774 \uc544\uc9c1 \ubbf8\uc219\ud55c \uac83 \uac19\ub2e4. \\n\\n\ub808\ubca8 3 \ub54c\ubd80\ud130 \ubcf8\uaca9\uc801\uc73c\ub85c \ud504\ub85c\uc81d\ud2b8\uac00 \uc2dc\uc791\ub41c\ub2e4. \\n\ud300\uc744 \uc704\ud574 \uc5b4\ub5a4 \uac83\uc744 \ud560 \uc218 \uc788\uc744\uc9c0 \uace0\ubbfc\uc744 \ub9ce\uc774 \ud574\ubd10\uc57c\uaca0\ub2e4. \\n\\n### \ub808\ubca8 2\ub97c \ub9c8\ubb34\ub9ac\ud558\uba70\\n\\n\ud68c\uace0 \uc791\uc131\ud558\uba74\uc11c \ub808\ubca8 2\uc5d0\uc11c \ud588\ub358 \uac83\ub4e4\uc744 \ubc18\ucd94\ud574 \ubd24\ub294\ub370 \ubd80\uc871\ud55c \uc810\uc740 \ub9ce\uc558\uc5b4\ub3c4 \uc88b\uc740 \ubc29\ud5a5\uc73c\ub85c \uac00\uace0 \uc788\ub294 \uac83 \uac19\ub2e4.\\n\uc77d\uace0 \uc2f6\uc740 \ucc45\ub3c4 \uc77d\uace0, \ubd80\uc871\ud55c \ubd80\ubd84 \ucc44\uc6b0\uba74\uc11c \uc26c\uc5b4\uc57c\uaca0\ub2e4."},{"id":"level2-interview-retrospective","metadata":{"permalink":"/level2-interview-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-08-\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-06-08-\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0.mdx","title":"\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0","description":"\ub808\ubca8 \uc778\ud130\ubdf0","date":"2023-06-08T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 8\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":3.435,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0","slug":"level2-interview-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 2 \ud68c\uace0","permalink":"/woowacourse-level2-retrospective"},"nextItem":{"title":"\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0","permalink":"/order-retrospective"}},"content":"### \ub808\ubca8 \uc778\ud130\ubdf0\\n\\n\ub808\ubca8 1 \ub54c\ub294 \uc900\ube44\ud574\ub454 \ub0b4\uc6a9\uc73c\ub85c \uc778\ud130\ubdf0\ub97c \uc9c4\ud589\ud574\uc11c \uadf8\ub807\uac8c \ud2b9\ubcc4\ud55c \ubd80\ubd84\uc774 \uc5c6\uc5c8\ub2e4. \\n\ub530\ub77c\uc11c \ub808\ubca8 1 \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0\ub294 \ub808\ubca8 1 \ud68c\uace0\ub97c \uc791\uc131\ud560 \ub54c \ub07c\uc6cc\ub123\uc5c8\ub2e4. \\n\uc774\ubc88\uc5d0\ub294 \ubc94\uc704\ub3c4 \uc81c\ud55c\ub418\uc5b4 \uc788\uc5b4 \uc5b4\ub5bb\uac8c \uc900\ube44\ud574\uc57c \ud560\uc9c0 \ub2f9\ud669\ud588\uace0, \ub2f5\ubcc0\uc5d0\ub3c4 \ubd80\uc871\ud55c \ubd80\ubd84\uc774 \ub9ce\uc558\uc5c8\ub2e4. \\n\uae30\uc5b5\uc774 \uc0ac\ub77c\uc9c0\uae30 \uc804\uc5d0 \ud070 \ubb38\uc81c \uc5c6\uc774 \ub2f5\ubcc0\ud55c \ub0b4\uc6a9\uc744 \uc81c\uc678\ud558\uace0, \uae30\uc5b5 \ub0a8\ub294 \uac83 \uc704\uc8fc\ub85c \uc791\uc131\ud574 \ubcf4\ub824\uace0 \ud55c\ub2e4. \\n\\n### API \ubb38\uc11c \ub3c4\uad6c \uc120\ud0dd\\n\\n\ud070 \ubb38\uc81c \uc5c6\uc774 \ub2f5\ubcc0\uc744 \ud588\ub294\ub370 \uc55e\uc73c\ub85c\ub3c4 \ud300 \ud504\ub85c\uc81d\ud2b8\ub97c \ud558\uba74\uc11c \ub3c4\uc6c0 \ub420 \uac83 \uac19\uc740 \ub0b4\uc6a9\uc774 \uc788\uc5b4\uc11c \ub0a8\uaca8\ub450\ub824\uace0 \ud55c\ub2e4. \\n\ubc31\uc5d4\ub4dc \ud300\uc6d0\uc774 \ud568\uaed8 \uc758\uc0ac\uacb0\uc815\uc744 \ud588\uace0, \ubbf8\uc158 \uae30\uac04\uc774 \uc9e7\uc740 \ub9cc\ud07c \ud300 \ucc28\uc6d0\uc5d0\uc11c \ube44\uad50\uc801 \ud559\uc2b5\ud558\uae30 \uc26c\uc6b4 Swagger\ub97c \uc120\ud0dd\ud588\ub2e4. \\n\ucd94\uac00\ub85c \ub4e4\uc5b4\uac00\ub294 \uc2dc\uac04 \ub300\ube44 \ud558\uc774 \ub9ac\ud134\uc774\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4\uace0 \ub2f5\ubcc0\ud588\ub2e4.\\n\\n\ud300 \ucc28\uc6d0\uc758 \ud559\uc2b5 \ube44\uc6a9\uc744 \uc5b8\uae09\ud574\uc11c, \ub2e4\uc74c\uacfc \uac19\uc740 \uc88b\uc740 \ud53c\ub4dc\ubc31\uc744 \ubc1b\uc558\ub2e4.\\n\\n> \ud2b9\ud788 \ud300\uc73c\ub85c \uc758\uc0ac\uacb0\uc815\ud558\ub294 \uacfc\uc815\uc744 \uacf5\uc720\ud574 \uc900 \uc810\uc774 \uc88b\uc558\uace0 \uae30\uc220\uc801 \uc758\uc0ac\uacb0\uc815 \uacfc\uc815\uc5d0\uc11c \ud300\uc758 \ud559\uc2b5\ube44\uc6a9\uc744 \uace0\ub824\ud55c \uc810\uc774 \uc88b\uc558\uc74c. \\n> \uc55e\uc73c\ub85c\ub3c4 \ud559\uc2b5 \ube44\uc6a9\uc740 \uc8fc\uc694\ud558\uac8c \uace0\ub824\ud574\uc57c \ud560 \uc0ac\ud56d\\n>\\n\\n### PUT\uacfc PATCH & \ud1a0\ud070\uacfc \uc138\uc158\\n\\nPUT\uacfc PATCH \ucc28\uc774\ub97c \uc124\uba85\ud558\ub294 \ubd80\ubd84\uc5d0\uc11c\ub294 PATCH\ub97c \uc0ac\uc6a9\ud560 \ub54c \ud398\uc774\ub85c\ub4dc\uac00 \uc801\uc5b4\uc9c4\ub2e4\ub294 \ub0b4\uc6a9\uc744 \ube7c\uba39\uace0 \ub2f5\ubcc0\uc744 \ud588\ub2e4. \\n\ud1a0\ud070\uacfc \uc138\uc158\uc758 \uacbd\uc6b0 \uae30\uc220\uc744 \uc798 \ubaa8\ub974\ub294 \uc0ac\ub78c\uc5d0\uac8c \uc124\uba85\ud574\ub2ec\ub77c\ub294 \uc81c\uc57d\uc870\uac74\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\n\\n\ud574\ub2f9 \ub0b4\uc6a9\uc744 \ub2f5\ubcc0\ud558\uba74\uc11c \uae30\uc220\uc801\uc778 \uae4a\uc774\uac00 \ub9ce\uc774 \ubd80\uc871\ud588\ub2e4\ub294 \uc0dd\uac01\uc774 \ub4e0\ub2e4. \\n\uc2e4\uc81c\ub85c \ub808\ubca8 2 \ub54c \uc774\ub860\uc801\uc778 \ud559\uc2b5 \uc2dc\uac04\uc774 \ub9e4\uc6b0 \uc801\uc5c8\uace0, \uc9d1\uc911\ub825\ub3c4 \ub9ce\uc774 \ubd80\uc871\ud588\ub2e4. \\n\uc55e\uc73c\ub85c \uc5b4\ub5bb\uac8c \uae4a\uc774\ub97c \ucc44\uc6b8\uc9c0 \uace0\ubbfc\uc744 \ud560 \uc218 \uc788\ub294 \uc9c8\ubb38\ub4e4\uc774\uc5c8\ub2e4. \\n\\n\ucd94\uac00\ub85c \uae30\uc220\uc744 \uc798 \ubaa8\ub974\ub294 \uc0ac\ub78c\uc5d0\uac8c \uc124\uba85\ud558\ub294 \uac00\uc815\uc744 \ub450\uace0 \ud559\uc2b5\uc744 \ud55c\ub2e4\uba74 \ud070 \ub3c4\uc6c0\uc774 \ub420 \uac70\ub77c\ub294 \ud53c\ub4dc\ubc31\uc744 \ubc1b\uc558\ub2e4. \\n\\n### \uadf8 \uc678 \uac1c\uc120\ud560 \uc810\\n\\n\uc778\ud130\ubdf0\ud560 \ub54c \ud2b9\uc720\uc758 \ub9d0\ubc84\ub987\uc744 \uac1c\uc120\ud558\uae30 \\n\uc0dd\uac01\ud560 \uc2dc\uac04\uc744 \uac00\uc84c\uc744 \ub54c \\"\ub2e4\uc2dc \ub9d0\uc500\ub4dc\ub824\ub3c4 \ub420\uae4c\uc694?\\"\ub77c\uace0 \ub9d0\ud558\uace0 \ub2f5\ubcc0\uc744 \uc774\uc5b4\ub098\uac00\uae30 \\n\uae30\uc220\uc801\uc73c\ub85c \uae4a\uc774\uac00 \ubd80\uc871\ud558\ub2e4\uace0 \uc0dd\uac01\uc774 \ub9ce\uc774 \ub4e4\uc5b4\uc11c \uc870\uae08 \ub354 \uae4a\uac8c \uacf5\ubd80\ud558\uace0 \uc815\ub9ac\ud558\uae30 \\n\uc774\uc804\uc5d0 \uacf5\ubd80\ud588\ub358\uac70 \ub418\ub3cc\uc544 \ubcf4\ub294 \uc2dc\uac04 \uac00\uc9c0\uae30"},{"id":"order-retrospective","metadata":{"permalink":"/order-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-04-\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-06-04-\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0","description":"\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158","date":"2023-06-04T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 4\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":4.595,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0","slug":"order-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0","permalink":"/level2-interview-retrospective"},"nextItem":{"title":"[\ud14c\ucf54\ucc57] 3. \uae30\ub2a5 \uad6c\ud604","permalink":"/tecochat-retrospective-3"}},"content":"### \uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158\\n\\n\ubc30\ud3ec \ubc0f \ud611\uc5c5\uc744 \ud560 \uc218 \uc788\ub294 \ubbf8\uc158\uc774\uc5c8\ub2e4. \\n\ub9c8\ucf54, \uc6b0\uac00, \uc6b0\ucf54, \uc6b0\uc2a4 \uadf8\ub9ac\uace0 \ub098\uae4c\uc9c0 \ud569\uccd0\uc11c 5\uba85\uc774 \ud55c \ud300\uc774 \ub418\uc5c8\ub2e4. \\n\\n### \ubc30\ud3ec\\n\\n\uc774\uc804 \ubbf8\uc158\ub4e4\uacfc \ub2ec\ub9ac AWS\ub97c \uc774\uc6a9\ud574 \ubc30\ud3ec\ub97c \ud574\uc57c \ud588\ub2e4. \\n\uac01\uc790 \ud558\ub098\uc758 EC2 \uc778\uc2a4\ud134\uc2a4\ub97c \uc81c\uacf5\ubc1b\uc744 \uc218 \uc788\uc5c8\uace0, \ud300 \ubcc4\ub85c DB\ub97c \uc704\ud55c \ucd94\uac00 \uc778\uc2a4\ud134\uc2a4\ub97c \uc81c\uacf5\ubc1b\uc558\ub2e4. \\n\ubc30\ud3ec \uc2a4\ud06c\ub9bd\ud2b8\ub97c \uc791\uc131\ud558\ub294 \uacbd\ud5d8\uc744 \ud574\ubcfc \uc218 \uc788\uc5c8\ub2e4. \\n\ubc30\ud3ec \uc2a4\ud06c\ub9bd\ud2b8\uc5d0 \uc2dc\uac04\uc744 \ub9ce\uc774 \ud22c\uc790\ud558\uc9c4 \uc54a\uc558\uace0, \ub2e4\uc74c\uacfc \uac19\uc774 \uac04\ub2e8\ud558\uac8c \uc791\uc131\ud588\ub2e4.\\n\\n```bash\\necho \\"Start Deploy Script\\"\\nREPOSITORY_NAME=/home/ubuntu/jwp-shopping-order\\nPROJECT_NAME=jwp-shopping-order\\n\\necho \\"Change Directory\\"\\ncd $REPOSITORY_NAME\\n\\necho \\"Git Pull\\"\\ngit pull origin step2\\n\\necho \\"Build\\"\\n./gradlew bootJar\\n\\necho \\"Copy, Start Server\\"\\nmv ./build/libs/$PROJECT_NAME.jar .\\n\\nPID=$(pgrep -f $PROJECT_NAME)\\n\\nif [ -n $PID ]; then\\n kill -9 $PID\\n\\tsleep 5\\nfi\\n\\nnohup java -Dspring.profiles.active=prod -jar $PROJECT_NAME.jar 1>stdout.txt 2>err.txt &\\n```\\n\\n### \ud611\uc5c5\\n\\n\uc77c\ub2e8 \uc6b0\uc2a4\ub791 \uc6b0\ucf54\uac00 \uba3c\uc800 \uc7a0\uc2e4\ub85c \uc640\uc918\uc11c \ub108\ubb34 \uac10\uc0ac\ud588\ub2e4. \\n\ubc31\uc5d4\ub4dc\uac00 \uc544\ub2cc \ub2e4\ub978 \ud06c\ub8e8\ub4e4\uacfc \ud574\ubcf4\ub294 \uccab \ud611\uc5c5\uc774\ub77c \uc57d\uac04 \ub450\uadfc\uac70\ub838\ub2e4. \\n\uc608\uc0c1\uc678\ub85c \ub300\ud654\uac00 \uc798 \ub418\uc5b4\uc11c, \ube60\ub974\uac8c \uba85\uc138\ub97c \uc815\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n**\uc5ec\ub7ec\uac00\uc9c0 \ubc29\ubc95\uc5d0 \ub300\ud55c \uc7a5\ub2e8\uc810\uc744 \uace0\ub824\ud574\ubcf4\uae30**\\n\\n\ubc31\uc5d4\ub4dc\uc640 \ud14c\uc774\ube14 \uba85\uc138\ub098 \ucfe0\ud3f0 \uad6c\ud604\uc5d0 \ub300\ud574\uc11c \uc774\uc57c\uae30\ud560 \ub54c \uc7a5\ub2e8\uc5d0 \ub300\ud574 \ub9ce\uc774 \uace0\ub824\ud558\uc9c0 \ubabb\ud55c \uac83 \uac19\ub2e4. \\n\uc870\uae08 \ub354 \uc2dc\uac04\uc744 \ub9ce\uc774 \ub4e4\uc5ec\uc11c \uc7a5\ub2e8\uc810\uc744 \uace0\ub824\ud588\ub2e4\uba74 \ub354 \uc88b\uc740 \uacb0\uacfc\ubb3c\uc774 \ub098\uc624\uc9c0 \uc54a\uc558\uc744\uae4c? \\n\uc55e\uc73c\ub85c \uc120\ud0dd\uc758 \uc21c\uac04\uc5d0\uc11c \uc870\uae08 \ub354 \uc2dc\uac04\uc744 \ub4e4\uc5ec\ubcf4\ub294 \uac83\ub3c4 \uc88b\uc744 \uac83 \uac19\ub2e4. \\n\\n### \uc0c8\ub85c \ubc30\uc6b4 \ubd80\ubd84\\n\\n**expose headers**\\n\\n\uc6f9 \ud398\uc774\uc9c0\uc5d0\uc11c Location \ud5e4\ub354\ub97c \ubc1b\uc744 \uc218 \uc5c6\ub294 \ubb38\uc81c\uac00 \uc788\uc5c8\ub2e4. \\n\uae30\ubcf8\uc801\uc73c\ub85c [\ud5c8\uc6a9 \ubaa9\ub85d\uc5d0 \uc874\uc7ac\ud558\ub294 \uc751\ub2f5\ud5e4\ub354](https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_response_header)\ub9cc \ubc18\ud658\ud55c\ub2e4\ub294 \uac83\uc744 \ubaa8\ub974\uace0 \uc788\uc5c8\ub2e4. \\n\uc774\ub97c expose headers \uc124\uc815\uc744 \ud1b5\ud574 \ud574\uacb0\ud560 \uc218 \uc788\uc5c8\ub2e4. \\nnginx \uc124\uc815\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc774 \ucd94\uac00\ud574 \uc8fc\uc5c8\ub2e4. \\n\\n```bash\\nadd_header \'Access-Control-Expose-Headers\' \'Location\'\\n```\\n\\n**\uc77d\uae30 \uc804\uc6a9 \ud2b8\ub79c\uc7ad\uc158** \\n\\n\ub2e8\uc21c \uc870\ud68c \uc694\uccad\uc5d0 \ub300\ud55c \uc131\ub2a5\uc744 \ud5a5\uc0c1\uc2dc\ucf1c\uc900\ub2e4\ub294 \uac83\uc774\ub77c\uace0 \uac04\ub2e8\ud788\ub9cc \uc54c\uace0 \uc788\uc5c8\ub2e4. \\n\uc774\ubc88\uc5d0 \ucf54\uba58\ud2b8\uac00 \ub2ec\ub824\uc11c \uc870\uae08 \ub354 \uc790\uc138\ud788 \uacf5\ubd80\ud574 \ubcf4\uae30\ub85c \ud588\ub2e4. \\nTransactional(readOnly = true)\ub97c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \ub2e4\uc74c\uacfc \uac19\uc774 \ub3d9\uc791\ud55c\ub2e4.\\n\\nsetReadOnly(true) \uc124\uc815\uc774 \ub41c Connection\uc73c\ub85c \uc5f0\uacb0\uc744 \uc2dc\ub3c4\ub97c \ud55c\ub2e4. \uc774 \uc124\uc815\uc744 \ud558\ub294 \uacbd\uc6b0 DB\ub9c8\ub2e4 \ub2e4\ub974\uac8c \ub3d9\uc791\ud55c\ub2e4.\\n- h2\uc758 Connection \uad6c\ud604\uccb4\ub294 readOnly \uc124\uc815\uc744 \ubb34\uc2dc\ud558\ub294 \ubc29\ud5a5\uc73c\ub85c \uad6c\ud604\ub418\uc5b4 Transactional \uc801\uc6a9\ub418\uc9c0 \uc54a\ub294\ub2e4. \\n- MySQL 8.0(InnoDB \uc0ac\uc6a9 \uc2dc)\uc758 \uacbd\uc6b0 \uc77d\uae30 \uc804\uc6a9\uc73c\ub85c \uc54c\ub824\uc9c4 \ud2b8\ub79c\uc7ad\uc158\uc758 \uacbd\uc6b0 \ud2b8\ub79c\uc7ad\uc158 ID\ub97c \uc124\uc815\ud558\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0 \uc870\ud68c \uc18d\ub3c4\uac00 \ub354 \ube68\ub77c\uc9c4\ub2e4.\\n\\nORM \ud504\ub808\uc784\uc6cc\ud06c\ub97c \uc0ac\uc6a9\ud55c\ub2e4\uba74 prepareTransactionalConnection\ub97c \ud638\ucd9c\ud55c\ub2e4\uace0 \ud55c\ub2e4. \\n\ucd94\uac00\ub85c \ud604\uc5c5\uc5d0\uc11c\ub294 \uace0\uac00\uc6a9\uc131 \ub0b4\uacb0\ud568\uc131 \ub4f1\uc744 \uc704\ud558\uc5ec \ud074\ub7ec\uc2a4\ud130\ub97c \uad6c\uc131\ud558\uc5ec \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0\uac00 \ub9ce\uace0, \uc774 \uacbd\uc6b0 readOnly \uc124\uc815\uc774 \ub418\uc5b4\uc788\ub2e4\uba74 \uc77d\uae30 \uc804\uc6a9 DB\ub85c \uc9c8\uc758\uac00 \ub4e4\uc5b4\uac00\uc11c \ubd80\ud558 \ubd84\uc0b0\uc758 \ud6a8\uacfc\uac00 \uc788\ub2e4\uace0 \ud55c\ub2e4. \\n\\n**DAO\uc5d0 `@Transactional` \uc801\uc6a9** \\n\\nDAO\uc5d0 \ud2b8\ub79c\uc7ad\uc158\uc744 \ubcf4\uc7a5\ud574 \ubcf4\ub294 \uac74 \uc5b4\ub5bb\uaca0\ub0d0\uace0 \ub9ac\ubdf0\uac00 \ub2ec\ub824\uc11c \uace0\ubbfc\uc744 \ub9ce\uc774 \ud588\ub2e4. \\nService \uacc4\uce35\uc5d0 \uc774\ubbf8 \ud2b8\ub79c\uc7ad\uc158\uc744 \ubcf4\uc7a5\ud574 \uc8fc\uace0 \uc788\uae30\uc5d0 \ud544\uc694 \uc5c6\uc9c0 \uc54a\uc744\uae4c \uc0dd\uac01\ud588\uc5c8\ub2e4. \\nDAO\ub97c \ub2e4\ub978 \uacf3\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub354\ub77c\ub3c4 \ud2b8\ub79c\uc7ad\uc158\uc744 \ubcf4\uc7a5\ud558\uae30 \uc704\ud574(\ud655\uc7a5\uc131 \uace0\ub824) `@Transactional`\uc744 \uc801\uc6a9\ud558\ub294 \uac83\ub3c4 \uad1c\ucc2e\uc740 \uac83 \uac19\ub2e4."},{"id":"tecochat-retrospective-3","metadata":{"permalink":"/tecochat-retrospective-3","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604.mdx","source":"@site/blog/2023-2/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604.mdx","title":"[\ud14c\ucf54\ucc57] 3. \uae30\ub2a5 \uad6c\ud604","description":"\uac1c\uc694","date":"2023-06-01T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 1\uc77c","tags":[{"label":"TecoChat","permalink":"/tags/teco-chat"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":4.005,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ud14c\ucf54\ucc57] 3. \uae30\ub2a5 \uad6c\ud604","slug":"tecochat-retrospective-3","tags":["TecoChat","Retrospective"]},"prevItem":{"title":"\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0","permalink":"/order-retrospective"},"nextItem":{"title":"\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30","permalink":"/composite"}},"content":"### \uac1c\uc694\\n\\n\uc6d0\ub798 \ubaa9\uc801\uc778 `\ud06c\ub8e8\ub4e4\uc758 \ud559\uc2b5\uc5d0 \ub3c4\uc6c0`\uc744 \uc8fc\uae30 \uc704\ud574 \uc5b4\ub5a4 \uae30\ub2a5\uc744 \ucd94\uac00\ud574\uc57c \ud560\uc9c0 \uace0\ubbfc\uc744 \ub9ce\uc774 \ud588\ub2e4. \\n\ub808\ubca8 2\uac00 \uac70\uc758 \ub05d\ub098\uac00\ub294 \uc2dc\uc810, \uadf8\ub3d9\uc548 \ud588\ub358 \uac83\uc744 \uc815\ub9ac\ud574 \ubcf4\ub824\uace0 \ud55c\ub2e4. \\n\\n### \ub098\uc758 \ucc44\ud305 \ud655\uc778\ud558\uace0 \uc774\uc5b4\ud558\ub294 \uae30\ub2a5\\n\\nGPT\uc5d0\ub3c4 \uc788\ub294 \uae30\ub2a5\uc778\ub370, \ub0b4\uac00 \uc774\uc804\uc5d0 \ud588\ub358 \ucc44\ud305\uc744 \uc774\uc5b4\ud560 \uc218 \uc788\ub294 \uae30\ub2a5\uc744 \ucd94\uac00\ud588\ub2e4. \\n\uc608\uc804\uc5d0 \uc5b4\ub5a4 \uc9c8\ubb38\uc744 \ub0a8\uacbc\ub294\uc9c0, \ub610\ud55c \ud574\ub2f9 \ucc44\ud305\uc744 \uc774\uc5b4\uc11c \ud560 \uc218 \uc788\ub2e4. \\n\\n![chat1](./chat1.png)\\n\\n### \uc88b\uc544\uc694\uc640 \ub313\uae00 \uae30\ub2a5\\n\\n\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc774 \uc9c8\ubb38\ud55c \ub0b4\uc6a9\uc5d0 \ubc18\uc751\ud560 \uc218 \uc788\ub294 \ubb34\uc5b8\uac00\uac00 \uc788\uc5c8\uc73c\uba74 \uc88b\uaca0\ub2e4\ub294 \uc758\uacac\ub4e4\uc774 \ub9ce\uc558\ub2e4. \\n\ub204\uac00 \uc88b\uc544\uc694\ub97c \ub20c\ub800\ub294\uc9c0, \uc5b4\ub5a4 \ucc44\ud305\uc774 \uc88b\uc544\uc694\ub97c \uac00\uc7a5 \ub9ce\uc774 \ubc1b\uc558\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub294 \uae30\ub2a5\uc744 \ucd94\uac00\ud588\ub2e4. \\n\ub610\ud55c \ub313\uae00 \ucd94\uac00 \ubc0f \uc0ad\uc81c \uae30\ub2a5\ub3c4 \ucd94\uac00\ud588\ub2e4.\\n\\n### \ud0a4\uc6cc\ub4dc \ucd94\ucd9c\\n\\n\uc5b4\ub5bb\uac8c \ud0a4\uc6cc\ub4dc \ucd94\ucd9c\uc744 \ud560\uc9c0 \uace0\ubbfc\uc744 \ub9ce\uc774 \ud588\ub294\ub370, \uc77c\ub2e8 GPT\ub97c \uc774\uc6a9\ud574\uc11c \ud0a4\uc6cc\ub4dc\ub97c \ucd94\ucd9c\ud558\uae30\ub85c \ud588\ub2e4. \\n\ud574\ub2f9 \ubd80\ubd84\uc740 \uccab \uc9c8\ubb38\uc5d0 \ub300\ud55c \ud0a4\uc6cc\ub4dc\ub9cc \ucd94\ucd9c\ud558\ub3c4\ub85d \ud588\ub2e4. \\n\ubc31\uc5d4\ub4dc\uc5d0\uc120 \ub9d0\ub791\uc774 \uc774\ubca4\ud2b8 \uc774\uc6a9\ud574\uc11c \uccab \ucc44\ud305 \uc694\uccad\uc774 \uc774\ub8e8\uc5b4\uc9c0\uba74, \ube44\ub3d9\uae30\ub85c \ud0a4\uc6cc\ub4dc\ub97c \ucd94\ucd9c\ud558\ub294 \uc9c8\ubb38\uc744 \ucd94\uac00\ub85c \ub0a0\ub9ac\ub3c4\ub85d \uad6c\ud604\ud558\uc600\ub2e4. \\nCSV \ud615\uc2dd\uc73c\ub85c GPT\uc5d0\uac8c \ub2f5\ubcc0\uc744 \uc785\ub825\ud574\ub2ec\ub77c\uace0 \uc694\uccad\ubc1b\ub294\ub370, \uc774 \ubd80\ubd84\uc774 \ubb38\uc81c(\ud504\ub86c\ud504\ud2b8 \uc5d4\uc9c0\ub2c8\uc5b4\ub9c1 \ubd80\ubd84\uc774 \ubc18\ud658\ub41c\ub2e4.)\uac00 \uc880 \uc788\ub294 \uac83 \uac19\uc544\uc11c \uac1c\uc120\uc774 \ud544\uc694\ud55c \uac83 \uac19\ub2e4. \\n\\n![chat2](./chat2.png)\\n\\n### \ub2e4\ub978 \ud06c\ub8e8\uc758 \ucc44\ud305 \ubcf5\uc0ac\ud574\uc11c \uc774\uc5b4\ud558\ub294 \uae30\ub2a5\\n\\n\ub2e4\ub978 \ud06c\ub8e8\ub4e4\uc758 \ucc44\ud305\uc744 \uc77d\ub2e4\uac00 \uad81\uae08\ud55c \uc810\uc774 \uc788\ub2e4\uba74 \ubcf5\uc0ac\ud574\uc11c \ubc14\ub85c \uc9c8\ubb38\uc744 \ud560 \uc218 \uc788\ub294 \uae30\ub2a5\uc744 \ucd94\uac00\ud588\ub2e4. \\n\ucc44\ud305\uc774 \ubcf5\uc0ac\ub41c \ud6c4 \ubc14\ub85c GPT\uc640 \ub300\ud654\ub97c \ud560 \uc218 \uc788\ub294 \uba54\uc778 \ud654\uba74\uc73c\ub85c \uc774\ub3d9\ud55c\ub2e4. \\n\\n### \uc0ac\uc6a9\uc131 \uace0\ub824\ud558\uae30\\n\\n![chat3](./chat3.png)\\n\\n\uc704 \ud654\uba74\uc740 \ud68c\uc6d0\uac00\uc785 \ucc3d\uc774\ub2e4. \\n\uc0ac\uc2e4 \uac00\uc7a5 \ub9c8\uc74c\uc5d0 \ub4dc\ub294 \ubd80\ubd84\uc774\uace0, \ud68c\uc6d0\uac00\uc785(\ub2c9\ub124\uc784\ub9cc \uc785\ub825\ud558\uc9c0\ub9cc)\ud560 \ub54c \uc775\uba85\uc744 \uc6d0\ud558\ub294 \uc0ac\ub78c\ub4e4\uc758 \uace0\ubbfc\uc744 \ub3c4\uc640\uc8fc\uac8c \ub054 \uc74c\uc2dd, \uacfc\uc77c, \uacfc\uc790 \ub4f1\uc758 \uc694\uc18c\ub4e4\uc744 \uc785\ub825\ud558\ub3c4\ub85d \uc720\ub3c4\ud588\ub2e4!\\n\ucd94\uac00\ub85c GPT\uc758 \ub2f5\ubcc0\uc774 \uc624\uba74 \uc790\ub3d9\uc73c\ub85c \ud654\uba74\uc744 \uc2a4\ud06c\ub864 \ud574\uc8fc\ub294 \uac83\uacfc \uac19\uc774 \uc0ac\uc6a9\uc131\uc744 \uac1c\uc120\ud574 \ubcf4\ub824\uace0 \ub178\ub825\ud588\uc9c0\ub9cc \uc27d\uc9c0 \uc54a\uc558\ub2e4. \\n\uc81c\uc77c \ud558\uace0 \uc2f6\uc740 \uac83\uc740 \uc2e4\uc81c GPT\ub97c \uc0ac\uc6a9\ud558\ub294 \uac83\ucc98\ub7fc stream/text \uac12\uc744 \ucc98\ub9ac\ud558\uace0 \uc2f6\uc740\ub370 \uc774 \ubd80\ubd84\uc740 \ubc29\ud559 \ub54c \uae30\ud68c\uac00 \ub418\uba74 \ub3c4\uc804\ud574 \ubd10\uc57c\uaca0\ub2e4. \\n\\n### \ud5a5\ud6c4 \uacc4\ud68d\\n\\n\uc2e4\uc81c \ud06c\ub8e8\ub4e4\uc774 \uc0ac\uc6a9\ud574 \uc8fc\ub294 \uc11c\ube44\uc2a4\ub97c \uc9c1\uc811 \ub9cc\ub4e4\uc5b4\ubcf4\uba74\uc11c \uc0ac\uc6a9\uc790\uc758 \uc785\uc7a5\uc5d0\uc11c \uace0\ubbfc\ub3c4 \ud558\uac8c \ub418\ub294 \uac83 \uac19\ub2e4. \\n\ud06c\ub8e8\ub4e4\uc774 \uc9c1\uc811 \uc0ac\uc6a9\ud574 \uc8fc\ub2c8\uae4c \ub108\ubb34 \uace0\ub9d9\uace0, \ud55c\ud3b8\uc73c\ub85c\ub294 \uc2e0\uae30\ud558\ub2e4. \\n\uc77c\ub2e8 \ubc29\ud559 \ub54c stream/text \uad00\ub828\ub41c \ubd80\ubd84 \ub3d9\uc791\ub418\ub3c4\ub85d \uad6c\ud604\ud574\ubcf4\ub824\uace0 \ud558\uace0, \uadf8 \uc678\uc758 \ubd80\ubd84\uc740 \uc870\uae08 \ub354 \uace0\ubbfc\ud574\uc57c\ub420 \uac83 \uac19\ub2e4."},{"id":"composite","metadata":{"permalink":"/composite","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30.mdx","source":"@site/blog/2023-2/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30.mdx","title":"\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30","description":"\uc694\uad6c\uc0ac\ud56d","date":"2023-05-26T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 26\uc77c","tags":[{"label":"Pattern","permalink":"/tags/pattern"},{"label":"Composite","permalink":"/tags/composite"}],"readingTime":4.74,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30","slug":"composite","tags":["Pattern","Composite"]},"prevItem":{"title":"[\ud14c\ucf54\ucc57] 3. \uae30\ub2a5 \uad6c\ud604","permalink":"/tecochat-retrospective-3"},"nextItem":{"title":"\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0","permalink":"/subway-retrospective"}},"content":"### \uc694\uad6c\uc0ac\ud56d\\n\\n\uc9c0\ud558\ucca0 \ubbf8\uc158\uc5d0\ub294 \ub2e4\uc74c\uacfc \uac19\uc740 \uc694\uad6c\uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4.\\n\\n- \uac70\ub9ac\ubcc4 \ucd94\uac00 \uc694\uae08 \uc815\ucc45\\n- \ub178\uc120\ubcc4 \ucd94\uac00 \uc694\uae08 \uc815\ucc45\\n- \uc5f0\ub839\ubcc4 \uc694\uae08 \ud560\uc778 \uc815\ucc45\\n\\n### \uc778\ud130\ud398\uc774\uc2a4 \uc0ac\uc6a9\\n\\n\uc694\uae08 \uc815\ucc45\uc740 \ub2e4\uc74c\uacfc \uac19\uc774 \uc778\ud130\ud398\uc774\uc2a4\ub85c \ud45c\ud604\ud560 \uc218 \uc788\ub2e4. \\n\uc694\uae08\uc744 \uacc4\uc0b0\ud558\ub294 \uba54\uc11c\ub4dc\ub294 \ucd5c\ub2e8 \uacbd\ub85c \uacc4\uc0b0\uc758 \uacb0\uacfc, \uc0ac\uc6a9\uc790\uc758 \uc815\ubcf4, \uc694\uae08\uc744 \ubc1b\uc544 \uc694\uae08\uc744 \uacc4\uc0b0\ud55c\ub2e4.\\n\\n```java\\npublic interface FarePolicy {\\n int calculate(Path path, Passenger passenger, int fare);\\n}\\n\\npublic class BaseFarePolicy implements FarePolicy { ... }\\npublic class DistanceFarePolicy implements FarePolicy { ... }\\npublic class AgeDiscountFarePolicy implements FarePolicy { ... }\\n```\\n\\n![composite1](./composite1.png)\\n\\n### \ubaa8\ub4e0 \uc694\uae08 \uc815\ucc45\uc744 \ud3ec\ud568\ud558\ub294 \uc0c8\ub85c\uc6b4 \uc694\uae08 \uc815\ucc45 \ub9cc\ub4e4\uae30\\n\\n\ub098\uba38\uc9c0 \uad6c\ud604\uccb4\ub97c \ubaa8\ub450 \uac00\uc9c0\uace0 \uc788\ub294 \ud558\ub098\uc758 \uad6c\ud604\uccb4\ub97c \ub9cc\ub4e4\uc5c8\ub2e4. \\n\uc774 \ub610\ud55c FarePolicy\ub97c \uad6c\ud604\ud55c \ud615\ud0dc\uac00 \ub418\uace0, \ud544\ub4dc\ub85c\ub294 \ub098\uba38\uc9c0 \uad6c\ud604\uccb4\ub4e4\uc744 \uac00\uc9c0\uace0 \uc788\ub2e4.\\n\\n```java\\npublic class SubwayFarePolicy implements FarePolicy {\\n\\n private final List farePolicies;\\n\\n public SubwayFarePolicy(final List farePolicies) {\\n this.farePolicies = farePolicies;\\n }\\n\\n @Override\\n public int calculate(final Path path, final Passenger passenger, final int fare) {\\n int calculatedFare = fare;\\n for (FarePolicy farePolicy : farePolicies) {\\n calculatedFare = farePolicy.calculate(path, passenger, calculatedFare);\\n }\\n return calculatedFare;\\n }\\n}\\n```\\n\\n\ub530\ub77c\uc11c \uadf8\ub9bc\uc73c\ub85c \ubcf8\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uad6c\uc870\uac00 \ub41c\ub2e4.\\n\\n![composite2](./composite2.png)\\n\\n### \uc815\ucc45\uc758 \uc21c\uc11c\\n\\n\uc9c0\ud558\ucca0 \uc694\uad6c\uc0ac\ud56d\uc740 \uc21c\uc11c\uac00 \uc911\uc694\ud588\ub2e4. \\n\uae08\uc561\uc758 \ucd1d\ud569\uc744 \uad6c\ud558\uace0, \uadf8 \ud6c4\uc5d0 \ud560\uc778 \uc815\ucc45\uc774 \ub4e4\uc5b4\uac00\uc57c\ud588\ub2e4. \\n\ub530\ub77c\uc11c \uc790\uc2dd\ub4e4\uc758 \uc21c\uc11c\ub97c \uad00\ub9ac\ud560 \ub54c \uc8fc\uc758\ub97c \uae30\uc6b8\uc5ec\uc57c \ud588\ub2e4. \\nConfiguration \ud074\ub798\uc2a4\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc774 \uc21c\uc11c\ub97c \uc9c1\uc811 \uc801\uc6a9\uc2dc\ucf30\ub2e4. \\n\\n```java\\n@Configuration\\npublic class FareConfiguration {\\n\\n @Bean\\n public FarePolicy farePolicy() {\\n return new SubwayFarePolicy(List.of(\\n new BaseFarePolicy(),\\n new DistanceFarePolicy(),\\n new AgeDiscountFarePolicy()\\n ));\\n }\\n}\\n```\\n\\n### \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc774\ub780?\\n\\n![composite3](./composite3.png)\\n\\nGOF\uc758 \ub514\uc790\uc778 \ud328\ud134 \ucc45\uc5d0\uc11c\ub294 \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc744 \ub2e4\uc74c\uacfc \uac19\uc774 \uc124\uba85\ud558\uace0 \uc788\ub2e4.\\n\\n> \ubd80\ubd84\uacfc \uc804\uccb4\uc758 \uacc4\uce35\uc744 \ud45c\ud604\ud558\uae30 \uc704\ud574 \uac1d\uccb4\ub4e4\uc744 \ubaa8\uc544 \ud2b8\ub9ac \uad6c\uc870\ub85c \uad6c\uc131\ud569\ub2c8\ub2e4. \\n\uc0ac\uc6a9\uc790\ub85c \ud558\uc5ec\uae08 \uac1c\ubcc4 \uac1d\uccb4\uc640 \ubcf5\ud569 \uac1d\uccb4\ub97c \ubaa8\ub450 \ub3d9\uc77c\ud558\uac8c \ub2e4\ub8f0 \uc218 \uc788\ub3c4\ub85d \ud558\ub294 \ud328\ud134\uc785\ub2c8\ub2e4.\\n> \\n\\n\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc740 \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud55c \uac1c\ubcc4 \uac1d\uccb4\uac00 \uc874\uc7ac\ud558\uace0, \uadf8 \uac1c\ubcc4 \uac1d\uccb4\ub4e4\uc744 \ud3ec\ud568\ud558\ub294 \ud558\ub098\uc758 \uad6c\ud604\uccb4\uac00 \ub530\ub85c \uc874\uc7ac\ud558\ub294 \ud328\ud134\uc774\ub2e4. \\n\uc774 \ub54c \uc0ac\uc6a9\uc790\ub294 \uac1c\ubcc4 \uac1d\uccb4\uc640 \ud569\uc131 \uac1d\uccb4(\uac1c\ubcc4 \uac1d\uccb4\ub4e4\uc744 \ud3ec\ud568\ud558\uace0 \uc788\ub294)\ub97c \ub611\uac19\uc774 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n### \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc758 \uad6c\uc131\uc694\uc18c\\n\\nComponent\\n\\n- \uc9d1\ud569 \uad00\uacc4\uc5d0 \uc815\uc758\ub420 \ubaa8\ub4e0 \uac1d\uccb4\uc5d0 \ub300\ud55c \uc778\ud130\ud398\uc774\uc2a4 \\n- ex) \uc694\uae08 \uc815\ucc45(FarePolicy) \\n\\nLeaf\\n\\n- \uac1c\ubcc4 \uac1d\uccb4, \uac1d\uccb4 \ud569\uc131\uc5d0 \uae30\ubcf8\uc774 \ub418\ub294 \uac1d\uccb4\uc758 \ud589\ub3d9 \\n- ex) \uac70\ub9ac \ubcc4 \uc694\uae08 \uc815\ucc45(DistanceFarePolicy) \\n\\nComposite\\n\\n- \uc5ec\ub7ec \uac1c\uc758 \uac1c\ubc1c \uac1d\uccb4\ub97c \ud3ec\ud568\ud558\ub294 \ud569\uc131 \uac1d\uccb4 \\n- ex) \uc9c0\ud558\ucca0 \uc694\uae08 \uc815\ucc45(SubwayFarePolicy) \\n\\nClient\\n\\n- \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\ub294 \ud074\ub77c\uc774\uc5b8\ud2b8\\n\\n### \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc758 \uc0ac\uc6a9\uacfc \uc8fc\uc694 \ubaa9\ud45c\\n\\n\ubd80\ubd84 - \uc804\uccb4\uc758 \uad00\uacc4\ub97c \ud45c\ud604\ud558\uace0 \uc2f6\uc744 \ub54c \\nClient \uae30\uc900\uc73c\ub85c Composite\uc640 Leaf\uc758 \ucc28\uc774\ub97c \uc54c\uc9c0 \ubabb\ud574\ub3c4 \uc798 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub3c4\ub85d \ud574\uc57c\ub420 \ub54c\\n\\n### \ud328\ud134 \uc0ac\uc6a9\uc2dc \uc8fc\uc758\ud574\uc57c\ud560 \ubd80\ubd84\\n\\n\ud328\ud134\uc740 \uacf5\ud1b5\uc73c\ub85c \uc0ac\uc6a9 \uac00\ub2a5\ud55c \uc5ed\ud560, \ucc45\uc784, \ud611\ub825\uc758 \ud15c\ud50c\ub9bf\uc774\ub2e4. \\n\ubc18\ubcf5\ub418\ub294 \ubb38\uc81c\ub97c \ud6a8\uc728\uc801\uc73c\ub85c \ud574\uacb0\ud560 \uc218 \uc788\uc9c0\ub9cc \ud328\ud134\uc5d0 \ub9e4\ubab0\ub418\uc11c\ub294 \uc548\ub41c\ub2e4. \\n\ud328\ud134\uc744 \ub9f9\ubaa9\uc801\uc73c\ub85c \uc0ac\uc6a9\ud574\uc11c\ub294 \uc548\ub418\uace0, \ud604\uc7ac\uc758 \uc694\uad6c\uc0ac\ud56d\uc5d0 \ub530\ub77c \ud328\ud134\uc744 \uc720\ub3d9\uc801\uc73c\ub85c \uc218\uc815\ud574\uac00\uba74\uc11c \uc801\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\ub2e4. \\n\ud56d\uc0c1 \ud2b8\ub808\uc774\ub4dc\uc624\ud504\ub97c \uc0dd\uac01\ud558\uc790!\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134, GoF\uc758 \ub514\uc790\uc778 \ud328\ud134 \\n\ub514\uc790\uc778 \ud328\ud134\uacfc \ud504\ub808\uc784\uc6cc\ud06c, \uc624\ube0c\uc81d\ud2b8"},{"id":"subway-retrospective","metadata":{"permalink":"/subway-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-25-\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-05-25-\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0","description":"\uc9c0\ud558\ucca0 \ubbf8\uc158","date":"2023-05-25T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 25\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":7.91,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0","slug":"subway-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30","permalink":"/composite"},"nextItem":{"title":"\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5","permalink":"/accidental-duplication"}},"content":"### \uc9c0\ud558\ucca0 \ubbf8\uc158\\n\\n\uc810\uc810 \uc77c\uc815\uc774 \ub9ce\uc544\uc9c0\ub294 \ub290\ub08c\uc774 \ub4e4\uba74\uc11c \ud68c\uace0\uac00 \ub2a6\uc5b4\uc9c4\ub2e4. \\n\uc9c0\ud558\ucca0 \ubbf8\uc158\uc740 \ubc00\ub9ac\ub791 \ud398\uc5b4\ub97c \uc9c4\ud589\ud588\ub2e4. \\n\uac04\ub2e8\ud55c CRUD\ub9cc \uc788\ub358 \uc774\uc804 \ubbf8\uc158\ub4e4\uacfc \ub2ec\ub9ac, \uc870\uae08 \ubcf5\uc7a1\ud55c \ub3c4\uba54\uc778 \uc694\uad6c\uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4. \\n\uc774\ub54c API, \ud14c\uc774\ube14, \ub3c4\uba54\uc778 \uc124\uacc4\ub97c \ud574\uc57c \ud588\ub294\ub370 \uc5b4\ub5a4 \uac83\ubd80\ud130 \ud574\uc57c \ud560\uc9c0 \uace0\ubbfc\uc744 \ub9ce\uc774 \ud588\ub2e4. \\nAPI\uc640 \ud14c\uc774\ube14 \uad6c\uc870\ub97c \uc6b0\ub9ac\uac00 \uc815\ud560 \uc218 \uc788\ub294 \uc0c1\ud669\uc774\uc5c8\uace0, \ub3c4\uba54\uc778 \ub85c\uc9c1\uc774 \ubcf5\uc7a1\ud588\uae30 \ub54c\ubb38\uc5d0 \ub3c4\uba54\uc778\uc744 \uba3c\uc800 \uad6c\ud604\ud588\ub2e4.\\n\\n**\ub178\uc120\uc758 \uad6c\uac04 \ucd94\uac00 \ubc0f \uc0ad\uc81c**\\n\\n\ub178\uc120\uc744 \uc800\uc7a5\ud558\ub294 \ubc29\ubc95\uc5d0 \ub300\ud574\uc11c \ubc00\ub9ac\uc640 \uc774\uc57c\uae30\ub97c \ub098\ub234\ub2e4.\\n\\n1. \uad6c\uac04\uc744 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \uc804\ubd80 \uc81c\uac70\ud558\uace0 \uc804\ubd80 \ucd94\uac00\ud558\ub294 \ubc29\ubc95\\n2. \ubcc0\uacbd\ub41c \uc694\uc18c\ub9cc \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \ubc18\uc601\ud558\ub294 \ubc29\ubc95\\n\\n\ud398\uc5b4 \uc2dc\uac04\uc774 \uc9e7\uc544\uc11c \ub354\uc6b1 \uac04\ub2e8\ud55c 1\ubc88\uc744 \uc120\ud0dd\ud588\uace0, \uc2dc\uac04 \ub0b4 \uc694\uad6c\uc0ac\ud56d\uc744 \ub9cc\uc871\uc2dc\ud0a4\uae30 \uc704\ud574 \ub354 \uac04\ub2e8\ud558\uac8c \uad6c\ud604\ud558\ub294 \ubc29\ubc95\uc744 \uc120\ud0dd\ud558\ub294 \uac83\ub3c4 \uc88b\uc740 \ud2b8\ub808\uc774\ub4dc\uc624\ud504\uc600\ub358 \uac83 \uac19\ub2e4. \\n\ucd94\ud6c4 \ud398\uc5b4\uac00 \ub05d\ub098\uace0 \ub9ac\ubdf0\uc5b4\uc778 \uc11c\ube0c\uc6e8\uc774\uac00 \uc77c\ubd80\ubd84\ub9cc \ubc18\uc601\ud558\ub294 \uac83\uc73c\ub85c \uac1c\uc120\ud574 \ubcf4\ub294 \uac83\ub3c4 \uc88b\uc744 \uac83 \uac19\ub2e4\uace0 \ucf54\uba58\ud2b8\ub97c \ub0a8\uaca8\uc8fc\uc154\uc11c \ucd94\uac00 \ubc0f \uc81c\uac70\ub41c \uc694\uc18c\ub9cc \ubc18\uc601\ud558\ub3c4\ub85d \ubcc0\uacbd\ud588\ub2e4.\\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n\ubbf8\uc158\uc758 \ub09c\uc774\ub3c4\uac00 \uc62c\ub77c\uac04 \ub9cc\ud07c, \ud398\uc5b4 \ud560 \ub550 \ucee8\ub514\uc158 \uad00\ub9ac\ub3c4 \uc798\ud558\ub824\uace0 \ub178\ub825\ud558\uace0 \ubbf8\uc158 \ud560 \ub54c\ub3c4 \uc9d1\uc911\ud574\uc11c \uc798 \ub05d\ub0b8 \uac83 \uac19\ub2e4. \\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\ub97c \uc9c4\ud589\ud558\uba74\uc11c \uc54c\uc544\uc57c \ud558\ub294 \uac8c \ub9ce\uc544\uc9c0\uba74\uc11c \uac00\ub054 \uc870\ubc14\uc2ec\uc744 \uac00\uc9c8 \ub54c\uac00 \uc788\ub294 \uac83 \uac19\uc740\ub370, \uc870\ubc14\uc2ec\uc744 \uacbd\uacc4\ud560 \ud544\uc694\uac00 \uc788\uc744 \uac83 \uac19\ub2e4. \\n\ubd80\uc871\ud55c \ubd80\ubd84\uc740 \uc778\uc815\ud558\uace0, \uc55e\uc73c\ub85c \ub098\uc544\uac00\uc57c\uaca0\ub2e4. \\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654**\\n\\n\uc694\uae08 \uc815\ucc45\uc740 \uae30\ubcf8\uc694\uae08 \uc815\ucc45, \uac70\ub9ac\ubcc4 \uc694\uae08 \uc815\ucc45, \uc5f0\ub839\ubcc4 \ud560\uc778 \uc815\ucc45\uc774 \uc788\uc5c8\ub2e4. \\n\uc694\uae08\uc744 \ub354\ud558\ub294 \ubd80\ubd84\uacfc, \ud560\uc778\ud558\ub294 \ubd80\ubd84\uc774 \uc788\uc5b4\uc11c \uc774 \ub458\uc744 \ubd84\ub9ac\ud560\uae4c \uc0dd\uac01\ud588\uc9c0\ub9cc, \uc774 \uc815\ub3c4 \ud06c\uae30\uc758 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0\uc11c\ub294 \uc624\ud788\ub824 \ubd84\ub9ac\ud558\uc9c0 \uc54a\uace0 \ud558\ub098\ub85c \ud569\uce58\ub294 \uac8c \ub354 \uc88b\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\ub610\ud55c \ubd84\ub9ac\ud558\uc9c0 \uc54a\ub294\ub2e4\uba74 \uc815\ucc45\uc758 \uc21c\uc11c\uac00 \uc911\uc694\ud55c\ub370, \uc5f0\ub839\ubcc4 \ud560\uc778 \uc815\ucc45\uc744 \ub9c8\uc9c0\ub9c9\uc5d0 \ub450\uc5b4\uc57c \ud588\uae30 \ub54c\ubb38\uc5d0 \ucc45\uc784 \uc5f0\uc1c4 \ud328\ud134\ub3c4 \uace0\ub824\ub97c \ud588\uc9c0\ub9cc \uc870\uae08 \ub354 \uac04\uacb0\ud574 \ubcf4\uc774\ub294 \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc744 \uc120\ud0dd\ud588\ub2e4.\\n\\n**\ub3c4\uba54\uc778\uc5d0 \ud2b9\uc815 \uae30\uc220\uc758 \uc758\uc874\uc131\uc744 \ubd84\ub9ac**\\n\\n\ucc98\uc74c\uc5d0 \ub3c4\uba54\uc778 \ud328\ud0a4\uc9c0\uc5d0 jgrapht \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \uc758\uc874\ud558\uace0 \uc788\ub294 \ud074\ub798\uc2a4\ub97c \ub450\uc5b4\uc11c \ub3c4\uba54\uc778 \ud328\ud0a4\uc9c0\uac00 jgrapht\uc640 \uac15\uacb0\ud569\uc774 \ub418\uc5b4\ubc84\ub838\ub2e4. \\n\ub530\ub77c\uc11c \ub3c4\uba54\uc778 \ud328\ud0a4\uc9c0 \ub0b4\uc5d0\ub294 \uacbd\ub85c \uac80\uc0c9\uc5d0 \ub300\ud55c \uc778\ud130\ud398\uc774\uc2a4\ub97c \ub450\uace0, \uc138\ubd80 \uad6c\ud604\uc740 \ub3c4\uba54\uc778 \ud328\ud0a4\uc9c0 \uc678\ubd80\ub85c \ubd84\ub9ac\ud588\ub2e4. \\n\ucd5c\ub300\ud55c \uac04\uacb0\ud558\uac8c \uad6c\ud604\ud55c\ub2e4\uace0 \uc0dd\uac01\uc744 \ud574\ub3c4, \uc774\ub7f0 \ubd80\ubd84\uc740 \uc778\ud130\ud398\uc774\uc2a4\ub97c \ub450\uc5b4 \uacb0\ud569\uc744 \ud53c\ud558\ub294 \uac83\uc774 \uc88b\uc744 \uac83 \uac19\ub2e4.\\n\\n:::note \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\\n\\n\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc740 \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud55c \uac1c\ubcc4 \uac1d\uccb4\uac00 \uc874\uc7ac\ud558\uace0, \uadf8 \uac1c\ubcc4 \uac1d\uccb4\ub4e4\uc744 \ud3ec\ud568\ud558\ub294 \ud558\ub098\uc758 \uad6c\ud604\uccb4\uac00 \ub530\ub85c \uc874\uc7ac\ud558\ub294 \ud328\ud134\uc774\ub2e4. \\n\uc774\ub54c \uc0ac\uc6a9\uc790\ub294 \uac1c\ubcc4 \uac1d\uccb4\uc640 \ud569\uc131 \uac1d\uccb4(\uac1c\ubcc4 \uac1d\uccb4\ub4e4\uc744 \ud3ec\ud568\ud558\uace0 \uc788\ub294)\ub97c \ub611\uac19\uc774 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n:::\\n\\n**\uc778\uc218 \ud14c\uc2a4\ud2b8 \uc791\uc131**\\n\\n\uc778\uc218 \ud14c\uc2a4\ud2b8\ub294 \uc0ac\uc6a9\uc790 \uc2a4\ud1a0\ub9ac \uc2dc\ub098\ub9ac\uc624 \uae30\ubc18 \ud14c\uc2a4\ud2b8\ub2e4. \\n\ube0c\ub77c\uc6b4\uc774 \ud574\uc8fc\uc2e0 \uac15\uc758 + \uc720\ud29c\ube0c\uc5d0 \uc788\ub294 \ube0c\ub77c\uc6b4\uc758 \uac15\uc758\ub97c \ubcf4\uace0 \uc9c0\ud558\ucca0 \ubbf8\uc158\uc5d0 \uc778\uc218 \ud14c\uc2a4\ud2b8\ub97c \uc801\uc6a9\ud574 \ubcf4\uc558\ub2e4. \\n\uba54\uc11c\ub4dc, \ubcc0\uc218\uba85\uc744 \uc804\ubd80 \ud55c\uae00\ub85c \uc791\uc131\ud588\ub294\ub370 \uc804\uccb4\uc801\uc778 \ud750\ub984\uc744 \uc54c\uae30 \ud3b8\ud558\uace0 \uc77d\uae30\ub3c4 \uc88b\uc558\ub2e4. \\n\uadf8\ub9ac\uace0 \uc778\uc218 \ud14c\uc2a4\ud2b8\uc5d0 \ud544\uc694\ud55c Steps\ub97c \ub9cc\ub4dc\ub294 \uacfc\uc815\uc774 \ub108\ubb34 \uc7ac\ubc0c\uc5c8\ub2e4.\\n\\n\uacb0\uacfc\ub294 \uc544\ub798\uc640 \uac19\ub2e4.\\n\\n```java\\n@Nested\\npublic class \ub178\uc120\uc744_\uc804\uccb4_\uc870\ud68c\ud560_\ub54c {\\n\\n @Test\\n void \uc0c1\ud589\uc885\uc810\uc5ed_\ubd80\ud130_\ud558\ud589\uc885\uc810\uc5ed\uc73c\ub85c_\uc815\ub82c\ub41c_\uacb0\uacfc\ub97c_\ubc18\ud658\ud55c\ub2e4() {\\n // given\\n \ub178\uc120_\uc0dd\uc131_\uc694\uccad(\\"2\ud638\uc120\\", \\"\ucd08\ub85d\\", 0);\\n \ub178\uc120\uc5d0_\uad6c\uac04\uc774_\uc874\uc7ac\ud558\uc9c0_\uc54a\uc744_\ub54c_\ucd08\uae30_\uad6c\uac04_\uc0dd\uc131_\uc694\uccad(\\"2\ud638\uc120\\", \\"\uc7a0\uc2e4\\", \\"\uc7a0\uc2e4\uc0c8\ub0b4\\", 5);\\n \uad6c\uac04_\uc0dd\uc131_\uc694\uccad(\\"2\ud638\uc120\\", \\"\uc7a0\uc2e4\uc0c8\ub0b4\\", \\"\uc885\ud569\uc6b4\ub3d9\uc7a5\\", \uc624\ub978\ucabd, 5);\\n\\n \ub178\uc120_\uc0dd\uc131_\uc694\uccad(\\"9\ud638\uc120\\", \\"\uace0\ub3d9\\", 0);\\n \ub178\uc120\uc5d0_\uad6c\uac04\uc774_\uc874\uc7ac\ud558\uc9c0_\uc54a\uc744_\ub54c_\ucd08\uae30_\uad6c\uac04_\uc0dd\uc131_\uc694\uccad(\\"9\ud638\uc120\\", \\"\ubd09\uc740\uc0ac\\", \\"\uc885\ud569\uc6b4\ub3d9\uc7a5\\", 3);\\n \uad6c\uac04_\uc0dd\uc131_\uc694\uccad(\\"9\ud638\uc120\\", \\"\uc885\ud569\uc6b4\ub3d9\uc7a5\\", \\"\uc0bc\uc804\\", \uc624\ub978\ucabd, 7);\\n\\n // when\\n final var \uc870\ud68c_\uacb0\uacfc = \ub178\uc120_\uc804\uccb4_\uc870\ud68c_\uc694\uccad();\\n\\n // then\\n \uc694\uccad_\uacb0\uacfc\uc758_\uc0c1\ud0dc\ub97c_\uac80\uc99d\ud55c\ub2e4(\uc870\ud68c_\uacb0\uacfc, \uc815\uc0c1_\uc694\uccad);\\n \ub178\uc120_\uc804\uccb4_\uc870\ud68c_\uacb0\uacfc\ub97c_\ud655\uc778\ud55c\ub2e4(\\n \uc870\ud68c_\uacb0\uacfc,\\n \ub178\uc120_\uc815\ubcf4(\\"2\ud638\uc120\\", \\"\ucd08\ub85d\\", 0, \\"\uc7a0\uc2e4\\", \\"\uc7a0\uc2e4\uc0c8\ub0b4\\", \\"\uc885\ud569\uc6b4\ub3d9\uc7a5\\"),\\n \ub178\uc120_\uc815\ubcf4(\\"9\ud638\uc120\\", \\"\uace0\ub3d9\\", 0, \\"\ubd09\uc740\uc0ac\\", \\"\uc885\ud569\uc6b4\ub3d9\uc7a5\\", \\"\uc0bc\uc804\\")\\n );\\n }\\n}\\n```\\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n**\uc758\uacac \uc870\uc728\ud558\uae30**\\n\\n\ubc00\ub9ac\uac00 \ud544\uc694\ud55c \ubd80\ubd84\uc5d0\uc11c \uc758\uacac\uc744 \uc801\uadf9\uc801\uc73c\ub85c \ub0b4\uc918\uc11c \uc9c4\ud589\uc774 \uc218\uc6d4\ud588\ub2e4. \\n\uc758\uc0ac\uc18c\ud1b5\uc774 \ub9e4\uc6b0 \uc798 \ub3fc\uc11c \uc88b\uc558\uace0 \ub355\ubd84\uc5d0 \uc2dc\uac04 \ub0b4\uc5d0 \uc694\uad6c\uc0ac\ud56d\uc744 \ub9cc\uc871\ud574 \ubbf8\uc158\uc744 \uc81c\ucd9c\ud560 \uc218 \uc788\uc5c8\ub358 \uac83 \uac19\ub2e4. \\n\\n**\uaf3c\uaf3c\ud558\uac8c \ucf54\ub529\ud558\uae30**\\n\\n\ubc00\ub9ac\ub294 \ucf54\ub529\uc744 \uc5c4\uccad \uaf3c\uaf3c\ud558\uac8c \ud558\ub294 \uac83 \uac19\ub2e4. \\n\ubcc0\uc218\uba85, \uba54\uc11c\ub4dc\uba85\uc744 \uc911\uc694\ud558\uac8c \uc0dd\uac01\ud588\uace0, \uc88b\uc740 \ubcc0\uc218\uba85\uc744 \uc798 \uc9d3\ub294 \uac83 \uac19\ub2e4. \\n\ub610\ud55c \ucf54\ub529\ud560 \ub54c \ub0b4\uac00 \ud3c9\uc18c\uc5d0 \uc0ac\uc6a9\ud558\ub294 \ucf54\ub529 \ucee8\ubca4\uc158\uc5d0 \ub9de\ucdb0\uc8fc\ub294 \uac83 \uac19\uc544\uc11c \ud398\uc5b4 \ud560 \ub54c \ud3b8\ud588\ub2e4! \\n\\n**\ud3b8\ud55c \ubd84\uc704\uae30**\\n\\n\uc804\uccb4\uc801\uc73c\ub85c \ud398\uc5b4 \ud560 \ub54c \ud3b8\ud558\uac8c \uc9c4\ud589\ud588\ub358 \uac83 \uac19\ub2e4. \\n\uc77c\uc815\ub3c4 \uadf8\ub807\uace0, \ud398\uc5b4 \uc9c4\ud589\ud560 \ub54c\ub3c4 \uadf8\ub807\uace0 \ud070 \ubb38\uc81c\uac00 \uc5c6\uc5c8\ub358 \uac83 \uac19\uc544\uc11c \uc88b\uc558\ub2e4. \\n\ub098\ub294 \uacfc\uc5f0 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc5d0\uac8c \ud3b8\ud55c \uc0ac\ub78c\uc77c\uae4c?"},{"id":"accidental-duplication","metadata":{"permalink":"/accidental-duplication","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5.mdx","source":"@site/blog/2023-2/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5.mdx","title":"\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5","description":"\uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc0c1\ud488 \ucd94\uac00\uc640 \uc0c1\ud488 \uc218\uc815\uc5d0 \ub300\ud55c \uc694\uad6c\uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4.","date":"2023-05-24T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 24\uc77c","tags":[{"label":"DTO","permalink":"/tags/dto"}],"readingTime":7.525,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5","slug":"accidental-duplication","tags":["DTO"]},"prevItem":{"title":"\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0","permalink":"/subway-retrospective"},"nextItem":{"title":"\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0","permalink":"/shopping-cart-retrospective"}},"content":"\uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc0c1\ud488 \ucd94\uac00\uc640 \uc0c1\ud488 \uc218\uc815\uc5d0 \ub300\ud55c \uc694\uad6c\uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4. \\n\uc694\uccad\uc5d0 \ub2f4\uae34 Body\ub97c \ud1b5\ud574 \uc804\ub2ec\ubc1b\uc740 \uac12\uc744 DTO\ub85c \ub9e4\ud551\ud558\uc5ec \ucd94\uac00\uc640 \uc218\uc815\uc744 \ud588\ub2e4.\\n\\n### \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\uc5d0\uc11c\uc758 \uc0c1\ud488 \ucd94\uac00 \ubc0f \uc218\uc815\\n\\n![\uc911\ubcf51](./\uc911\ubcf51.png)\\n\\n\ud074\ub798\uc2a4\uba85\uc744 \uc81c\uc678\ud558\uace0 \ud544\ub4dc\uc640 \uac80\uc99d\ub85c\uc9c1 \uadf8 \uc678 \ubaa8\ub4e0\uac8c \uac19\uc740 DTO\ub97c \ubcf4\uba70 \uc911\ubcf5\uc774\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\ud558\uc9c0\ub9cc \ubc18\ub300\ub85c \uc6a9\ub3c4\uac00 \ub2e4\ub974\uae30 \ub54c\ubb38\uc5d0 \uc911\ubcf5\uc774 \uc544\ub2c8\ub77c\uace0 \uc0dd\uac01\ud558\uae30\ub3c4 \ud588\ub2e4. \\n\uc704 \uacbd\uc6b0\ub294 \uc911\ubcf5\uc77c\uae4c? \uc911\ubcf5\uc774 \uc544\ub2d0\uae4c?\\n\\n\uc774 \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c \ub2e4\uc74c\uacfc \uac19\uc740 \ub9ac\ubdf0\ub97c \ubc1b\uc558\ub2e4.\\n\\n> `ProductSaveRequest`\uc640 `ProductUpdateRequest`\uac00 \uc644\uc804\ud788 \ub3d9\uc77c\ud55c\ub370, \uc7ac\uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc744\uae4c? \ub77c\ub294 \ub9ac\ubdf0\ub97c \ub0a8\uacbc\uc5c8\uc5b4\uc694. \uc0ac\uc2e4 \uc0dd\uc131\uacfc \uc218\uc815\uc740 \uc11c\ub85c \ub2ec\ub77c\uc9c8 \uac1c\uc5f0\uc131\uc774 \ub192\uc544\uc11c \ubbf8\ub9ac \ubd84\ub9ac\ud574\ub193\ub294 \uac8c \ub354 \uc88b\uc740 \ubc29\ubc95\uc774\uae34 \ud55c\ub370, \uadf8\ub798\ub3c4 \uc911\ubcf5\uc740 \uc2eb\uc5b4\uc11c \uc800\ub3c4 \uc694\uc998 \uc774\ub7f0\uc800\ub7f0 \ubc29\ubc95\ub4e4\uc744 \uc2dc\ub3c4\ud574\ubcf4\ub294 \uc911 \uc785\ub2c8\ub2e4. \ud5c8\ube0c\ub294 \uc774 \ubd80\ubd84\uc5d0 \ub300\ud574 \uc5b4\ub5a4 \uc0dd\uac01\uc744 \uac00\uc9c0\uace0 \uc788\uc744\uc9c0 \uad81\uae08\ud558\ub124\uc694 \u314e\u314e\\n> \\n\\n\uc9c8\ubb38\uc5d0 \ub300\ud574 \uc544\ub798\uc640 \uac19\uc774 \ub2f5\ubcc0\uc744 \ud588\ub2e4.\\n\\n> \uc800\uc7a5\uacfc \uc218\uc815\ud560 \ub54c \ud544\uc694\ud55c \ud544\ub4dc\uac12\uc774 \ub3d9\uc77c\ud558\uc5ec \ud604\uc7ac \uad6c\uc870\uc5d0\uc11c\ub294 \ud558\ub098\ub85c \uc0ac\uc6a9\ud574\ub3c4 \ub41c\ub2e4\uace0 \uc0dd\uac01\uc744 \ud558\uc9c0\ub9cc, \ub9d0\uc500\ud574\uc8fc\uc2e0\ub300\ub85c \uc694\uad6c\uc0ac\ud56d\uc774 \ubcc0\uacbd\ub41c\ub2e4\uba74 \ub2ec\ub77c\uc9c8 \uac00\ub2a5\uc131\uc774 \ub192\ub2e4\uace0 \ud310\ub2e8\ud558\uc600\uc2b5\ub2c8\ub2e4!\\n> \\n\\n### \uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5\\n\\n\ub85c\ubc84\ud2b8 \ub9c8\ud2f4\ub2d8\uc774 \uc9d1\ud544\ud558\uc2e0 \ud074\ub9b0 \uc544\ud0a4\ud14d\ucc98\ub294 \uc544\ub798\uc640 \uac19\uc774 \uc911\ubcf5\uc744 \uc5ec\ub7ec\uac00\uc9c0 \uc885\ub958\ub85c \ub098\ub204\uc5b4 \uc124\uba85\ud558\uace0 \uc788\ub2e4.\\n\\n- \uc9c4\uc9dc \uc911\ubcf5: \ud55c \uc778\uc2a4\ud134\uc2a4\uac00 \ubcc0\uacbd\ub418\uba74, \ub3d9\uc77c\ud55c \ubcc0\uacbd\uc744 \uadf8 \uc778\uc2a4\ud134\uc2a4\uc758 \ubaa8\ub4dc \ubcf5\uc0ac\ubcf8\uc5d0 \ubc18\ub4dc\uc2dc \uc801\uc6a9\ud574\uc57c \ud55c\ub2e4.\\n- \uac70\uc9d3\ub41c \uc911\ubcf5, \uc6b0\ubc1c\uc801 \uc911\ubcf5: \uc911\ubcf5\uc73c\ub85c \ubcf4\uc774\ub294 \ub450 \ucf54\ub4dc \uc601\uc5ed\uc774 \uac01\uc790\uc758 \uacbd\ub85c\ub85c \ubc1c\uc804\ud55c\ub2e4\uba74, \uc989 \uc11c\ub85c \ub2e4\ub978 \uc18d\ub3c4\uc640 \ub2e4\ub978 \uc774\uc720\ub85c \ubcc0\uacbd\ub41c\ub2e4\uba74 \uc774 \ub450 \ucf54\ub4dc\ub294 \uc9c4\uc9dc \uc911\ubcf5\uc774 \uc544\ub2c8\ub2e4.\\n\\n\ucd94\uac00\uc640 \uc218\uc815\uc740 \ucd08\uae30\uc5d0\ub294 \uc911\ubcf5\uc73c\ub85c \ubcf4\uc774\uc9c0\ub9cc \ucd08\uae30 \uc0dd\uc131\uc2dc\uc5d0\ub9cc \uae30\uc785\ud558\ub294 \ub370\uc774\ud130\ub4e4\uc774 \ucd94\uac00\ub418\uac70\ub098, \uc2dc\uac04\uc774 \uc9c0\ub098\uba74\uc11c \uc11c\ub85c \ub2ec\ub77c\uc9c8 \uac00\ub2a5\uc131\uc774 \ub192\uc544\uc9c4\ub2e4.\\n\uadf8\ub807\uae30 \ub54c\ubb38\uc5d0 \uc704 \uc0c1\ud669\uc740 \uc6b0\ubc1c\uc801 \uc911\ubcf5\uc73c\ub85c \ubcf4\uc778\ub2e4. \uadf8\ub798\ub3c4 \uc911\ubcf5\uc744 \uc81c\uac70\ud574\ubcfc \uc218 \uc788\uc9c0 \uc54a\uc744\uae4c?\\n\\n### \ud558\ub098\ub85c \uc0ac\uc6a9\ud558\ub294 \uac74 \uc548\uc88b\uc544\ubcf4\uc774\uace0, \uc911\ubcf5\uc740 \uc81c\uac70\ud558\uace0 \uc2f6\uc740 \ub9c8\uc74c\\n\\n\uc9c0\uae08\uc740 \ucd94\uac00, \uc218\uc815 2\uac00\uc9c0 \uacbd\uc6b0 \ubc16\uc5d0 \uc5c6\uc9c0\ub9cc \uc870\uae08 \ub354 \ubcf5\uc7a1\ud55c \uc694\uad6c\uc0ac\ud56d\uc774 \uc8fc\uc5b4\uc838\uc11c 10\uac00\uc9c0 \uacbd\uc6b0\ub85c \uc785\ub825\uc744 \ubc1b\uc73c\uba74 \uc5b4\ub5bb\uac8c \ud574\uc57c\ud560\uae4c? \\n\uc11c\ube44\uc2a4 \uacc4\uce35\uc5d0\uc11c\ub3c4 \uacc4\uce35\uc758 \ubd84\ub9ac\ub97c \uc704\ud574\uc11c \ub2e4\ub978 DTO\ub97c \uc0ac\uc6a9\ud558\uace0 \uc788\ub2e4\uba74 20\uac1c\uc758 DTO\ub97c \ub9cc\ub4e4\uc5b4\uc57c \ud560\uae4c? \\n\ub9ac\ubdf0\uc5b4\uac00 \uc54c\ub824\uc900 \uc758\uc874 \uc5ed\uc804\uc744 \uc774\uc6a9\ud55c \ubc29\ubc95\uc744 \ud1b5\ud574 \uc774\ub97c \ud574\uacb0\ud574\ubcf4\uc790! \\n\\n### \uc911\ubcf5 \uc81c\uac70 \uc804 \ucf54\ub4dc\\n\\n\ud604\uc7ac \ucf54\ub4dc\uc5d0\uc11c\ub294 \uc544\ub798\uc640 \uac19\uc740 \uad6c\uc870\ub85c \ub418\uc5b4\uc788\ub2e4. \\nController\uc640 Service\uc5d0\uc11c \uc800\uc7a5, \uc218\uc815\ud560 \ub54c \uac01\uac01\uc758 DTO\ub97c \uc0ac\uc6a9\ud558\uace0 \uc788\ub2e4.\\n\ud604\uc7ac DTO\ub294 controller, service \ud328\ud0a4\uc9c0 \ub0b4\uc5d0 \uc788\ub294 \uac83\uc774 \uc544\ub2c8\ub77c dto\ub77c\ub294 \ud328\ud0a4\uc9c0\uc5d0 \uc704\uce58\ud558\uace0 \uc788\ub2e4.\\n\\n```java\\n\u251c\u2500\u2500 controller\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductController\\n\u251c\u2500\u2500 service\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductService\\n\u251c\u2500\u2500 dto\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductSaveRequest\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductUpdateRequest\\n```\\n\\n![\uc911\ubcf52](./\uc911\ubcf52.png)\\n\\n### \uc778\ud130\ud398\uc774\uc2a4 \uc791\uc131\ud558\uae30\\n\\n![\uc911\ubcf53](./\uc911\ubcf53.png)\\n\\n\uc11c\ube44\uc2a4 \ub808\uc774\uc5b4\uc5d0\uc11c \ud544\uc694\ub85c \ud558\ub294 \uac12\ub4e4\uc744 \uc778\ud130\ud398\uc774\uc2a4\ub85c \uc815\uc758\ud55c\ub2e4. \\n\ud574\ub2f9 \uc778\ud130\ud398\uc774\uc2a4\ub294 \uc11c\ube44\uc2a4\uc5d0\uc11c \uc0ac\uc6a9\ud558\uae30 \ub54c\ubb38\uc5d0 service \ud328\ud0a4\uc9c0 \ub0b4\ubd80\ub85c \uc62e\uaca8\uc900\ub2e4.\\n\\n```java\\n\u251c\u2500\u2500 controller\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductController\\n\u251c\u2500\u2500 service\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductService\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductSaveRequest\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductUpdateRequest\\n```\\n\\n```java\\npublic interface ProductSaveRequest {\\n\\n String getName();\\n\\n String getImage();\\n\\n Long getPrice();\\n}\\n\\n// ProductService\\npublic Long save(final ProductSaveRequest request) {\\n final Product product = new Product(request.getName(), request.getImage(), request.getPrice());\\n return productDao.saveAndGetId(product);\\n}\\n```\\n\\n### \uad6c\ud604\uccb4 \uc791\uc131\ud558\uae30\\n\\n![\uc911\ubcf54](./\uc911\ubcf54.png)\\n\\n\uc704\uc5d0\uc11c \uc791\uc131\ud55c \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud558\ub294 \ud074\ub798\uc2a4\ub97c \uc791\uc131\ud55c\ub2e4. \\n\uc694\uccad\uc740 ProductRequest \ud074\ub798\uc2a4\ub85c \ubc1b\uace0, \uc11c\ube44\uc2a4\uc5d0 \uc804\ub2ec\ud560 \ub550 \ud574\ub2f9 \uc778\ud130\ud398\uc774\uc2a4\uc758 \uba85\uc138\ub9cc \ub9de\ucd94\uba74 \ubb38\uc81c\uc5c6\uc774 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n```java\\n\u251c\u2500\u2500 controller\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductController\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductRequest\\n\u251c\u2500\u2500 service\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductService\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductSaveRequest\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductUpdateRequest\\n```\\n\\n```java\\npublic class ProductRequest implements ProductSaveRequest, ProductUpdateRequest {\\n\\n @NotBlank(message = \\"\uc774\ub984\uc740 \uacf5\ubc31\uc77c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.\\")\\n @Size(min = 1, max = 100, message = \\"\uc774\ub984\uc740 \ucd5c\uc18c {min}\uc790 \uc774\uc0c1, {max}\uc790 \uc774\ud558\uc5ec\uc57c \ud569\ub2c8\ub2e4.\\")\\n private final String name;\\n\\n @NotBlank(message = \\"\uc774\ubbf8\uc9c0\ub294 \uacf5\ubc31\uc77c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.\\")\\n private final String image;\\n\\n @Range(message = \\"\uac00\uaca9\uc740 \ucd5c\uc18c {min}\uc6d0 \uc774\uc0c1, {max}\uc6d0 \uc774\ud558\uc5ec\uc57c \ud569\ub2c8\ub2e4.\\")\\n private final long price;\\n\\n public ProductRequest(final String name, final String image, final long price) {\\n this.name = name;\\n this.image = image;\\n this.price = price;\\n }\\n\\n @Override\\n public String getName() {\\n return name;\\n }\\n\\n @Override\\n public String getImage() {\\n return image;\\n }\\n\\n @Override\\n public long getPrice() {\\n return price;\\n }\\n}\\n\\n// ProductController\\n@PostMapping(\\"/products\\")\\npublic ResponseEntity save(@Valid @RequestBody final ProductRequest request) {\\n final Long id = productService.save(request);\\n return ResponseEntity.created(URI.create(\\"/products/\\" + id)).build();\\n}\\n```\\n\\n### \uc815\ub9ac\\n\\n\uc704\uc640 \uac19\uc774 \uad6c\ud604\ud55c\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uc7a5\uc810\uc744 \uc5bb\uc744 \uc218 \uc788\ub2e4. \\n\\n1. Service\uc5d0\uc11c \ubaa8\ub4e0 \ud074\ub77c\uc774\uc5b8\ud2b8 \uc694\uccad\uc5d0 \ub300\ud55c DTO\ub97c \uc54c\uc9c0 \uc54a\uc544\ub3c4 \ub41c\ub2e4.\\n2. \uacf5\ud1b5\uc801\uc73c\ub85c \uc0ac\uc6a9\ud558\ub294 DTO\ub97c \uc81c\uc678\ud558\uace0 DTO \ud328\ud0a4\uc9c0\uc5d0 \ub300\ud55c \uacb0\ud569\ub3c4\uac00 \ub0ae\uc544\uc9c0\uace0, \uac01 \ub808\uc774\uc5b4\uc758 \uc751\uc9d1\ub3c4\uac00 \uc99d\uac00\ud55c\ub2e4.\\n3. \uc694\uccad \uac1d\uccb4\ub9cc \ub2e4\ub974\uace0 \uc11c\ube44\uc2a4\uc5d0\uc11c \ub3d9\uc77c\ud55c \ud589\uc704\ub97c \uc218\ud589\ud558\ub294 \uacbd\uc6b0 \uc911\ubcf5\uc744 \uc81c\uac70\ud560 \uc218 \uc788\ub2e4.\\n\\n\uc704 \ubc29\ubc95\uc744 \uc9c0\uae08 \ubbf8\uc158\uc5d0\uc11c \ubc14\ub85c \uc801\uc6a9\ud560\uae4c \ud558\ub2e4\uac00, \ub098\uc911\uc5d0 \ud544\uc694\ud560 \ub54c \uc801\uc6a9\ud558\uba74 \ub354 \uc88b\uc744 \uac83 \uac19\uc544\uc11c \ubbf8\uc158\uc5d0\ub294 \uc801\uc6a9\ud558\uc9c0 \uc54a\uc558\ub2e4. \\n\uc0c1\ud669\uc5d0 \ub9de\ucdb0 \uc801\uc7ac\uc801\uc18c\uc5d0 \uc758\uc874 \uc5ed\uc804\uc744 \uc774\uc6a9\ud574\ubcf4\ub294 \uac83\ub3c4 \uc88b\uc744 \uac83 \uac19\ub2e4.\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n\ud074\ub9b0 \uc544\ud0a4\ud14d\ucc98 16\uc7a5 \ub3c5\ub9bd\uc131, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4 \\n[https://techblog.woowahan.com/2647/](https://techblog.woowahan.com/2647/) \\n[https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/](https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/)"},{"id":"shopping-cart-retrospective","metadata":{"permalink":"/shopping-cart-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0","description":"\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158","date":"2023-05-12T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 12\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":4.78,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0","slug":"shopping-cart-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5","permalink":"/accidental-duplication"},"nextItem":{"title":"\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0","permalink":"/web-racing-car-retrospective"}},"content":"### \uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\\n\\n\uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\uc740 \ube14\ub799\ucea3\uc774\ub791 \uc9c4\ud589\ud588\ub2e4. \\n\uc694\uad6c\uc0ac\ud56d\uc774 \uc5c4\uccad \ubcf5\uc7a1\ud55c \ubbf8\uc158\uc740 \uc544\ub2c8\uc5c8\uace0, \uc2a4\ud504\ub9c1\uc744 \uc0ac\uc6a9\ud558\uc5ec \uae30\ubcf8\uc801\uc778 CRUD\ub97c \uad6c\ud604\ud558\ub294 \ubbf8\uc158\uc774\uc5c8\ub2e4. \\n2\ub2e8\uacc4\uc5d0\uc11c\ub294 Basic \uc778\uc99d\uc744 \ud1b5\ud574 \uc790\uc2e0\uc758 \uc7a5\ubc14\uad6c\ub2c8\uc5d0\ub9cc \uc0c1\ud488\uc744 \ub2f4\uace0, \uc81c\uac70\ud560 \uc218 \uc788\ub3c4\ub85d \uad6c\ud604\ud558\ub294 \uc694\uad6c\uc0ac\ud56d\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\nInterceptor\ub098 Argument Resolver\uc5d0 \ub300\ud55c \uc774\ud574\ub3c4\uac00 \ub192\uc9c0 \uc54a\uc558\ub294\ub370, \uc774\ubc88 \ubbf8\uc158\uc744 \ud1b5\ud574 \uc870\uae08 \ub354 \uc54c\uc544\uac04 \ub290\ub08c\uc774\ub2e4. \\n\uc774\uc804\uc5d0 \uc2a4\ud504\ub9c1 \uc0ac\uc6a9\ud560 \ub54c\ub294 \uc544\ubb34 \uc0dd\uac01 \uc5c6\uc774 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\ub294 \uacbd\uc6b0\uac00 \ub9ce\uc558\ub294\ub370, \ucf54\ub4dc\ub97c \uc791\uc131\ud560 \ub54c \uadfc\uac70\uac00 \uc0dd\uae30\uace0 \uc788\ub294 \uac83 \uac19\ub2e4. \\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**DTO \uc6b0\ubc1c\uc801 \uc911\ubcf5**\\n\\n\uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc0c1\ud488 \ucd94\uac00\uc640 \uc0c1\ud488 \uc218\uc815\uc5d0 \ub300\ud55c \uc694\uad6c\uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4. \\n\\n![dto1](./dto1.png)\\n\\n\ud074\ub798\uc2a4\uba85\uc744 \uc81c\uc678\ud558\uace0 \ud544\ub4dc\uc640 \uac80\uc99d \ub85c\uc9c1 \uadf8 \uc678 \ubaa8\ub4e0 \uac8c \uac19\uc740 DTO\ub97c \ubcf4\uba70 \uc911\ubcf5\uc774\ub77c\uace0 \uc0dd\uac01\uc744 \ud588\uace0, \ubc18\ub300\ub85c \uc6a9\ub3c4\uac00 \ub2e4\ub974\uae30 \ub54c\ubb38\uc5d0 \uc911\ubcf5\uc774 \uc544\ub2c8\ub77c\uace0 \uc0dd\uac01\ud558\uae30\ub3c4 \ud588\ub2e4. \\n\ub85c\ubc84\ud2b8 \ub9c8\ud2f4\ub2d8\uc774 \uc9d1\ud544\ud558\uc2e0 \ud074\ub9b0 \uc544\ud0a4\ud14d\ucc98\ub294 \uc544\ub798\uc640 \uac19\uc774 \uc911\ubcf5\uc744 \uc5ec\ub7ec \uac00\uc9c0 \uc885\ub958\ub85c \ub098\ub204\uc5b4 \uc124\uba85\ud558\uace0 \uc788\ub2e4. \\n\\n- \uc9c4\uc9dc \uc911\ubcf5: \ud55c \uc778\uc2a4\ud134\uc2a4\uac00 \ubcc0\uacbd\ub418\uba74, \ub3d9\uc77c\ud55c \ubcc0\uacbd\uc744 \uadf8 \uc778\uc2a4\ud134\uc2a4\uc758 \ubaa8\ub4dc \ubcf5\uc0ac\ubcf8\uc5d0 \ubc18\ub4dc\uc2dc \uc801\uc6a9\ud574\uc57c \ud55c\ub2e4.\\n- \uc6b0\ubc1c\uc801 \uc911\ubcf5: \uc911\ubcf5\uc73c\ub85c \ubcf4\uc774\ub294 \ub450 \ucf54\ub4dc \uc601\uc5ed\uc774 \uac01\uc790\uc758 \uacbd\ub85c\ub85c \ubc1c\uc804\ud55c\ub2e4\uba74, \uc989 \uc11c\ub85c \ub2e4\ub978 \uc18d\ub3c4\uc640 \ub2e4\ub978 \uc774\uc720\ub85c \ubcc0\uacbd\ub41c\ub2e4\uba74 \uc774 \ub450 \ucf54\ub4dc\ub294 \uc9c4\uc9dc \uc911\ubcf5\uc774 \uc544\ub2c8\ub2e4.\\n\\n\ucd94\uac00\uc640 \uc218\uc815\uc740 \ucd08\uae30\uc5d0\ub294 \uc911\ubcf5\uc73c\ub85c \ubcf4\uc774\uc9c0\ub9cc \ucd08\uae30 \uc0dd\uc131 \uc2dc\uc5d0\ub9cc \uae30\uc785\ud558\ub294 \ub370\uc774\ud130\ub4e4\uc774 \ucd94\uac00\ub418\uac70\ub098, \uc2dc\uac04\uc774 \uc9c0\ub098\uba74\uc11c \uc11c\ub85c \ub2ec\ub77c\uc9c8 \uac00\ub2a5\uc131\uc774 \ub192\uc544\uc9c4\ub2e4. \\n\ub530\ub77c\uc11c \ub9ac\ubdf0\uc5b4 \uc6e8\uc9c0\uac00 \uc544\ub798\uc640 \uac19\uc774 \uc758\uc874 \uc5ed\uc804\uc744 \uc774\uc6a9\ud558\ub294 \ubc29\ubc95\ub3c4 \uc788\ub2e4\uace0 \uc54c\ub824\uc8fc\uc168\ub2e4. \\n\\n![dto2](./dto2.png)\\n\\n**Interceptor\uc5d0\uc11c \uc778\uc99d\ud55c \uac12 \uc7ac\uc0ac\uc6a9**\\n\\n\uc0ac\uc2e4 \uc870\ud68c\ub97c \ub450 \ubc88 \ud558\uae30 \uc2eb\uc5b4\uc11c \ub2e4\uc591\ud55c \ubc29\ubc95\uc744 \uc0dd\uac01\ud588\uc5c8\ub294\ub370 \uc774\ubc88 \ubbf8\uc158\uc5d0\uc11c\ub294 ThreadLocal\uc744 \uc0ac\uc6a9\ud588\ub2e4. \\n\uc77c\ub2e8 Tomcat\uc740 \uc694\uccad\ub9c8\ub2e4 \ub2e4\ub978 \uc2a4\ub808\ub4dc\ub97c \uc0ac\uc6a9\ud558\uace0, Interceptor\uc5d0\uc11c \uc870\ud68c\ud574\uc11c \ub9cc\ub4e0 Credential\uc744 ThreadLocal\uc5d0 \ub123\uc5b4\ub450\uc5c8\ub2e4\uac00 ArgumentResolver\uc5d0\uc11c \uaebc\ub0b8 \ub2e4\uc74c ThreadLocal\uc744 clear \ud558\uba74 \ubb38\uc81c\uac00 \uc5c6\uc744 \uac70\ub77c \ud310\ub2e8\ud588\ub2e4. \\n\\n\ub9ac\ubdf0\uc5b4\uc778 \uc6e8\uc9c0\uc5d0\uac8c\ub3c4 \uc5b4\ub5a4 \ubc29\ubc95\uc744 \uc0ac\uc6a9\ud560\uc9c0 \uad81\uae08\uc99d\uc744 \uc791\uc131\ud588\uc5c8\ub2e4. \\n\uc6e8\uc9c0\ub294 email\uc5d0 index\ub97c \uac78\uc5b4\ub450\uace0 dao \uc7ac\uc870\ud68c\ub97c \uc0ac\uc6a9\ud560 \uac83\uc774\ub77c\uace0 \ud588\ub2e4. \\n\uc7ac\uc0ac\uc6a9\ud558\uc9c0 \uc54a\uace0 db\uc5d0 \uc778\ub371\uc2a4\ub97c \uac78 \uc0dd\uac01\uc740 \ud558\uc9c0 \ubabb\ud588\ub294\ub370, \uc81c\uc77c \uc9c1\uad00\uc801\uc774\uace0 \uc88b\uc740 \ubc29\ubc95\uc774\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n**\uae30\ub85d**\\n\\n\ube14\ub799\ucea3\uc740 \uae30\ub85d\uc744 \uad49\uc7a5\ud788 \uc798 \ud558\ub294 \ud06c\ub8e8\uc600\ub2e4. \\n\ub178\uc158\uc5d0 \ud398\uc5b4\ub97c \uc9c4\ud589\ud558\uba74\uc11c \ud588\ub358 \ub0b4\uc6a9 + \uace0\ubbfc\ud588\ub358 \ubd80\ubd84 + \ud68c\uace0\ub97c \uaf3c\uaf3c\ud558\uac8c \uae30\ub85d\ud574\uc11c \uacf5\uc720\ud574 \uc8fc\uc5c8\ub2e4. \\n\ucd94\uac00\uc801\uc73c\ub85c \uc774\ubaa8\uc9c0\ub97c \uc801\uadf9\uc801\uc73c\ub85c \uc0ac\uc6a9\ud558\uc5ec \ub354\uc6b1 \uc88b\uc558\ub2e4!\\n\\n**\uc758\uacac \uc77c\uce58\uc2dc\ud0a4\uae30**\\n\\n\ud398\uc5b4 \uc2dc\uac04\uc740 \ud55c\uc815\ub418\uc5b4 \uc788\uace0, \uae30\uac04 \ub0b4 \uc694\uad6c\uc0ac\ud56d\uc744 \ub9cc\uc871\ud574\uc57c \ud55c\ub2e4. \\n\ub530\ub77c\uc11c \uc801\ub2f9\ud788 \ud0c0\ud611\uc744 \ubd10\uc11c \uc758\uacac\uc744 \ube60\ub974\uac8c \uc218\uc6a9\ud574 \ub370\ub4dc\ub77c\uc778\uc744 \ub9de\ucd94\ub294 \uac83\ub3c4 \uc911\uc694\ud558\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\ube14\ub799\ucea3\uc740 \ub0b4 \uc758\uacac\uc744 \uc798 \ub4e4\uc5b4\uc92c\uace0, \ub355\ubd84\uc5d0 \ub9c9\ud788\ub294 \ubd80\ubd84 \uc5c6\uc774 \ube60\ub974\uac8c \ubbf8\uc158\uc744 \uc9c4\ud589\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n\ube68\ub9ac \uce5c\ud574\uc84c\uace0, \uc758\uc0ac\uc18c\ud1b5\uc774 \uc798 \ub3fc\uc11c \uc7ac\ubc0c\uac8c \ucf54\ub529\ud560 \uc218 \uc788\uc5c8\ub2e4!"},{"id":"web-racing-car-retrospective","metadata":{"permalink":"/web-racing-car-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-02-\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-05-02-\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0","description":"\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158","date":"2023-05-02T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 2\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":3.535,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0","slug":"web-racing-car-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0","permalink":"/shopping-cart-retrospective"},"nextItem":{"title":"[\ud14c\ucf54\ucc57] 2. \ubc30\ud3ec","permalink":"/tecochat-retrospective-2"}},"content":"### \uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158\\n\\n\uc0ac\uc774\ub4dc \ud504\ub85c\uc81d\ud2b8\ub97c \ud55c\ub2e4\uace0 \uc2dc\uac04\uc774 \ub9ce\uc774 \uc5c6\uc5b4\uc11c \ud68c\uace0\uac00 \ub2a6\uc5b4\uc84c\ub2e4. \\n\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158\uc5d0\uc11c\ub294 \ube44\ubc84\uc640 \ud398\uc5b4\uac00 \ub9e4\uce6d\ub418\uc5c8\ub2e4. \\n\ub808\ubca8 2\uc5d0\uc11c \uc9c4\ud589\ud558\ub294 \uccab \ubbf8\uc158\uc774\ub77c \ub9ce\uc774 \uae34\uc7a5\ub418\uc5c8\uc9c0\ub9cc, \uadf8\ub798\ub3c4 \ube44\ubc84\ub791 \ucd08\ubc18\uc5d0 \ub9db\uc788\ub294 \uac83\ub3c4 \ub9ce\uc774 \uba39\uc73c\uba74\uc11c \ube68\ub9ac \uce5c\ud574\uc838\uc11c \uc7ac\ubc0c\uac8c \ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n\uc2a4\ud504\ub9c1\uc744 \uc870\uae08 \uc0ac\uc6a9\ud560 \uc904 \uc54c\uc544\uc11c, \ube44\ubc84\ub791 \uac19\uc774 \ud559\uc2b5\ud558\uba74\uc11c \ubbf8\uc158\uc744 \uc9c4\ud589\ud588\ub2e4. \\n\uccab \ubbf8\uc158\uc774\ub77c \uadf8\ub7f0\uc9c0 \ud2b9\ubcc4\ud55c \ubd80\ubd84\uc740 \uc5c6\uc5c8\uace0, \ucd5c\ub300\ud55c \uae54\ub054\ud558\uac8c \uc791\uc131\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. \\n\ub09c\uc774\ub3c4 \ub192\uc740 \ubbf8\uc158\uc774 \uc544\ub2c8\uc5c8\uc9c0\ub9cc \ub9ac\ubdf0\uc5b4\uc778 \ub77c\ube48\uc5d0\uac8c \uce6d\ucc2c\uc744 \ub9ce\uc774 \ubc1b\uc544\uc11c \uae30\ubd84\uc774 \uc88b\uc558\ub2e4. \\n\ub77c\ube48 \uac10\uc0ac\ud569\ub2c8\ub2e4! \\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n\ucee8\ub514\uc158\ub3c4 \uc88b\uc9c0 \uc54a\uace0 \uc5f4\uc815\ub3c4 \uc2dd\uc740 \uac83 \uac19\uc740 \ub290\ub08c\uc774 \ub4e4\uc5c8\ub2e4. \\n\ubbf8\uc158\uc774 \ub2e4\uc18c \uc5ec\uc720\ub86d\ub2e4\uace0 \ub290\uaef4\uc838\uc11c, \uc2dc\uac04\uc5d0 \ub300\ud55c \ubd80\ubd84\ub3c4 \uc798 \uad00\ub9ac\ud558\uc9c0 \ubabb\ud55c \uac83 \uac19\ub2e4. \\n\ubbf8\uc158\uc5d0 \uc798 \uc9d1\uc911\ud558\uc9c0 \ubabb\ud574\uc11c \ud398\uc5b4\uc5d0\uac8c \ub9ce\uc774 \ubbf8\uc548\ud588\uace0, \ub098 \uc790\uc2e0\uc5d0\uac8c \uc544\uc26c\uc6e0\ub358 \ubd80\ubd84\uc774 \ub9ce\uc558\ub2e4. \\n\\n\uc9c0\ub09c\ubc88 \ud68c\uace0\ub97c \ub2e4\uc2dc \ubcf4\ub294\ub370 \uc9d1\uc911\uc744 \uc798 \ubabb\ud55c \uacbd\uc6b0\uac00 \ub9ce\uc740 \uac83 \uac19\ub2e4. \\n\ub3c4\uc804\uc801\uc774\uc9c0 \uc54a\uac70\ub098 \uc2dc\uac04\uc774 \ubd80\uc871\ud558\uc9c0 \uc54a\uc73c\uba74 \uc9d1\uc911\uc744 \uc798 \ubabb\ud558\ub294 \uac83 \uac19\ub2e4. \\n\uba38\ub9bf\uc18d\uc5d0\uc11c \uc2dc\uac04\uc801 \uc5ec\uc720\uac00 \uc788\ub2e4\uace0 \uc0dd\uac01\ud560 \ub54c\uac00 \uac00\uc7a5 \uc704\ud5d8\ud55c \uc21c\uac04\uc778 \uac83 \uac19\ub2e4. \\n\\n\ud568\uaed8 \uc790\ub77c\uae30\uc5d0\uc11c \ub098\uc628 `\ub09c\uc774\ub3c4 \ub192\uc774\uae30`\uac00 \ud544\uc694\ud574\uc9c0\ub294 \uc21c\uac04\uc774\ub2e4. \\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**\uc911\uc694\ub3c4\uac00 \uc788\ub294 \uc5b4\ub178\ud14c\uc774\uc158\ubd80\ud130 \ud074\ub798\uc2a4 \uc774\ub984\uc5d0 \uac00\uae5d\uac8c \uba85\uc2dc\ud558\uae30**\\n\\n```java\\n@SuppressWarnings(\\"NonAsciiCharacters\\")\\n@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)\\n@Transactional\\n@AutoConfigureMockMvc\\n@SpringBootTest\\npublic class RacingGameIntegrationTest {\\n```\\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n**\ube44\ubc84\uc758 \uc131\uaca9** \\n\ube44\ubc84\uac00 \uc131\uaca9\uc774 \uc88b\uc544\uc11c \ud3b8\ud558\uac8c \ud398\uc5b4\ub97c \ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc131\uae09\ud558\uc9c0 \uc54a\uace0 \uc5ec\uc720\ub85c\uc6cc\uc11c \uc88b\uc558\ub2e4. \\n\\n**\ubbf8\uc158\uc5d0 \uc9d1\uc911\ud558\ub294 \ubd80\ubd84** \\n\ub0b4\uac00 \ubbf8\uc158\uc5d0 \uc798 \uc9d1\uc911\ud558\uc9c0 \ubabb\ud588\ub294\ub370\ub3c4 \uac19\uc774 \ud398\uc5b4\ub97c \uc798 \uc9c4\ud589\ud55c \uac83 \uac19\uc544\uc11c \uc88b\uc558\ub2e4. \\n\ube44\ubc84\uac00 \ubbf8\uc158\uc5d0 \uc798 \uc9d1\uc911\ud574\uc11c \uadf8\ub807\uc9c0 \uc54a\uc558\ub098 \uc0dd\uac01\ud588\ub2e4. \\n\uadfc\uc721\ub9e8 \ube44\ubc84\ub77c \uadf8\ub7f0\uc9c0 \uccb4\ub825\uc774 \uc88b\uc544\uc11c \uadf8\ub7f0\uac00? \\n\uc911\uac04\uc5d0 \uc798 \uc548 \uc26c\uace0\ub3c4 \uc9d1\uc911\ud574\uc11c \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\ub294 \uac78 \ubcf4\uace0 \ub300\ub2e8\ud558\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\\n**\ud559\uc2b5\uc5d0 \ub300\ud55c \uc5f4\uc815** \\n\ucd94\uac00\uc801\uc73c\ub85c \uc54c\uace0 \uc2f6\uc740 \ubd80\ubd84\uc744 \ub530\ub85c \ud559\uc2b5\ud558\ub294 \uc5f4\uc815\uc774 \uc88b\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\ube44\ubc84\uc640 \uc2a4\ud504\ub9c1\uc5d0 \ub300\ud574 \uc54c\uc544\uac00\ub294 \uc2dc\uac04\uc744 \ub9ce\uc774 \uac00\uc9c4 \ubd80\ubd84\uc774 \ub9e4\uc6b0 \uc88b\uc558\ub2e4. \\n\ub098\ub3c4 5\uc6d4\ubd80\ud130 \uc870\uae08 \ub354 \ud654\uc774\ud305 \ud574\uc57c\uaca0\ub2e4."},{"id":"tecochat-retrospective-2","metadata":{"permalink":"/tecochat-retrospective-2","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-01-\ud14c\ucf54\ucc57 2. \ubc30\ud3ec.mdx","source":"@site/blog/2023-2/2023-05-01-\ud14c\ucf54\ucc57 2. \ubc30\ud3ec.mdx","title":"[\ud14c\ucf54\ucc57] 2. \ubc30\ud3ec","description":"\ud504\ub860\ud2b8\uc5d4\ud2b8","date":"2023-05-01T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 1\uc77c","tags":[{"label":"TecoChat","permalink":"/tags/teco-chat"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":4.67,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ud14c\ucf54\ucc57] 2. \ubc30\ud3ec","slug":"tecochat-retrospective-2","tags":["TecoChat","Retrospective"]},"prevItem":{"title":"\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0","permalink":"/web-racing-car-retrospective"},"nextItem":{"title":"Jenkins\ub85c CI/CD \uc124\uc815","permalink":"/jenkins"}},"content":"### \ud504\ub860\ud2b8\uc5d4\ud2b8\\n\\n\ub2c9\ub124\uc784\uc744 \uc785\ub825\ud558\uc5ec \uac04\ub2e8\ud788 \ub85c\uadf8\uc778\ud558\ub294 \ud654\uba74, \ucc44\ud305 \ubaa9\ub85d\uc744 \ubcf4\uc5ec\uc8fc\ub294 \ud654\uba74\ub3c4 \ub9cc\ub4e4\uc5c8\uace0 \ub2e8\uc77c \ucc44\ud305\uc744 \ud655\uc778\ud560 \uc218 \uc788\ub294 \ud654\uba74\ub3c4 \ub9cc\ub4e4\uc5c8\ub2e4. \\n\ucd94\uac00\ub85c \ucc44\ud305\uc744 \uc774\uc5b4\ub098\uac08 \uc218 \uc788\uac8c \ud558\ub294 \uae30\ub2a5\ub3c4 \ucd94\uac00\ud588\ub2e4. \\n\uc790\uc798\ud558\uac8c \uc2e0\uacbd \uc4f8 \ubd80\ubd84\uc774 \ub9ce\uc544\uc11c, \ud504\ub860\ud2b8\uc5d4\ub4dc \ud558\ub294 \uc0ac\ub78c\ub4e4\uc774 \ub300\ub2e8\ud558\ub2e4\uace0 \uc0dd\uac01\ub418\uc5c8\ub2e4. \\n\uc5ec\uc720\uac00 \ub41c\ub2e4\uba74 \uc790\uc2e0\uc758 \ucc44\ud305\uc744 \ubcfc \uc218 \uc788\ub294 \uae30\ub2a5\uc774\ub098, \ucc44\ud305\uc744 \uc774\uc5b4\uc11c \ud560 \uc218 \uc788\ub294 \uae30\ub2a5, \ub313\uae00 \uae30\ub2a5\ub3c4 \ucd94\uac00\ud560 \uc608\uc815\uc774\ub2e4. \\n\\n### \ubc31\uc5d4\ub4dc\\n\\n\ucd5c\ub300\ud55c \ube68\ub9ac \uc11c\ube44\uc2a4\ub97c \ud06c\ub8e8\ub4e4\uc5d0\uac8c \uc81c\uacf5\ud558\uae30\ub85c \uc815\ud574\uc11c, \ubc31\uc5d4\ub4dc\ub294 \ub9d0\ub791\uc774 \uc77c\ub2e8 \ub2e4 \ub9cc\ub4e4\uace0 \uc788\ub2e4. \\n\ub9d0\ub791\uc774 \ud55c \ubd80\ubd84\uc774 \ub108\ubb34 \ub9ce\uc544\uc11c \ub0b4\uac00 \ubabb \ub530\ub77c\uac00\ub294 \uac83 \uac19\ub2e4. \\n\ub098\uc911\uc5d0 \ubc31\uc5d4\ub4dc \ucf54\ub4dc\ub97c \uc774\ud574\ud558\ub294 \uc2dc\uac04\uc744 \uac00\uc838\uc57c\uaca0\ub2e4. \\n\\n### Http Request Header\\n\\n\uc544\uc9c1 \uc778\uc99d\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \ud558\uc9c0 \uc54a\uc544\uc11c \uc694\uccad \ud5e4\ub354\uc5d0 \uc774\ub984\uc744 \ubcf4\ub0b4\uae30\ub85c \ud588\ub2e4. \\n\ub9d0\ub791\uc774 \ud55c\uae00\uc740 \uc548\ub41c\ub2e4\uace0 \ub9d0\ud574\uc918\uc11c Base64\ub85c \uc778\ucf54\ub529\ud558\uace0, \ubc31\uc5d4\ub4dc\uc5d0\uc11c \ub514\ucf54\ub529 \ud558\uc5ec \uc0ac\uc6a9\ud558\uae30\ub85c \ud588\ub2e4. \\n\uc544\ub798\ub294 pinia\uc5d0 \uc788\ub294 name \uac12\uc744 \uc778\ucf54\ub529 \ud558\ub294 \ucf54\ub4dc\ub2e4. deprecated \ub418\uc5c8\ub2e4\ub294\ub370, \ub2e4\ub978 \ubc29\ubc95\uc744 \uc0ac\uc6a9\ud560 \uc904 \ubab0\ub77c\uc11c \uc77c\ub2e8 \uc774\uac78 \uc0ac\uc6a9\ud588\ub2e4. \\n\\n```ts\\nconst encodedName = () => {\\n const uriComponent = unescape(encodeURIComponent(name.value));\\n return btoa(uriComponent);\\n};\\n```\\n\\n### Elastic Beanstalk\\n\\n\uac00\uc7a5 \ube60\ub974\uac8c \ubc31\uc5d4\ub4dc\ub97c \ubc30\ud3ec\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc774 \ubb58\uc9c0 \uace0\ubbfc\ud558\ub2e4\uac00 Elastic Beanstalk\ub97c \uc0ac\uc6a9\ud558\uae30\ub85c \ud588\ub2e4. \\nElastic Beanstalk\ub97c \uc0ac\uc6a9\ud558\uba74 \uc778\ud504\ub77c\uc5d0 \ub300\ud574 \uc798 \uc54c\uc9c0 \ubabb\ud574\ub3c4 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \ube60\ub974\uac8c \ubc30\ud3ec\ud558\uace0 \uad00\ub9ac\ud560 \uc218 \uc788\ub2e4. \\n\ubaa8\ub2c8\ud130\ub9c1, \ub85c\uae45, \ub85c\ub4dc \ubc38\ub7f0\uc2f1 \ub4f1 \ub2e4\uc591\ud55c \uae30\ub2a5\uc744 \uc81c\uacf5\ud55c\ub2e4. \\n\\n### Elastic Beanstalk RDS \uc124\uc815 \ud6c4 \ubd84\ub9ac\\n\\n\ucd08\uae30 \uc124\uc815 \uc2dc RDS\ub97c \uc5f0\uacb0\ud558\uace0 \uc124\uc815 \uc644\ub8cc \ud6c4 \ubd84\ub9ac\ud55c\ub2e4\uba74, Beanstalk \uc778\uc2a4\ud134\uc2a4 -> RDS \uc694\uccad \uc2dc \uc778\ubc14\uc6b4\ub4dc \uc124\uc815\uc744 \uc548 \ud574\ub3c4 \ub41c\ub2e4. \\nRDS \ubd84\ub9ac \uc2dc Beanstalk\uc5d0 \uae30\ubcf8\uc801\uc73c\ub85c \uc124\uc815\ub418\uc5b4 \uc788\ub294 RDS_HOSTNAME, RDS_PORT, RDS_USERNAME, RDS_PASSWORD\uc640 \uac19\uc740 \ud658\uacbd \ubcc0\uc218\uac00 \uac19\uc774 \uc81c\uac70\ub41c\ub2e4. \\n\ucd94\uac00\ub85c Elastic Beanstalk\ub85c RDS\ub97c \uc124\uc815\ud558\uba74 \uae30\ubcf8 \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uba85\uc740 ebdb\ub2e4. \\n\\n### Elastic Beanstalk nginx \uc124\uc815\\n\\n\uc5c5\ub85c\ub4dc\ud558\ub294 zip \ud30c\uc77c \ub0b4\ubd80\uc5d0 `.platform/nginx/conf.d/` \uacbd\ub85c\uc5d0 \uc124\uc815 \ud30c\uc77c\uc744 \ucd94\uac00\ud558\uba74 nginx \uc124\uc815\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\\n### Jenkins\\n\\n\ubc31\uc5d4\ub4dc \ucf54\ub4dc\ub97c \uc77c\uc77c\ud788 \ubc30\ud3ec\ud558\uae30 \ubd88\ud3b8\ud574\uc11c Jenkins\ub97c \uc774\uc6a9\ud558\uc5ec Repository\uc5d0 \ucf54\ub4dc\ub97c push \ud560 \ub54c \uc790\ub3d9\uc73c\ub85c \ubc30\ud3ec\uac00 \ub418\uac8c \uc124\uc815\ud558\uae30\ub85c \ud588\ub2e4. \\n\uc791\ub144\uc5d0 \ud655\uc778\ud588\uc744 \ub550 2022\ub144 12\uc6d4 31\uc77c\uae4c\uc9c0 EC2 ARM \uae30\ubc18 t4g.small\uc774 \ubb34\ub8cc\uc600\ub294\ub370, \ub2e4\uc2dc \ub4e4\uc5b4\uac00 \ubcf4\ub2c8 2023\ub144\uae4c\uc9c0 12\uc6d4 31\uc77c\uae4c\uc9c0 t4g.small\uc744 \ubb34\ub8cc\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\uc5c8\ub2e4. \\nt4g.small\uc740 \ub7a8\uc774 2G\uc778\ub370, \uc608\uc804\uc5d0\ub294 \ubd80\uc871\ud558\uc9c0 \uc54a\uc558\ub2e4\uace0 \uc0dd\uac01\ud588\ub294\ub370 Java 17\uc744 \uc368\uc11c \uadf8\ub7f0\uac00 \ube4c\ub4dc \ud560 \ub54c \ub7a8\uc774 \ub9ce\uc774 \ubd80\uc871\ud55c \uac83 \uac19\uc544\uc11c Swap \uba54\ubaa8\ub9ac 2\uae30\uac00\ub97c \ucd94\uac00\ub85c \uc124\uc815\ud588\ub2e4. \\n\ucd94\uac00\ub85c build.gradle\uc5d0\uc11c \uc544\ub798\uc640 \uac19\uc774 \uc124\uc815\ud55c\ub2e4\uba74 \ud14c\uc2a4\ud2b8 \uc2dc \uc0ac\uc6a9\ud558\ub294 \ub7a8\uc744 \ub298\ub9b4 \uc218 \uc788\ub2e4. \uae30\ubcf8\uac12\uc740 512MB\ub77c\uace0 \ud55c\ub2e4. \\n\\n```groovy\\ntest {\\n maxHeapSize = \\"1024m\\"\\n}\\n```\\n\\n### Jenkins Blue Ocean\\n\\nBlue Ocean\uc740 Jenkins Pipeline\uc744 \uad6c\uc131\ud558\ub294 \ub370\uc5d0 \uc788\uc5b4 \ud3b8\ub9ac\ud558\uac8c \ud574\uc8fc\ub294 \ub3c4\uad6c\ub2e4. \\n\uc2dc\uac01\ud654\ub3c4 \uc798 \ub418\uc5b4\uc788\uace0, \uc124\uc815\ub3c4 \ud3b8\ub9ac\ud55c \uac83 \uac19\ub2e4. \\n\uc624\ub298 \uc801\uc6a9\ud574 \ubcf4\ub2c8 \ub7a8\uc774 \ubd80\uc871\ud558\uc5ec \uc911\uac04\uc5d0 \uc798 \uc548\ub418\uae30\ub3c4 \ud558\uace0 \uadf8\ub798\uc11c \uadf8\ub0e5 \\"Pipeline\ub9cc \uc0ac\uc6a9\ud560 \uac78 \uadf8\ub7ac\ub098?\\" \ub77c\ub294 \uc0dd\uac01\uc774 \ub4e0\ub2e4. \\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n[Elastic Beanstalk, AWS](https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/Welcome.html) \\n[EC2 AWS Graviton, AWS](https://aws.amazon.com/ko/ec2/graviton/) \\n[Default Memory Settings, AWS](https://docs.gradle.org/current/userguide/upgrading_version_4.html#rel5.0:default_memory_settings)"},{"id":"jenkins","metadata":{"permalink":"/jenkins","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815.mdx","source":"@site/blog/2023-2/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815.mdx","title":"Jenkins\ub85c CI/CD \uc124\uc815","description":"\uc124\uc815 \ud658\uacbd","date":"2023-04-30T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 30\uc77c","tags":[{"label":"Jenkins","permalink":"/tags/jenkins"},{"label":"Elastic Beanstalk","permalink":"/tags/elastic-beanstalk"}],"readingTime":7.495,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"Jenkins\ub85c CI/CD \uc124\uc815","slug":"jenkins","tags":["Jenkins","Elastic Beanstalk"]},"prevItem":{"title":"[\ud14c\ucf54\ucc57] 2. \ubc30\ud3ec","permalink":"/tecochat-retrospective-2"},"nextItem":{"title":"[\ud14c\ucf54\ucc57] 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30","permalink":"/tecochat-retrospective-1"}},"content":"### \uc124\uc815 \ud658\uacbd\\n\\n\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc774\ubbf8\uc9c0: Amazon Linux 2023 AMI \\n\uc544\ud0a4\ud14d\uccd0: ARM \\n\uc778\uc2a4\ud134\uc2a4 \uc720\ud615: t4g.small \\n\ud658\uacbd \uad6c\uc131\uc774 \uc644\ub8cc\ub41c Elastic Beanstalk \\n\ub2e8\uc77c Spring Boot \ud504\ub85c\uc81d\ud2b8\uac00 \uc874\uc7ac\ud558\ub294 Github Repository\\n\\n### \\\\[EC2 CLI\\\\] Swap \uba54\ubaa8\ub9ac \uc124\uc815\\n\\nt4g.small\uc774 \ub7a8\uc774 2G\uc778\ub370 \ub7a8\uc774 \ubd80\uc871\ud558\ub2e4\uace0 \ub290\uaef4\uc838\uc11c swap \uba54\ubaa8\ub9ac\ub97c \uc124\uc815\ud588\ub2e4. \\n\uc544\ub798 \uba85\ub839\uc5b4\ub97c \ub530\ub77c swap \uba54\ubaa8\ub9ac\ub97c \uc124\uc815\ud558\uace0 free -h \uba85\ub839\uc5b4\ub97c \ud1b5\ud574 \uc798 \uc124\uc815\ub418\uc5c8\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n``` bash\\n# fallocate \uc774\uc6a9\ud558\uc5ec \uc2a4\uc651 \ud30c\uc77c \uc0dd\uc131\\nsudo fallocate -l 2G /swapfile\\n\\n# \uad8c\ud55c \uc124\uc815\\nsudo chmod 600 /swapfile\\n\\n# \ud30c\uc77c\uc744 Swap \ud3ec\ub9f7\uc73c\ub85c \ubcc0\uacbd \ud6c4 \uc2dc\uc2a4\ud15c\uc5d0 \ub4f1\ub85d\\nsudo mkswap /swapfile\\nsudo swapon /swapfile\\n\\n# Swap \uba54\ubaa8\ub9ac \ubd80\ud305\uc2dc \uc790\ub3d9\uc73c\ub85c \ub9c8\uc6b4\ud2b8\ud558\ub3c4\ub85d \uc801\uc6a9\\n# \ucd5c\ud558\ub2e8\uc5d0 \ub2e4\uc74c \uad6c\ubb38 \uc124\uc815 -> /swapfile swap swap defaults 0 0\\nsudo vim /etc/fstab\\n```\\n\\n\\n### \\\\[EC2 CLI\\\\] jenkins \uc124\uce58\\n\\n```bash\\nsudo wget -O /etc/yum.repos.d/jenkins.repo \\\\\\n https://pkg.jenkins.io/redhat-stable/jenkins.repo\\nsudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key\\nsudo yum upgrade\\nsudo yum install java-17-amazon-corretto-devel\\nsudo yum install jenkins\\nsudo systemctl daemon-reload\\n```\\n\\n[Jenkins \uacf5\uc2dd \ud648\ud398\uc774\uc9c0](https://www.jenkins.io/doc/book/installing/linux/#red-hat-centos) \ub97c \ucc38\uace0\ud558\uc5ec \uc124\uce58\ud558\ub294 \uac8c \uc88b\ub2e4.\\n\\n### \\\\[EC2 CLI\\\\] Jenkins \uc2dc\uc791\\n\\n```bash\\nsudo systemctl enable jenkins\\nsudo systemctl start jenkins\\n```\\n\\nenable\ub85c \uc124\uc815\ud558\uc5ec \ubd80\ud305\uc2dc \uc790\ub3d9\uc2dc\uc791 \ub418\ub3c4\ub85d \uc124\uc815\ud55c\ub2e4.\\n\\n### \\\\[EC2 CLI\\\\] nginx & git \uc124\uce58\\n\\n```bash\\nsudo yum install nginx\\nsudo systemctl enable nginx\\nsudo systemctl start nginx\\n\\nsudo yum install git\\n```\\n\\nnginx\uc640 \ucf54\ub4dc\ub97c \ubd88\ub7ec\uc62c \ub54c \uc0ac\uc6a9\ud560 git\uc744 \uc124\uce58\ud55c\ub2e4.\\n\\n### \\\\[EC2 CLI\\\\] nginx \ub9ac\ubc84\uc2a4 \ud504\ub85d\uc2dc \uc124\uc815\\n\\n\uc544\ub798 \uc124\uc815 \ud30c\uc77c\uc740 \uacf5\uc2dd \ud648\ud398\uc774\uc9c0\uc5d0\uc11c \uc548\ub0b4\ud55c \uae30\ubcf8\uc801\uc778 \uc124\uc815 \ud30c\uc77c\uc774\ub2e4.\\n\\n```bash\\nupstream jenkins {\\n keepalive 32; # keepalive connections\\n server 127.0.0.1:8080; # jenkins ip and port\\n}\\n\\n# Required for Jenkins websocket agents\\nmap $http_upgrade $connection_upgrade {\\n default upgrade;\\n \'\' close;\\n}\\n\\nserver {\\n listen 80; # Listen on port 80 for IPv4 requests\\n\\n server_name jenkins.example.com; # replace \'jenkins.example.com\' with your server domain name\\n\\n # this is the jenkins web root directory\\n # (mentioned in the output of \\"systemctl cat jenkins\\")\\n root /var/run/jenkins/war/;\\n\\n access_log /var/log/nginx/jenkins.access.log;\\n error_log /var/log/nginx/jenkins.error.log;\\n\\n # pass through headers from Jenkins that Nginx considers invalid\\n ignore_invalid_headers off;\\n\\n location ~ \\"^/static/[0-9a-fA-F]{8}\\\\/(.*)$\\" {\\n # rewrite all static files into requests to the root\\n # E.g /static/12345678/css/something.css will become /css/something.css\\n rewrite \\"^/static/[0-9a-fA-F]{8}\\\\/(.*)\\" /$1 last;\\n }\\n\\n location /userContent {\\n # have nginx handle all the static requests to userContent folder\\n # note : This is the $JENKINS_HOME dir\\n root /var/lib/jenkins/;\\n if (!-f $request_filename){\\n # this file does not exist, might be a directory or a /**view** url\\n rewrite (.*) /$1 last;\\n break;\\n }\\n sendfile on;\\n }\\n\\n location / {\\n sendfile off;\\n proxy_pass http://jenkins;\\n proxy_redirect default;\\n proxy_http_version 1.1;\\n\\n # Required for Jenkins websocket agents\\n proxy_set_header Connection $connection_upgrade;\\n proxy_set_header Upgrade $http_upgrade;\\n\\n proxy_set_header Host $host;\\n proxy_set_header X-Real-IP $remote_addr;\\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\\n proxy_set_header X-Forwarded-Proto $scheme;\\n proxy_max_temp_file_size 0;\\n\\n #this is the maximum upload size\\n client_max_body_size 10m;\\n client_body_buffer_size 128k;\\n\\n proxy_connect_timeout 90;\\n proxy_send_timeout 90;\\n proxy_read_timeout 90;\\n proxy_buffering off;\\n proxy_request_buffering off; # Required for HTTP CLI commands\\n proxy_set_header Connection \\"\\"; # Clear for keepalive\\n }\\n\\n}\\n```\\n\\nJenkins\ub294 8080 \ud3ec\ud2b8\ub85c \ub3d9\uc791\ud558\uae30 \ub54c\ubb38\uc5d0 \ub9ac\ubc84\uc2a4 \ud504\ub85d\uc2dc\ub97c \uc124\uc815\ud574\uc900\ub2e4. \\n`/etc/nginx/conf.d`\xa0\uc544\ub798\xa0`default.conf`\xa0\ud30c\uc77c\uc744 \ud558\ub098 \uc0dd\uc131\ud558\uace0 \uc704\uc640 \uac19\uc774 \uc785\ub825\ud558\uace0 \uc800\uc7a5\ud55c\ub2e4. \\nnginx\uc758 \uae30\ubcf8 \uc124\uc815 \ud30c\uc77c\uc5d0 \uc874\uc7ac\ud558\ub294\xa0`include /etc/nginx/conf.d/*.conf;`\xa0\uc124\uc815 \ub54c\ubb38\uc5d0\xa0`.conf`\xa0\ub85c \ub05d\ub09c\ub2e4\uba74 \uc124\uc815\uc774 \uc801\uc6a9\ub41c\ub2e4. \\n\uc124\uc815 \ud6c4\xa0`sudo nginx -t`\ub85c \uc124\uc815\ud30c\uc77c\uc774 \uc815\uc0c1\uc778\uc9c0 \ud655\uc778\ud558\uace0,\xa0`sudo systemctl restart nginx`\xa0\uba85\ub839\uc5b4\ub85c nginx\ub97c \uc7ac\uc2dc\uc791\ud55c\ub2e4. \\n\\n### \\\\[Jenkins\\\\] Jenkins \uc811\uc18d\\n\\nJenkins\ub97c \uc124\uce58\ud55c EC2 \uc778\uc2a4\ud134\uc2a4 \uc778\ubc14\uc6b4\ub4dc \uc124\uc815\uc5d0 80\ubc88 \ud3ec\ud2b8\uac00 \uc5f4\ub824\uc788\ub294\uc9c0 \ud655\uc778\ud55c\ub2e4. \\nEC2\uc758 \uc544\uc774\ud53c \uc8fc\uc18c\ub97c \uc785\ub825\ud558\uace0 \ub4e4\uc5b4\uac00\uba74 \ube44\ubc00\ubc88\ud638\ub97c \uc785\ub825\ud558\ub77c\ub294 \ucc3d\uc774 \ub098\uc628\ub2e4.\\n\\n![jenkins-start](./jenkins-start.png)\\n\\n\ucd08\uae30 \ube44\ubc00\ubc88\ud638\ub97c \uc785\ub825\ud574\uc57c \ud558\ub294\ub370 `sudo cat /var/lib/jenkins/secrets/initialAdminPasswor` \ub97c \uc785\ub825\ud574 \ucd08\uae30 \ube44\ubc00\ubc88\ud638\ub97c \uc5bb\uc744 \uc218 \uc788\ub2e4. \\n\ube44\ubc00\ubc88\ud638\ub97c \uc785\ub825\ud558\uba74 \ud50c\ub7ec\uadf8\uc778 \uc124\uc815 \ucc3d\uc774 \ub098\uc62c\ud150\ub370 `install suggested plugins`\uc744 \ud074\ub9ad\ud558\uc5ec Jenkins\uac00 \ucd94\ucc9c\ud558\ub294 \uae30\ubcf8 \ud50c\ub7ec\uadf8\uc778\ub4e4\uc744 \uc124\uce58\ud558\uba74 \ub41c\ub2e4. \\n\ud50c\ub7ec\uadf8\uc778\uc744 \uc124\uce58\ud558\uba74 \uacc4\uc815 \ubc0f \uc8fc\uc18c \uc124\uc815\uc744 \ud574\uc57c\ud558\ub294\ub370 \uc774\uac74 \ud3b8\ud558\uac8c \uc124\uc815\ud558\uba74 \ub41c\ub2e4. \\n\\n### \\\\[Jenkins\\\\] Jenkins Blue Ocean \uc124\uce58\\n\\nJenkins \uad00\ub9ac \u2192 Plugin Manager\uc5d0\uc11c Blue Ocean\uc744 \uac80\uc0c9\ud574 \uc124\uce58\ud55c\ub2e4.\\n\\n### \\\\[AWS IAM & EC2\\\\] IAM\uc73c\ub85c EC2 \uc778\uc2a4\ud134\uc2a4 \uad8c\ud55c \uc124\uc815\ud558\uae30\\n\\nS3\uc640 Elastic Beanstalk\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\ub294 \uad8c\ud55c\uc744 \ubd80\uc5ec\ud558\ub824\uba74 AmazonS3FullAccess, AdministratorAccess-AWSElasticBeanstalk \ub450 \uac1c\uc758 \uc815\ucc45\uc744 \uac00\uc9c0\uace0 \uc788\ub294 \uc5ed\ud560\uc744 \uc0dd\uc131\ud574\uc57c \ud55c\ub2e4. \\nIAM\uc5d0\uc11c \ub2e4\uc74c\uacfc \uac19\uc774 \uc5ed\ud560\uc744 \ud558\ub098 \uc0c8\ub85c \uc0dd\uc131\ud55c\ub2e4.\\n\\n1. \uc5d4\ud130\ud2f0 \uc120\ud0dd\\n\\n![aws-iam1](./aws-iam1.png)\\n\\n2. \uad8c\ud55c \ucd94\uac00\\n\\n![aws-iam2](./aws-iam2.png)\\n\\n3. \uc774\ub984 \uc9c0\uc815, \uac80\ud1a0 \ubc0f \uc0dd\uc131\\n\\n![aws-iam3](./aws-iam3.png)\\n\\n4. \uc0dd\uc131\ud55c IAM EC2 Jenkins \uc778\uc2a4\ud134\uc2a4\ub97c \uc120\ud0dd\ud558\uace0, \uc791\uc5c5 \u2192 \ubcf4\uc548 \u2192 IAM \uc5ed\ud560 \uc218\uc815\uc744 \ub20c\ub7ec Role \uc124\uc815\\n\\n![aws-iam4](./aws-iam4.png)\\n\\n### \\\\[AWS S3\\\\] Jar \ud30c\uc77c\uc744 \uc5c5\ub85c\ub4dc \ud560 S3 \ubc84\ud0b7 \uc0dd\uc131\\n\\n\ubc84\ud0b7\uc744 \uc0dd\uc131\ud560 \ub54c \ub2e4\uc74c \uc124\uc815\uc744 \uc81c\uc678\ud558\uace0 \ubaa8\ub450 \ucc28\ub2e8 \ud65c\uc131\ud654\ub97c \ud574\uc900\ub2e4.\\n\\n- `\uc0c8 ACL(\uc561\uc138\uc2a4 \uc81c\uc5b4 \ubaa9\ub85d)\uc744 \ud1b5\ud574 \ubd80\uc5ec\ub41c \ubc84\ud0b7 \ubc0f \uac1d\uccb4\uc5d0 \ub300\ud55c \ud37c\ube14\ub9ad \uc561\uc138\uc2a4 \ucc28\ub2e8`\\n\\n![aws-s3](./aws-s3.png)\\n\\n### \\\\[Github\\\\] Blue Ocean\uc5d0\uc11c \ud30c\uc774\ud504\ub77c\uc778 \uc0dd\uc131\uc5d0 \ud544\uc694\ud55c Github Token \uc0dd\uc131\\n\\nrepo, user:email \uad8c\ud55c\uc774 \uc788\ub294 \ud1a0\ud070\uc774 \ud544\uc694\ud558\ub2e4. \\n\\n### \\\\[Jenkins\\\\] \ube14\ub8e8 \uc624\uc158 \uc2dc\uc791\\n\\n![jenkins-blue-ocean1](./jenkins-blue-ocean1.png)\\n\\n\ube14\ub8e8 \uc624\uc158 \uc5f4\uae30\ub85c \ud30c\uc774\ud504\ub77c\uc778\uc744 \uc0dd\uc131\ud55c\ub2e4. \\n\ud1a0\ud070 \uc785\ub825 \u2192 \uc870\uc9c1 \uc120\ud0dd \u2192 CI/CD \uc124\uc815\ud560 Repository \uc120\ud0dd\uc744 \ud558\uba74 \ud30c\uc774\ud504\ub77c\uc778 \ucc3d\uc73c\ub85c \ub118\uc5b4\uac04\ub2e4. \\nJenkinsfile\uc744 \uc9c1\uc811 \uc791\uc131\ud558\uc5ec \uc124\uc815\ud558\uae30 \uc704\ud574 \uac04\ub2e8\ud558\uac8c print \ud558\ub098 \ucd9c\ub825\ud558\ub294 \uac83\uc73c\ub85c \uc124\uc815\ud588\ub2e4. \\n\\n![jenkins-blue-ocean2](./jenkins-blue-ocean2.png)\\n\\n\ud30c\uc774\ud504\ub77c\uc778\uc774 \uc2e4\ud589\ub420 \ud150\ub370 pipeline status\uc5d0\uc11c \uc544\ub798\uc640 \uac19\uc774 \ucd08\ub85d\ubd88\uc774 \ub728\uba74 \ub41c\ub2e4.\\n\\n![jenkins-blue-ocean3](./jenkins-blue-ocean3.png)\\n\\n### \\\\[Github Repsoitory\\\\] Jenkinsfile \uc124\uc815\\n\\n\ube14\ub8e8 \uc624\uc158 \uc2dc\uc791\uc744 \ud1b5\ud574 \uc124\uc815\ud558\uba74 Jenkinsfile\uc774 \ud558\ub098 \ub9cc\ub4e4\uc5b4\uc9c0\uace0, \uc544\ub798\uc640 \uac19\uc774 \uc6d0\ud558\ub294 \ud30c\uc774\ud504\ub77c\uc778\uc744 \uc124\uc815\ud55c\ub2e4.\\n\\n```bash\\npipeline {\\n agent any\\n stages {\\n stage(\'build and test\') {\\n steps {\\n sh \'/gradlew clean build\'\\n }\\n }\\n stage(\'zip\') {\\n steps {\\n sh \'mv ./build/libs/woowachat.jar .\'\\n sh \'zip -r woowachat.zip .platform delivery.jar Procfile\'\\n }\\n }\\n stage(\'upload\') {\\n steps {\\n sh \'aws s3 cp woowachat.zip s3://woowa-chat/woowachat.zip --region ap-northeast-2\'\\n }\\n }\\n stage(\'deploy\') {\\n steps {\\n sh \'aws elasticbeanstalk create-application-version --region ap-northeast-2 --application-name woowachat --version-label ${BUILD_TAG} --source-bundle S3Bucket=\\"woowa-chat\\",S3Key=\\"woowachat.zip\\"\'\\n sh \'aws elasticbeanstalk update-environment --region ap-northeast-2 --environment-name Woowachat-env --version-label ${BUILD_TAG}\'\\n }\\n }\\n }\\n}\\n```\\n\\n### \\\\[Github\\\\] Webhooks \uc124\uc815\\n\\n![github-hook](./github-hook.png)\\n\\npush \uc774\ubca4\ud2b8\uac00 \ubc1c\uc0dd\ud560 \ub54c `http://Jenkins\uc8fc\uc18c/github-webhook/` \ub85c post request\ub97c \ud558\ub3c4\ub85d \uc6f9\ud6c5\uc744 \uc124\uc815\ud55c\ub2e4.\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n[Install Jenkins - CentOS, Jenkins](https://www.jenkins.io/doc/book/installing/linux/#red-hat-centos) \\n[Nginx Reverse Proxy Configuration, Jenkins](https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-nginx/) \\n[Amazon Corretto 17 JDK Install, AWS](https://docs.aws.amazon.com/corretto/latest/corretto-17-ug/amazon-linux-install.html) \\n[Amazon Linux 2023 packages, AWS](https://docs.aws.amazon.com/linux/al2023/release-notes/all-packages-al2023-20230419.html)"},{"id":"tecochat-retrospective-1","metadata":{"permalink":"/tecochat-retrospective-1","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30.mdx","source":"@site/blog/2023-2/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30.mdx","title":"[\ud14c\ucf54\ucc57] 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30","description":"4\uc6d4 21\uc77c \uae08\uc694\uc77c","date":"2023-04-22T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 22\uc77c","tags":[{"label":"TecoChat","permalink":"/tags/teco-chat"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":5.68,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ud14c\ucf54\ucc57] 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30","slug":"tecochat-retrospective-1","tags":["TecoChat","Retrospective"]},"prevItem":{"title":"Jenkins\ub85c CI/CD \uc124\uc815","permalink":"/jenkins"},"nextItem":{"title":"[\ucc45] \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c","permalink":"/book-leadership-and-self-deception"}},"content":"### 4\uc6d4 21\uc77c \uae08\uc694\uc77c\\n\\n\ub808\ubca8 2\ub97c \uc2dc\uc791\ud55c \ub4a4 \ub0b4\uac00 \ud559\uc2b5\uc5d0 \ub300\ud55c \ubc29\ud5a5\uc744 \uc783\uc5b4\ubc84\ub838\ub2e4\ub294 \uc0dd\uac01\uc774 \ub4e4\uc5c8\ub2e4. \\n\ub808\ubca8 3, 4\uc5d0\uc11c \ub098\ub9cc\uc758 \uac15\uc810\uc744 \uac00\uc9c0\uace0 \uc2f6\uc5b4 \uace0\ubbfc\uc744 \ub9ce\uc774 \ud588\ub2e4. \\n\ub2e8\uc21c\ud788 \uc2a4\ud504\ub9c1\uc744 \uae4a\uac8c \uacf5\ubd80\ud558\ub294 \uac74 \ud6a8\uc728\uc774 \ub9ce\uc774 \ub5a8\uc5b4\uc9c4\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uae00\uc4f0\uae30 \uc218\uc0c1\uc73c\ub85c \ubc1b\uc740 \ucfe0\ud3f0\uc744 \uc0ac\uc6a9\ud574 \ube0c\ub77c\uc6b4\uc5d0\uac8c \ucee4\ud53c\ucc57\uc744 \uc2e0\uccad\ud588\uace0, \uc0ac\uc774\ub4dc \ud504\ub85c\uc81d\ud2b8\ub97c \ud574\ubcf4\ub77c\ub294 \ub2f5\uc744 \ubc1b\uc558\ub2e4. \\n\\n\ub098\ub294 \uc544\uc774\ub514\uc5b4\ub97c \ubabb\ub0b4\ub294 \ud3b8\uc778\ub370 \ube0c\ub77c\uc6b4\uc774 \uc544\uc774\ub514\uc5b4\uae4c\uc9c0 \ub358\uc838\uc8fc\uc168\ub2e4. \\n`Chat-GPT \uc11c\ube44\uc2a4\ub97c \ud06c\ub8e8\ub4e4\uc5d0\uac8c \uc81c\uacf5\ud558\uace0, \ud574\ub2f9 \ud06c\ub8e8\ub4e4\uc774 \uc9c8\ubb38\ud55c \ub0b4\uc6a9\uc744 \uacf5\uc720\ud560 \uc218 \uc788\ub294 \uac74 \uc5b4\ub5a4\uc9c0?` \\n\\n\uae30\uc220\uc774 \ubaa9\uc801\uc778 \uc0ac\uc774\ub4dc \ud504\ub85c\uc81d\ud2b8\ub97c \uc9c4\ud589\ud558\uba74 \uc88b\uc744 \uac83 \uac19\ub2e4\ub294 \ub2f5\ubcc0\uc744 \ub4e4\uc5c8\uace0, \ud63c\uc790 \uc544\ub2c8\uba74 \ud398\uc5b4\ud560 \uc218 \uc788\uc744 \uc815\ub3c4\uc758 \uc778\uc6d0\uc73c\ub85c \uc9c4\ud589\ud558\uba74 \uc88b\uaca0\ub2e4\uace0 \ud558\uc168\ub2e4. \\n\ud504\ub860\ud2b8\ub791 \uac04\ub2e8\ud558\uac8c \ubc30\ud3ec\uae4c\uc9c0 \ud574\ubcf8 \uacbd\ud5d8\uc774 \uc788\uc5b4\uc11c \ud63c\uc790\ud574\ub3c4 \ud06c\uac8c \uc5b4\ub835\uc9c0 \uc54a\uc744 \uac83 \uac19\uc544\uc11c \ud63c\uc790 \ud558\uae30\ub85c \ub9c8\uc74c\uc744 \uba39\uc5c8\ub2e4. \\n\\n\uc774\uac74 \ubabb\ucc38\uc9c0\\n\\n### \ub3c4\uba54\uc778 \uad6c\uc785 \uc131\uacf5?\\n\\n\ucee4\ud53c\ucc57\uc774 \ub05d\ub098\uace0 \uc9d1\uc73c\ub85c \ub3cc\uc544\uac00\ub294 \uae38\uc5d0 \ubc14\ub85c \ub3c4\uba54\uc778\uc744 \uad6c\ub9e4\ud558\ub824\uace0 namecheap\uc5d0\uc11c \uc801\ub2f9\ud55c \ub3c4\uba54\uc778\uc774 \uc5c6\uc744\uae4c \uac80\uc0c9\uc744 \uacc4\uc18d\ud588\ub2e4. \\n\ub9c8\uce58 \uc5b4\ub9b4 \ub54c \ud588\ub358 \uac8c\uc784 \ub2c9\ub124\uc784 \uc815\ud558\ub294 \uac83\ucc98\ub7fc \uc2dc\uac04\uc774 \uc624\ub798 \uac78\ub838\ub2e4. \\ndev, io, chat \ub3c4\uba54\uc778\uc774 \ud6c4\ubcf4\uc600\uace0 \uc9d1 \uac00\ub294 \uae38\uc5d0 \uacb0\uc815\ub9cc \ud558\ub2e4\uac00 \uad6c\ub9e4\ud558\uc9c0 \ubabb\ud588\ub2e4.\\n\\n### \ub9d0\ub791\uc758 DM\\n\\n\uc9d1\uc5d0 \uac00\uc11c \ubc25\uc744 \uba39\uace0 \ub9d0\ub791\uc774\ub791 DM \ud558\ub2e4 \ud504\ub85c\uc81d\ud2b8\ub97c \uac19\uc774 \ud558\uc790\ub294 \uc774\uc57c\uae30\uac00 \ub098\uc654\ub2e4. \\n\uc6b0\ud14c\ucf54 \ucd5c\uace0 \uace0\uc218 \ub9d0\ub791\uc758 \uc694\uad6c\ub77c \uc218\ub77d\ud558\uc9c0 \uc54a\uc73c\uba74 \ud6c4\ud3ed\ud48d\uc744 \uac10\ub2f9\ud560 \uc218 \uc5c6\uc5c8\ub2e4. \\n\\n\uc774\ub7f0\uc800\ub7f0 \ub300\ud654\ub97c \ub098\ub204\ub2e4\uac00 \ub09c \ube60\ub974\uac8c \ud504\ub85c\ud1a0\ud0c0\uc785\uc744 \ub9cc\ub4e4\uc5b4 \ubcf4\uace0 \uc2f6\uc5b4\uc11c \ud504\ub860\ud2b8\ub97c \uad6c\ud604\ud55c\ub2e4\uace0 \ud588\uace0, \ub9d0\ub791\uc740 GPT api\ub97c \uc870\uc0ac\ud558\uae30\ub85c \ud588\ub2e4. \\n\ucd94\uac00\ub85c \ub3c4\uba54\uc778\uc5d0 \uad00\ud55c \uc774\uc57c\uae30\ub97c \ud558\ub2e4\uac00 woowachat\uc774 \uc5b8\uae09\ub418\uc5c8\uace0, namecheap\uc5d0\uc11c chat \ub3c4\uba54\uc778\uc744 \uc0ac\uc6a9\ud55c woowa.chat\uc73c\ub85c \uad6c\ub9e4\ud588\ub2e4. \\n\uc774\ud6c4\uc5d0 teco.chat\uc73c\ub85c \ubcc0\uacbd\ud588\ub2e4!\\n\\n### \ub3c4\uba54\uc778 \uc124\uc815 \ubc0f \ubc30\ud3ec\\n\\n\ud1a0\uc694\uc77c\uc5d0 \uad6c\ub9e4\ud55c \ub3c4\uba54\uc778\uc744 CDN, \ubcf4\uc548 \ub4f1 \ub2e4\uc591\ud55c \uae30\ub2a5\uc744 \uc81c\uacf5\ud558\ub294 Cloudflare\uc5d0 \ub3c4\uba54\uc778 \ub4f1\ub85d\uc744 \ud588\ub2e4. \\n\ub098\uc5d0\uac8c \uc775\uc219\ud55c Nuxt3\ub97c \uc0ac\uc6a9\ud558\uae30\ub85c \ud588\uace0, Cloudflare Pages\ub97c \uc774\uc6a9\ud558\uc5ec \ubc30\ud3ec\ud588\ub2e4. \\n\\n### GPT\\n\\n\ubb34\ub8cc \ud06c\ub808\ub527\uc744 \uc0ac\uc6a9\ud558\ub2c8 api limit\uc774 \uc788\uc5b4 \ubd84\ub2f9 3\ubc88\ubc16\uc5d0 \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc5c8\ub2e4. \\n\uc77c\ub2e8 \ubc31\uc5d4\ub4dc\ub97c \uad6c\ucd95\ud558\uae30 \uc804\uc5d0\ub294 \ubb34\ub8cc \ud06c\ub808\ub527\uc744 \uc0ac\uc6a9\ud560 \uc0dd\uac01\uc774\ub2e4. \\n\\n### Sonarcloud\\n\\n\uc815\uc801 \ucf54\ub4dc \ubd84\uc11d \ub3c4\uad6c\ub85c Sonarcloud\ub97c \uc801\uc6a9\ud588\ub2e4. \\nSonarcloud\ub294 SonarQube\uc758 SaaS \ubc84\uc804\uc774\uace0 \uc0ac\uc6a9\uc774 \ub9e4\uc6b0 \ud3b8\ud558\ub2e4. \\n\uc608\uc804\uc5d0 Sonarcloud\ub97c \uc0ac\uc6a9\ud560 \ub550 \ubc84\ud2bc \uba87 \ubc88 \ub204\ub974\uba74 \uc801\uc6a9\ud560 \uc218 \uc788\uc5c8\ub294\ub370, \uc774\ubc88\uc5d0\ub294 \ubc14\ub85c github action\uc744 \uc0ac\uc6a9\ud558\ub77c\ub294 \uc548\ub0b4 \ud398\uc774\uc9c0\ub85c \uc774\ub3d9\ud588\ub2e4. \\nSonarcloud\uac00 \uc790\uccb4\uc801\uc73c\ub85c github repository\uc5d0 push \ud558\uba74 \uc815\uc801 \ubd84\uc11d\uc744 \ud574\uc8fc\ub294 \uae30\ub2a5\uc744 \uc6d0\ud588\uace0, Administration -> Analysis Method\uc5d0 Automatic Analysis\ub97c \uc124\uc815\ud558\ub2c8 \ub418\uc5c8\ub2e4. \\n\ub108\ubb34 \uaf41\uaf41 \uc228\uaca8\uc838\uc788\ub124\\n\\n### Tiptap\\n\\n\ucf54\ub4dc \ud558\uc774\ub77c\uc774\ud305 \uae30\ub2a5\uc744 \ub123\uace0 \uc2f6\uc5b4\uc11c Tiptap\uc744 \uc0ac\uc6a9\ud588\ub2e4. \\nTiptap\uc740 Headless WYSIWYG \uc5d0\ub514\ud130\ub85c \uc0ac\uc6a9\uc790 \uc815\uc758 \uae30\ub2a5\uc5d0 \ud2b9\ud654\ub418\uc5b4\uc788\ub294 \uc5d0\ub514\ud130\ub2e4. \\n\uc544\uc9c1 Tiptap\uc774 \uc81c\uacf5\ud558\ub294 \ubaa8\ub4e0 \uae30\ub2a5\uc744 \uc790\uc5f0\uc2a4\ub7fd\uac8c \uc0ac\uc6a9\ud558\uc9c0\ub294 \ubabb\ud558\uc9c0\ub9cc CodeBlockLowlight \ud50c\ub7ec\uadf8\uc778\uc744 \uc0ac\uc6a9\ud558\uc5ec \ucf54\ub4dc \ube14\ub85d\uc744 \uc608\uc058\uac8c \ucd9c\ub825\ud560 \uc218 \uc788\uc5c8\ub2e4. \\napi \ubc18\ud658\uac12 \uadf8\ub300\ub85c tiptap\uc758 content\uc5d0 \uc124\uc815\ud588\ub354\ub2c8 \ucf54\ub4dc \ube14\ub85d\uc774 \uc124\uc815\ub418\uc9c0 \uc54a\uc544\uc11c \ubc31 \ud2f1 3\uac1c\ub97c `
`\ub85c \ubcc0\ud658\ud588\ub2e4.  \\n\ucd94\uac00\ub85c \ub744\uc5b4\uc4f0\uae30\ub3c4 \uc801\uc6a9\ub418\uc9c0 \uc54a\uc544\uc11c `\\\\n`\ub97c `
`\ud0dc\uadf8\ub85c \ubcc0\ud658\ud588\ub2e4. \\n\ubcc0\ud658\ud558\ub294 \ub85c\uc9c1\uc740 GPT\uc758 \ub3c4\uc6c0\uc744 \ub9ce\uc774 \ubc1b\uc558\ub2e4. \\n\\n```ts\\nconst replaceCodeFences = (input: String) => {\\n const codeFencesRegex = /```([\\\\w-]*)\\\\n([\\\\s\\\\S]*?)\\\\n```/g;\\n return input\\n .replace(codeFencesRegex, (match, p1, p2) => {\\n const languageClass = p1 ? ` class=\\"language-${p1}\\"` : \\"\\";\\n return `
${p2}
`;\\n })\\n .replace(/\\\\n/g, \\"
\\");\\n};\\n```\\n\\nTiptap\uc744 \uc801\uc6a9\ud558\ub2c8 \ub2e4\uc74c\uacfc \uac19\uc774 \uae54\ub054\ud55c \ucf54\ub4dc \ube14\ub85d\uc744 \ubcfc \uc218 \uc788\uc5c8\ub2e4. \\n\\n![tecochat](./teco-chat.png)\\n\\n### \ud3f0\ud2b8 \ubc0f favicon \uc801\uc6a9\\n\\n\ud0c0\uc774\ud2c0\uc740 \ubc30\ub2ec\uc758\ubbfc\uc871 \ub3c4\ud604\uccb4, \ub0b4\uc6a9\uc740 IBM Plex Sans\ub97c \uc0ac\uc6a9\ud588\ub2e4. \\n\ucd94\uac00\ub85c favicon\ub3c4 \uac04\ub2e8\ud558\uac8c \uc801\uc6a9\ud574\uc11c \ub9cc\uc871\uc2a4\ub7ec\uc6e0\ub2e4."},{"id":"book-leadership-and-self-deception","metadata":{"permalink":"/book-leadership-and-self-deception","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-08-\uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c.mdx","source":"@site/blog/2023-2/2023-04-08-\uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c.mdx","title":"[\ucc45] \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c","description":"\ucc45 \uc815\ubcf4","date":"2023-04-08T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 8\uc77c","tags":[{"label":"Book","permalink":"/tags/book"}],"readingTime":5.16,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ucc45] \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c","slug":"book-leadership-and-self-deception","tags":["Book"]},"prevItem":{"title":"[\ud14c\ucf54\ucc57] 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30","permalink":"/tecochat-retrospective-1"},"nextItem":{"title":"InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08","permalink":"/innodb-lock"}},"content":"### \ucc45 \uc815\ubcf4\\n\\n> \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c \\n> \uc544\ube48\uc800\uc5f0\uad6c\uc18c\\n> \\n\\n### \uc790\uae30\uae30\ub9cc\uacfc \uc790\uae30\ubc30\ubc18\\n\\n\ucc45\uc5d0\uc11c\ub294 \uc790\uae30\uae30\ub9cc\uacfc \uc790\uae30\ubc30\ubc18\uc5d0 \ub300\ud55c \ub0b4\uc6a9\uc744 \ub2e4\ub8ec\ub2e4. \\n- \uc790\uae30\uae30\ub9cc: \uc790\uc2e0\uc758 \ubb38\uc81c\ub97c \uc778\uc815\ud558\uc9c0 \uc54a\ub294 \uac83 \\n- \uc790\uae30\ubc30\ubc18: \ub2e4\ub978 \uc0ac\ub78c\uc744 \uc704\ud574 \ubb34\uc5b8\uac00 \ud574\uc57c\ub9cc \ud55c\ub2e4\ub294 \uc0dd\uac01\uc744 \ubc18\ud558\ub294 \ud589\uc704\\n\\n\uc790\uae30\ubc30\ubc18\uc744 \ud55c\ub2e4\uba74 \uc790\uae30\uae30\ub9cc \uc0c1\ud0dc\uac00 \ub41c\ub2e4. \\n\uc790\uae30\uae30\ub9cc \uc0c1\ud0dc\uc5d0 \ube60\uc9c0\ub294 \uac83\uc744 \ucc45\uc5d0\uc11c\ub294 \uc0c1\uc790 \uc548\uc5d0 \ub4e4\uc5b4\uac04\ub2e4\uace0 \ud45c\ud604\ud55c\ub2e4. \\n\\n### \uc77d\uace0 \ub098\uc11c\\n\\n\ucd5c\uadfc\uc5d0 \uc77d\uc740 \ucc45 \uc911 \uac00\uc7a5 \ub9c8\uc74c\uc774 \ubd88\ud3b8\ud588\ub2e4. \\n\uadf8\ub807\uae30\uc5d0 \ub354\ub354\uc6b1 \ub098\uc5d0\uac8c \ud544\uc694\ud55c \ub0b4\uc6a9\uc774 \ub2f4\uaca8\uc788\uc5c8\ub2e4. \\n\\n\uc0b4\uba74\uc11c \ub9ce\uc740 \uc120\ud0dd\uc758 \uc21c\uac04\uc774 \uc874\uc7ac\ud588\uace0, \uadf8 \uc21c\uac04\ub9c8\ub2e4 \uc790\uae30\ubc30\ubc18\uc744 \ud0dd\ud558\ub294 \uacbd\uc6b0\uac00 \ub9ce\uc558\ub2e4. \\n\uc791\uac8c\ub294 \uc9d1\uc548\uc77c\uc744 \ud574\uc57c \ud558\ub294\ub370 \ubab8\uc774 \uc870\uae08 \ud798\ub4e4\ub2e4\uace0 \ud558\uc9c0 \uc54a\uac70\ub098 \\n\ud06c\uac8c\ub294 \uc798\ubabb\uc744 \uc778\uc815\ud574\uc57c \ud558\ub294 \uc0c1\ud669\uc5d0\uc11c \uadf8\ub7ec\uc9c0 \uc54a\uc740 \uacbd\uc6b0\uac00 \uc788\uc5c8\ub2e4. \\n\uc774\ub7f0 \uc0c1\ud669\uc774 \ubc18\ubcf5\ub418\uc5b4 \uacb0\uad6d \uc0c1\uc790 \uc548\uc5d0 \ub098 \uc790\uc2e0\uc744 \uac00\ub450\ub294 \uacbd\uc6b0\uac00 \ub9ce\uc558\ub2e4. \\n\\n\ub354 \ub098\uc740 \uc0b6\uc744 \uc704\ud574 \ub0b4\uac00 \uc0c1\uc790 \uc548\uc5d0 \uc788\ub294\uc9c0 \uc9c0\uc18d\uc801\uc73c\ub85c \ud655\uc778\ud558\uace0, \uc0c1\uc790 \ubc16\uc73c\ub85c \ub098\uac00\ub824\ub294 \uc5f0\uc2b5\uc744 \ud574\uc57c\uaca0\ub2e4. \\n\ub113\uc740 \uc2dc\uc120\uc744 \uac00\uc9c0\uace0, \ud56d\uc0c1 \ub0b4\uac00 \ud2c0\ub9b4 \uc218 \uc788\ub2e4\ub294 \uac83\uc744 \uc0dd\uac01\ud558\uace0 \uc0b4\uc544\uac00\uc790. \\n\\n### \ubc11\uc904 \uce5c \ubb38\uc7a5\ub4e4\\n\\n> \uc6b0\ub9ac\uc758 \uc0dd\uac01\uc740 \uc9c0\uc2dd\ubcf4\ub2e4 \uc791\ub2e4. \\n\uc6b0\ub9ac\uc758 \uc9c0\uc2dd\uc740 \uc0ac\ub791\ubcf4\ub2e4 \uc791\ub2e4. \\n\uc6b0\ub9ac\uc758 \uc0ac\ub791\uc740 \uc874\uc7ac\ubcf4\ub2e4 \uc791\ub2e4. \\n\uadf8\ub9ac\uace0 \uc6b0\ub9ac\uac00 \uc0dd\uac01\ud558\ub294 \ub098\ub294 \uc2e4\uc81c\uc758 \ub098\ubcf4\ub2e4 \uadf8\ub9cc\ud07c \uc791\ub2e4. \\nR. D. \ub7ad \\np.19\\n>\\n\\n> \uc6b0\ub9ac\uac00 \uc678\uc801\uc73c\ub85c \uc5b4\ub5a4 \ud589\ub3d9\uc744 \ud558\ub4e0\uc9c0 \uac04\uc5d0, \uc0ac\ub78c\ub4e4\uc740 \uc6b0\ub9ac \ub9c8\uc74c\uc5d0\uc11c \uadf8\ub4e4\uc744 \uc5b4\ub5bb\uac8c \ub300\ud558\uace0 \uc788\ub294\uc9c0\uc5d0 \ub530\ub77c \uc8fc\ub85c \ubc18\uc751\ud569\ub2c8\ub2e4. \\n\uc6b0\ub9ac\uac00 \uc0ac\ub78c\ub4e4\uc5d0 \ub300\ud574 \uc5b4\ub5bb\uac8c \ub290\ub07c\uac8c \ub418\ub294\uc9c0\ub294 \uc6b0\ub9ac\uac00 \uc0c1\uc790 \uc548\uc5d0 \uc788\ub294\uc9c0 \ud639\uc740 \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294\uc9c0\uc5d0 \ub530\ub77c \ub2ec\ub77c\uc9c0\uac8c \ub429\ub2c8\ub2e4. \\np.66\\n>\\n\\n> \ube44\ub09c\uc740 \uac10\uc815\uc5d0 \uc18d\ud558\uace0 \ub099\uad00\uc740 \uc758\uc9c0\uc5d0 \uc18d\ud55c\ub2e4. \\n\uc778\uac04\uc740 \uac10\uc815\ubcf4\ub2e4 \ub354 \ud070 \uc874\uc7ac\uc774\ub2e4. \\n\uc54c\ub7ad, \ud0c1\ub2db\ud55c \\np.103\\n>\\n\\n> \uc6b0\ub9ac\uac00 \uc790\uc2e0\uc5d0\uac8c\ub9cc \uc9d1\uc911\ud558\uace0 \uc788\ub294 \ud55c, \ud63c\uc790\uc11c \uc77c\ud558\ub294 \uac83 \uc774\uc0c1\uc758 \ucc3d\uc870\uc801\uc778 \uacb0\uacfc\ub098 \ud611\ub825\uc744 \uc774\ub04c\uc5b4 \ub0b8\ub2e4\ub294 \uac83\uc740 \ubd88\uac00\ub2a5\ud569\ub2c8\ub2e4. \\n\uc624\ub298\ub0a0 \uacbd\uc81c \ud658\uacbd\uc5d0\uc11c\ub294 \ud63c\uc790\uc11c\ub294 \uc77c\uc758 \uacb0\uacfc\ub97c \ud0c1\uc6d4\ud558\uac8c \ub9cc\ub4e4\uc5b4 \ub0b4\uae30\uac00 \uc5b4\ub835\uc2b5\ub2c8\ub2e4. \\n\ub0b4\uac00 \uc911\uc2ec\uc774\uc5b4\uc57c \ub41c\ub2e4\ub294 \ud3d0\uc1c4\uc801\uc778 \uc0ac\uace0\ub294 \ud568\uaed8 \uc77c\ud558\ub294 \uc0ac\ub78c\ub4e4\uc758 \uc5f4\uc815\uc744 \ubd88\ub7ec\uc624\uc9c0 \ubabb\ud569\ub2c8\ub2e4. \\np.175\\n> \\n\\n> \uc194\uc9c1\ud568\uc740 \uc6b0\ub9ac\uc758 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \uc5f4\uc1e0\uc785\ub2c8\ub2e4. \\n\uadf8\uac83\uc740 \uc790\uc2e0\uc758 \ud589\ub3d9\uacfc \uad00\ub828\ub41c \uc0ac\ub78c\uc5d0 \ub300\ud574 \uae30\uaebc\uc774 \uc0ac\uacfc\ub97c \ud558\ub294 \uac83\uc785\ub2c8\ub2e4. \\n\uadf8\uac83\ub9cc\uc774 \uc2e4\ud0c0\ub798\ucc98\ub7fc \uc5c9\ud0a8 \uad00\uacc4\uc758 \ubb38\uc81c\ub97c \ud574\uacb0\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc774\uc8e0. \\np.188\\n> \\n\\n> \ub204\uad70\uac00\ub97c \ub098\uc640 \uac19\uc774 \ub3d9\uc77c\ud55c \uac00\uce58\ub97c \uc9c0\ub2cc \ud55c \uc778\uac04\uc73c\ub85c \uc0dd\uac01\ud574\uc11c \uadf8 \uc0ac\ub78c\uc744 \uc704\ud574 \ub0b4\uac00 \uc0c1\uc790 \ubc16\uc5d0 \uacc4\uc18d \uba38\ubb34\ub974\uace0 \uc2f6\uc740 \uc5f4\ub9dd\uc774 \uc0dd\uae38 \ub54c, \ub098\ub294 \uc774\ubbf8 \uadf8 \uc0ac\ub78c\uc5d0 \ub300\ud574 \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub2e4. \\np.214\\n> \\n\\n> \ub300\ubd80\ubd84\uc758 \uc0ac\ub78c\ub4e4\uc774 \uad00\uacc4 \uae30\uc220\uc744 \uac00\uc9c0\uace0 \uadf8\ub4e4\uc774 \uacaa\uace0 \uc788\ub294 \ubb38\uc81c\ub97c \ubc14\ub85c\uc7a1\uc73c\ub824\uace0 \ud558\ub294 \ub178\ub825\uc774 \uacb0\uc2e4\uc744 \uc5bb\uc9c0 \ubabb\ud558\ub294 \uac83\uc740 \uacb0\ucf54 \uadf8\ub7ec\ud55c \uae30\uc220 \ubd80\uc871 \ub54c\ubb38\uc5d0 \uc0dd\uae30\ub294 \uac83\uc774 \uc544\ub2d9\ub2c8\ub2e4. \\n\uadf8\uac83\ub4e4\uc740 \uc790\uae30\ubc30\ubc18 \ub54c\ubb38\uc5d0 \uc0dd\uaca8\ub0a9\ub2c8\ub2e4. \\np.224\\n>\\n\\n> \uc6b0\ub9ac\ub294 \ud568\uaed8 \uc77c\ud558\uace0 \uc6b0\ub9ac\uc640 \ud568\uaed8 \uc0b4\uc544\uac00\ub294 \uc0ac\ub78c\uc774 \uc9c4\uc815\uc73c\ub85c \ub204\uad6c\uc778\uc9c0 \uc54c\uc9c0 \ubabb\ud569\ub2c8\ub2e4. \\n\uc6b0\ub9ac\uac00 \uadf8\ub4e4\uacfc \uc9c4\uc815\uc73c\ub85c \ud568\uaed8 \uc18c\ud1b5\ud558\uae30 \uc804\uae4c\uc9c0\ub294 \uc6b0\ub9ac\ub294 \uadf8\ub4e4\uc758 \uac00\uce58\ub97c \uc798 \ubaa8\ub985\ub2c8\ub2e4. \\n\uc6b0\ub9ac\uc758 \uc704\ub300\ud568\uc774\ub780 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc758 \uc704\ub300\ud55c \uc810\uc744 \ubc1c\uacac\ud574 \uc8fc\ub294 \uac83\uc5d0 \uc788\uc2b5\ub2c8\ub2e4. \\np.280\\n>"},{"id":"innodb-lock","metadata":{"permalink":"/innodb-lock","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-07-InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx","source":"@site/blog/2023-2/2023-04-07-InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx","title":"InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08","description":"InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08","date":"2023-04-07T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 7\uc77c","tags":[{"label":"DataBase","permalink":"/tags/data-base"},{"label":"Lock","permalink":"/tags/lock"},{"label":"InnoDB","permalink":"/tags/inno-db"}],"readingTime":5.805,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08","slug":"innodb-lock","tags":["DataBase","Lock","InnoDB"]},"prevItem":{"title":"[\ucc45] \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c","permalink":"/book-leadership-and-self-deception"},"nextItem":{"title":"MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08","permalink":"/mysql-lock"}},"content":"## InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08\\n\\nMySQL\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uc7a0\uae08\uacfc \ubcc4\uac1c\ub85c \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4 \ub0b4\ubd80\uc5d0\uc11c \ub85c\uc6b0 \ub2e8\uc704\uc758 \uc7a0\uae08\uc744 \uc9c0\uc6d0\ud55c\ub2e4. \\n\ubcf4\ud1b5 \uba85\uc2dc\uc801\uc73c\ub85c \uc7a0\uae08\uc744 \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0\ub294 \ub4dc\ubb3c\uace0, \uaca9\ub9ac \uc218\uc900\uc5d0 \ub530\ub77c \ubb35\uc2dc\uc801\uc73c\ub85c \uc7a0\uae08\uc774 \uc0ac\uc6a9\ub41c\ub2e4. \\n\\n\ub3d9\uc2dc\uc131 \uc81c\uc5b4 \ubc29\uc2dd\uc5d0\ub294 \ub099\uad00\uc801\uc778 \ubc29\uc2dd\uacfc \ube44\uad00\uc801\uc778 \ubc29\uc2dd\uc774 \uc788\ub2e4. \\nInnoDB\ub294 \uae30\ubcf8\uc801\uc73c\ub85c MVCC(\ub2e4\uc911 \ubc84\uc804 \ub3d9\uc2dc\uc131 \uc81c\uc5b4)\ub97c \ud1b5\ud574 \ub099\uad00\uc801\uc778 \ubc29\uc2dd\uc744 \uc0ac\uc6a9\ud558\uace0 \ub77d\uc744 \ud1b5\ud574 \ud2b9\uc815 \uc0c1\ud669\uc5d0\uc11c \ube44\uad00\uc801\uc778 \ubc29\uc2dd\uc744 \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n:::note \ub099\uad00\uc801 \ub3d9\uc2dc\uc131 \uc81c\uc5b4(OCC, Optimistic concurrency control)\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc774 \uc11c\ub85c \ucda9\ub3cc\ud558\uc9c0 \uc54a\ub294\ub2e4\uace0 \uac00\uc815\ud558\ub294 \ubc29\uc2dd \\n\\n:::\\n\\n:::note \ube44\uad00\uc801 \ub3d9\uc2dc\uc131 \uc81c\uc5b4(PCC, Pessimistic Concurrency Control)\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc774 \ucda9\ub3cc\ud558\ub294 \uac00\uc815\ud558\uc5d0 \uc7a0\uae08\uc744 \uac70\ub294 \ubc29\uc2dd \\n\uc77c\ubc18\uc801\uc73c\ub85c\xa0Shared Lock, Exclusive Lock\uc744 \ud1b5\ud574 \uc774\ub97c \uad6c\ud604\ud55c\ub2e4.\\n\\n:::\\n\\n### Shared & Exclusive Locks\\n\\nInnoDB\ub294 \ub85c\uc6b0 \ub2e8\uc704\uc758 \uc7a0\uae08\uc744 \uc218\ud589\ud560 \ub54c \uacf5\uc720 \uc7a0\uae08\uacfc \ubc30\ud0c0\uc801 \uc7a0\uae08\uc744 \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n**\uacf5\uc720 \uc7a0\uae08(S, shared lock)**\\n\\n\ub370\uc774\ud130 \uc870\ud68c\ub97c \uc704\ud55c \ub77d, \uc77d\uae30 \uc7a0\uae08(read lock)\uc73c\ub85c\ub3c4 \ubd88\ub9b0\ub2e4. \\n\ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \uc77d\uae30\uac00 \uac00\ub2a5\ud558\uc9c0\ub9cc, \uc4f0\uae30\ub294 \ubd88\uac00\ub2a5\ud558\ub2e4. \\n\uc608) `SELECT * FROM table_name WHERE id = 1 LOCK IN SHARE MODE;`\\n\\n**\ubc30\ud0c0\uc801 \uc7a0\uae08(X, exclusive lock)** \\n\\n\ub370\uc774\ud130 \ubcc0\uacbd\uc744 \uc704\ud55c \ub77d, \uc4f0\uae30 \uc7a0\uae08(write lock)\uc73c\ub85c\ub3c4 \ubd88\ub9b0\ub2e4. \\n\ub77d\uc744 \uac74 \ud2b8\ub79c\uc7ad\uc158\ub9cc\uc774 \ud574\ub2f9 \ub370\uc774\ud130\uc5d0 \uc811\uadfc \uac00\ub2a5\ud558\ub2e4. \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc758 \uacbd\uc6b0 \uc77d\uae30, \uc4f0\uae30\uac00 \ubd88\uac00\ub2a5\ud558\ub2e4. \\n\uc608) `SELECT * FROM table_name WHERE id = 1 FOR UPDATE;`\\n\\n### Intention Locks\\n\\nInnoDB\ub294 \ub85c\uc6b0 \ub2e8\uc704 \uc7a0\uae08\uacfc \ud14c\uc774\ube14 \uc7a0\uae08\uc758 \uacf5\uc874\uc744 \uc704\ud574 \uc778\ud14d\uc158 \uc7a0\uae08\uc744 \uc9c0\uc6d0\ud55c\ub2e4. \\n\ud14c\uc774\ube14\uc5d0 \uc788\ub294 \ub85c\uc6b0\uc5d0 \ub300\ud574\uc11c \ub098\uc911\uc5d0 \uc694\uccad\ub418\ub294 \uac83\uc774 \uc5b4\ub5a4 \ud615\ud0dc\uc758 \uc7a0\uae08\uc778\uc9c0 \uac00\ub9ac\ud0a4\uae30 \uc704\ud574 \uc0ac\uc6a9\ub41c\ub2e4. \\n\uae30\ubcf8\uc801\uc73c\ub85c \ub85c\uc6b0 \ub2e8\uc704 \uc7a0\uae08\uc744 \uc218\ud589\ud558\uae30 \uc804\uc5d0 \uc778\ud150\uc158 \uc7a0\uae08\uc744 \uba3c\uc800 \ud68d\ub4dd\ud55c\ub2e4. \\n\uc778\ud150\uc158 \ub77d\uc740 \uae30\ubcf8\uc801\uc73c\ub85c \ucda9\ub3cc\uc744 \ubc29\uc9c0\ud558\uace0 \ub370\ub4dc\ub77d\uc744 \ubc29\uc9c0\ud558\ub294 \uc5ed\ud560\uc744 \ud55c\ub2e4. \\n\\n**\uc778\ud150\uc158 \uacf5\uc720 \uc7a0\uae08(IS, intention shared lock)**\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc774 \ud14c\uc774\ube14\uc758 \uac1c\ubcc4 \ub85c\uc6b0\uc5d0 \ub300\ud55c \uacf5\uc720 \uc7a0\uae08\uc744 \uc218\ud589\ud558\ub294 \uac83\uc744 \uc758\ubbf8\ud55c\ub2e4.\\n\\n**\uc778\ud150\uc158 \ubc30\ud0c0\uc801 \uc7a0\uae08(IX, intention exclusive lock)** \\n\\n\ud2b8\ub79c\uc7ad\uc158\uc774 \ud14c\uc774\ube14\uc758 \uac1c\ubcc4 \ub85c\uc6b0\uc5d0 \ub300\ud55c \ubc30\ud0c0\uc801 \uc7a0\uae08\uc744 \uc218\ud589\ud558\ub294 \uac83\uc744 \uc758\ubbf8\ud55c\ub2e4.\\n\\n** \uc7a0\uae08\uac04\uc758 \ud638\ud658\uc131 **\\n\\n| | X | IX | S | IS |\\n| --- | --- | --- | --- | --- |\\n| X | Conflict | Conflict | Conflict | Conflict |\\n| IX | Conflict | Compatible | Conflict | Compatible |\\n| S | Conflict | Conflict | Compatible | Compatible |\\n| IS | Conflict | Compatible | Compatible | Compatible |\\n\\n### Record Locks\\n\\n\ub808\ucf54\ub4dc \uc790\uccb4\ub9cc\uc744 \uc7a0\uadf8\ub294 \ub77d\uc774\ub2e4. \\nInnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc740 \ub808\ucf54\ub4dc \uc790\uccb4\uac00 \uc544\ub2c8\ub77c \uc778\ub371\uc2a4\uc758 \ub808\ucf54\ub4dc\ub97c \uc7a0\uadfc\ub2e4. \\n\\n### Gap Locks\\n\\n\ub808\ucf54\ub4dc\uc640 \ubc14\ub85c \uc778\uc811\ud55c \ub808\ucf54\ub4dc \uc0ac\uc774\uc758 \uac04\uaca9\ub9cc\uc744 \uc7a0\uadf8\ub294 \ub77d\uc774\ub2e4. \\n\ub808\ucf54\ub4dc\uc640 \ub808\ucf54\ub4dc \uc0ac\uc774\uc758 \uac04\uaca9\uc5d0 \uc0c8\ub85c\uc6b4 \ub808\ucf54\ub4dc\uac00 \uc0dd\uc131\ub418\ub294 \uac83\uc744 \uc81c\uc5b4\ud558\uace0, \ub125\uc2a4\ud2b8 \ud0a4 \ub77d\uc758 \uc77c\ubd80\ub85c \uc0ac\uc6a9\ub41c\ub2e4. \\n\\n### Next-Key Locks\\n\\n\ub808\ucf54\ub4dc \ub77d\uacfc \uac2d \ub77d\uc744 \ud569\uccd0\ub193\uc740 \ud615\ud0dc\uc758 \uc7a0\uae08\uc73c\ub85c \ub808\ucf54\ub4dc\uc640 \uadf8 \ub808\ucf54\ub4dc \uc55e\uc758 \uac2d \ub77d\uc744 \ud3ec\ud568\ud55c\ub2e4. \\n`REPEATABLE READ` \uaca9\ub9ac \uc218\uc900\uc5d0\uc11c \ud32c\ud140 \ub9ac\ub4dc\ub97c \ubc29\uc9c0\ud558\uae30 \uc704\ud55c \uc7a0\uae08\uc774\ub2e4. \\n\\n### AUTO-INC Locks\\n\\n`AUTO_INCREMENT` \uce7c\ub9bc\uc774 \uc0ac\uc6a9\ub41c \ud14c\uc774\ube14\uc5d0 \ub3d9\uc2dc\uc5d0 \uc5ec\ub7ec \ub808\ucf54\ub4dc\uac00 `INSERT`\ub418\ub294 \uacbd\uc6b0, \uac01 \ub808\ucf54\ub4dc\ub294 \uc911\ubcf5\ub418\uc9c0 \uc54a\uace0 \uc800\uc7a5\ub41c \uc21c\uc11c\ub300\ub85c \uc99d\uac00\ud558\ub294 \uc77c\ub828\ubc88\ud638 \uac12\uc744 \uac00\uc838\uc57c \ud55c\ub2e4. \\nInnoDB \ub294 \ub0b4\ubd80\uc801\uc73c\ub85c AUTO-INC \ub77d\uc774\ub77c\uace0 \ud558\ub294 \ud14c\uc774\ube14 \uc218\uc900\uc758 \uc7a0\uae08\uc744 \uc0ac\uc6a9\ud55c\ub2e4. \\n\ud2b8\ub79c\uc7ad\uc158\uacfc \uad00\uacc4 \uc5c6\uc774 `INSERT`\ub098 `REPLACE` \ubb38\uc7a5\uc5d0\uc11c `AUTO_INCREMENT` \uac12\uc744 \uac00\uc838\uc624\ub294 \uc21c\uac04\ub9cc \ub77d\uc774 \uac78\ub838\ub2e4\uac00 \ud574\uc81c\ub41c\ub2e4.\\n\\n### \uc7a0\uae08 \uc608\uc2dc\\n\\n```sql\\n-- \ub808\ucf54\ub4dc\ub294 id \uae30\uc900 10, 20, 30, 40, 50\uc774 \uc788\ub2e4\uace0 \uac00\uc815\\n-- Record Locks: 10\uc5d0 \ub300\ud574 \ub77d\uc774 \uac78\ub9b0\ub2e4.\\nSELECT * FROM table_name where id = 10 for update;\\n\\n-- Gap Locks: 51\ubd80\ud130 PositiveInfinity\uae4c\uc9c0 \ub77d\uc774 \uac78\ub9b0\ub2e4.\\nSELECT * FROM table_name where id > 100 for update;\\n\\n-- Next-Key Locks: 21\ubd80\ud130 30, 31\ubd80\ud130 40\uc5d0 \ub77d\uc774 \uac78\ub9b0\ub2e4.\\nSELECT * FROM table_name where id BETWEEN 25 AND 35 for update;\\n```\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\nReal My SQL 8.0 - 5\uc7a5 \ud2b8\ub79c\uc7ad\uc158\uacfc \uc7a0\uae08, \ubc31\uc740\ube48, \uc774\uc131\uc6b1 \\n[Optimistic and Pessimistic record locking, IBM](https://www.ibm.com/docs/en/rational-clearquest/9.0.0?topic=clearquest-optimistic-pessimistic-record-locking) \\n[MySQL Innodb Locks, cecil1018](https://cecil1018.wordpress.com/2016/06/18/mysql-innodb-locks/) \\n[MySQL 8.0 InnoDB Locks, MySQL](https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html) \\n[Locks Set by Different SQL Statements in InnoDB, MySQL](https://dev.mysql.com/doc/refman/8.0/en/innodb-locks-set.html)"},{"id":"mysql-lock","metadata":{"permalink":"/mysql-lock","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-06-MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx","source":"@site/blog/2023-2/2023-04-06-MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx","title":"MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08","description":"MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08","date":"2023-04-06T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 6\uc77c","tags":[{"label":"DataBase","permalink":"/tags/data-base"},{"label":"Lock","permalink":"/tags/lock"},{"label":"MySQL","permalink":"/tags/my-sql"}],"readingTime":4.405,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08","slug":"mysql-lock","tags":["DataBase","Lock","MySQL"]},"prevItem":{"title":"InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08","permalink":"/innodb-lock"},"nextItem":{"title":"\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900","permalink":"/transaction-and-isolation"}},"content":"## MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08\\n\\nMySQL\uc5d0\uc11c\uc758 \ub77d\uc740 \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4 \ub808\ubca8\uacfc, MySQL \uc5d4\uc9c4 \ub808\ubca8\ub85c \ub098\ub20c \uc218 \uc788\ub2e4. \\nMySQL \uc5d4\uc9c4 \ub808\ubca8\uc758 \uc7a0\uae08\uc740 \ubaa8\ub4e0 \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc5d0 \uc601\ud5a5\uc744 \ubbf8\uce5c\ub2e4. \\n\\n### \uae00\ub85c\ubc8c \ub77d(Global lock)\\n\\nMySQL\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uc7a0\uae08 \uc911 \uac00\uc7a5 \ub113\uc740 \ubc94\uc704\ub97c \uac00\uc9c0\uace0 \uc788\ub294 \uc7a0\uae08\uc774\ub2e4. \\n - \uc601\ud5a5\uc744 \ubbf8\uce58\ub294 \ubc94\uc704\ub294 \ud574\ub2f9 \uc11c\ubc84 \uc804\uccb4\uc774\ub2e4.\\n - \uc791\uc5c5 \ub300\uc0c1 \ud14c\uc774\ube14, \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uc0c1\uad00 \uc5c6\uc774 \ub3d9\uc77c\ud558\uac8c \uc601\ud5a5\uc744 \ubc1b\ub294\ub2e4.\\n\\n\ud55c \uc138\uc158\uc5d0\uc11c \uae00\ub85c\ubc8c \ub77d\uc744 \ud68d\ub4dd\ud558\uba74 \ud574\uc81c \ub420 \ub54c \uae4c\uc9c0 \uc870\ud68c\ub97c \uc81c\uc678\ud55c \ub300\ubd80\ubd84\uc758 \uba85\ub839\uc774 \ub300\uae30 \uc0c1\ud0dc\uac00 \ub41c\ub2e4. \\n\ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc874\uc7ac\ud558\ub294 MyISAM\uc774\ub098 MEMORY \ud14c\uc774\ube14\uc5d0 \ub300\ud574 \uc77c\uad00\ub41c \ubc31\uc5c5\uc744 \ubc1b\uc544\uc57c\ud560 \ub54c \uc0ac\uc6a9\ud55c\ub2e4. \\nInnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc5d0\uc11c\ub294 \ubc31\uc5c5 \uc2dc \uc870\uae08 \ub354 \uac00\ubcbc\uc6b4 \ubc31\uc5c5 \ub77d\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. \\n\\n```sql\\n-- GLOBAL LOCK\\nFLUSH TABLES WITH READ LOCK;\\n-- UNLOCK\\nUNLOCK TABLES;\\n\\n-- BACKUP LOCK\\nLOCK INSTANCE FOR BACKUP;\\n-- UNLOCK\\nUNLOCK INSTANCE;\\n```\\n\\n:::note MyISAM\\n\\nMySQL 5.5 \ubc84\uc804 \uc774\uc804\uc758 \uae30\ubcf8 \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc774\ub2e4. \\n\ud2b8\ub79c\uc7ad\uc158\uc744 \uc9c0\uc6d0\ud558\uc9c0 \uc54a\uace0, SELECT \uc791\uc5c5 \uc18d\ub3c4\uac00 \ube60\ub974\ub2e4.\\n\\n:::\\n\\n### \ud14c\uc774\ube14 \ub77d(Table lock)\\n\\n\uac1c\ubcc4 \ud14c\uc774\ube14 \ub2e8\uc704\ub85c \uc124\uc815\ub418\ub294 \uc7a0\uae08\uc774\ub2e4. \\n\uba85\uc2dc\uc801 \ub610\ub294 \ubb35\uc2dc\uc801\uc73c\ub85c \ud2b9\uc815 \ud14c\uc774\ube14\uc758 \ub77d\uc744 \ud68d\ub4dd\ud560 \uc218 \uc788\ub2e4. \\n\ubb35\uc2dc\uc801 \ub77d\uc740 MyISAM\uc774\ub098 MEMORY \ud14c\uc774\ube14\uc5d0 \ub370\uc774\ud130\ub97c \ubcc0\uacbd\ud558\ub294 \ucffc\ub9ac\ub97c \uc2e4\ud589\ud558\uba74 \ubc1c\uc0dd\ud55c\ub2e4. \\nInnoDB \ud14c\uc774\ube14\uc5d0\ub294 DML \ucffc\ub9ac\ub294 \ubb34\uc2dc\ub418\uace0 DDL \uc77c \uacbd\uc6b0\uc5d0\ub9cc \ubb35\uc2dc\uc801\uc73c\ub85c \ub77d\uc744 \ud68d\ub4dd\ud55c\ub2e4.\\n\\n```sql\\n-- TABLE LOCK\\nLOCK TABLES table_name [ READ | WRITE ]\\n\\n-- UNLOCK\\nUNLOCK TABLES;\\n```\\n\\n### \ub124\uc784\ub4dc \ub77d(Named lock)\\n\\n\uc784\uc758\uc758 \ubb38\uc790\uc5f4\uc5d0 \ub300\ud55c \uc7a0\uae08\uc744 \uc124\uc815\ud560 \uc218 \uc788\ub294 \uc7a0\uae08\uc73c\ub85c \uc720\uc800 \ub808\ubca8 \ub77d\uc73c\ub85c\ub3c4 \ubd88\ub9b0\ub2e4. \\n\uc5ec\ub7ec \uc2a4\ub808\ub4dc\ub098 \ud504\ub85c\uc138\uc2a4\uac00 \ub3d9\uc77c\ud55c \ub370\uc774\ud130\ub97c \uc218\uc815\ud558\ub824\ub294 \uacbd\uc6b0, \ub3d9\uc2dc\uc5d0 \uc218\uc815\ud558\uc9c0 \ubabb\ud558\ub3c4\ub85d \ubcf4\ud638\ud560 \uc218 \uc788\ub2e4. \\n\\n```sql\\n-- aGVyYg== \ub77c\ub294 \ubb38\uc790\uc5f4\uc5d0 \ub300\ud55c \uc7a0\uae08 \ud68d\ub4dd, \uc774\ubbf8 \uc7a0\uae08\uc744 \uc0ac\uc6a9\uc911\uc778 \uacbd\uc6b0 1\ucd08 \ub3d9\uc548\ub9cc \ub300\uae30\\nSELECT GET_LOCK(\'aGVyYg==\', 1);\\n\\n-- \ubb38\uc790\uc5f4\uc5d0 \ub300\ud55c \uc7a0\uae08\uc774 \uc124\uc815\ub418\uc5b4 \uc788\ub294\uc9c0 \ud655\uc778\ud55c\ub2e4.\\nSELECT IS_FREE_LOCK(\'aGVyYg==\');\\n\\n-- \ubb38\uc790\uc5f4\uc5d0 \ub300\ud55c \uc7a0\uae08\uc744 \ud574\uc81c\ud55c\ub2e4.\\nSELECT RELEASE_LOCK(\'aGVyYg==\');\\n\\n-- \uc704 3\uac1c \ud568\uc218 \ubaa8\ub450 \uc815\uc0c1\uc801\uc73c\ub85c \ub77d\uc744 \ud68d\ub4dd\ud558\uac70\ub098 \ud574\uc81c\ud55c \uacbd\uc6b0\uc5d0 1\uc744, \uc544\ub2c8\uba74 0\uc744 \ubc18\ud658\ud55c\ub2e4.\\n\\n-- \ubaa8\ub4e0 \ubb38\uc790\uc5f4\uc5d0 \ub300\ud55c \uc7a0\uae08\uc744 \ud574\uc81c\ud55c\ub2e4. \ud574\uc81c\ub41c \uc7a0\uae08\uc758 \uac1c\uc218\ub97c \ubc18\ud658\ud55c\ub2e4.\\nSELECT RELEASE_ALL_LOCKS();\\n```\\n\\n### \uba54\ud0c0\ub370\uc774\ud130 \ub77d(Metadata lock)\\n\\n\ub370\uc774\ud130\ubca0\uc774\uc2a4 \uac1d\uccb4\uc758 \uc774\ub984\uc774\ub098 \uad6c\uc870\ub97c \ubcc0\uacbd\ud558\ub294 \uacbd\uc6b0 \ud68d\ub4dd\ud558\ub294 \uc7a0\uae08\uc774\ub2e4. \\n\uba85\uc2dc\uc801\uc73c\ub85c \ud68d\ub4dd \ub610\ub294 \ud574\uc81c \ud560 \uc218 \uc5c6\uc9c0\ub9cc \ud14c\uc774\ube14\uc758 \uc774\ub984\uc744 \ubcc0\uacbd\ud558\ub294 \uacbd\uc6b0 \uc790\ub3d9\uc73c\ub85c \ud68d\ub4dd\ud55c\ub2e4. \\n\ubcf4\ud1b5 \ubc30\uce58 \ud504\ub85c\uadf8\ub7a8\uc5d0\uc11c \uc2e4\uc2dc\uac04\uc73c\ub85c \ud14c\uc774\ube14\uc744 \ubc14\uafd4\uc57c\ud558\ub294 \uacbd\uc6b0\uc5d0 \uc0ac\uc6a9\ub41c\ub2e4.\\n\\n```sql\\n-- \ubc30\uce58 \ud504\ub85c\uadf8\ub7a8\uc5d0\uc11c \ubcc4\ub3c4\uc758 \uc784\uc2dc \ud14c\uc774\ube14\uc5d0 \uc11c\ube44\uc2a4\uc6a9 \ub7ad\ud0b9 \ub370\uc774\ud130 \uc0dd\uc131 \ud6c4 \uae30\uc874 \ud14c\uc774\ube14\uc744 \ubc31\uc5c5\ud558\ub294 \uacbd\uc6b0\\n-- \uc544\ub798 \uad6c\ubb38 \uc2e4\ud589 \uc2dc \uba54\ud0c0\ub370\uc774\ud130 \ub77d\uc744 \uc790\ub3d9\uc73c\ub85c \ud68d\ub4dd\ud55c\ub2e4.\\nRENAME TABLE rank TO rank_backup, rank_new TO rank;\\n```\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\nReal My SQL 8.0 - 5\uc7a5 \ud2b8\ub79c\uc7ad\uc158\uacfc \uc7a0\uae08, \ubc31\uc740\ube48, \uc774\uc131\uc6b1 \\n[MySQL\uc758 User Level Lock\ub97c \ud65c\uc6a9\ud55c\ub2e4\uba74?, gywndi](https://gywn.net/2013/12/mysql-user-level-lock/) \\n[Locking Functions, MySQL 5.7 Reference](https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html#function_release-all-locks) \\n[Locking Functions, MySQL 8.0 Reference](https://dev.mysql.com/doc/refman/8.0/en/locking-functions.html#function_release-all-locks)"},{"id":"transaction-and-isolation","metadata":{"permalink":"/transaction-and-isolation","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-05-\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900.mdx","source":"@site/blog/2023-2/2023-04-05-\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900.mdx","title":"\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900","description":"\ud2b8\ub79c\uc7ad\uc158(Transaction)","date":"2023-04-05T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 5\uc77c","tags":[{"label":"DataBase","permalink":"/tags/data-base"},{"label":"Transaction","permalink":"/tags/transaction"},{"label":"Isolation","permalink":"/tags/isolation"}],"readingTime":9.68,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900","slug":"transaction-and-isolation","tags":["DataBase","Transaction","Isolation"]},"prevItem":{"title":"MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08","permalink":"/mysql-lock"},"nextItem":{"title":"\ud14c\uc2a4\ud2b8 \ub300\uc5ed","permalink":"/test-double"}},"content":"## \ud2b8\ub79c\uc7ad\uc158(Transaction)\\n\\n\ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \ub17c\ub9ac\uc801 \uae30\ub2a5\uc744 \uc218\ud589\ud558\uae30 \uc704\ud55c \uc791\uc5c5\uc758 \ub2e8\uc704\ub97c \ub9d0\ud55c\ub2e4. \\n\ud2b8\ub79c\uc7ad\uc158\uc740 \uc791\uc5c5\uc758 \uc644\uc804\uc131\uacfc \ub370\uc774\ud130\uc758 \uc815\ud569\uc131\uc744 \ubcf4\uc7a5\ud574 \uc900\ub2e4. \\n\ub17c\ub9ac\uc801\uc778 \uc791\uc5c5 \uc14b\uc744 \uc644\ubcbd\ud558\uac8c \ucc98\ub9ac\ud558\uac70\ub098, \uc624\ub958 \uc2dc \uc791\uc5c5\uc758 \uc77c\ubd80\ub9cc \uc801\uc6a9\ub418\ub294 \ud604\uc0c1\uc744 \ub9c9\uc544\uc900\ub2e4. \\n\\n### \ud2b8\ub79c\uc7ad\uc158\uc758 \uc18d\uc131(ACID)\\n\\n\uc6d0\uc790\uc131(Atomicity): \ud2b8\ub79c\uc7ad\uc158 \ub0b4\uc5d0\uc11c \uc2e4\ud589\ub41c \uc791\uc5c5\ub4e4\uc740 \ubaa8\ub450 \uc131\uacf5\ud558\uac70\ub098, \uc2e4\ud328\ud574\uc57c \ud55c\ub2e4. \\n\uc77c\uad00\uc131(Consistency): \ud2b8\ub79c\uc7ad\uc158\uc774 \uc218\ud589\ub418\uae30 \uc804\uacfc \ud6c4\uc5d0 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uac00 \uc77c\uad00\ub41c \uc0c1\ud0dc\ub97c \uc720\uc9c0\ud574\uc57c \ud55c\ub2e4. \\n\uaca9\ub9ac\uc131(Isolation): \uac01\uac01\uc758 \ud2b8\ub79c\uc7ad\uc158\uc740 \ub3c5\ub9bd\uc801\uc774\ub77c \uc11c\ub85c\uc5d0\uac8c \uc601\ud5a5\uc744 \uc8fc\uc9c0 \uc54a\uc544\uc57c \ud55c\ub2e4. \\n\uc9c0\uc18d\uc131(Durability): \ud2b8\ub79c\uc7ad\uc158\uc774 \uc131\uacf5\uc801\uc73c\ub85c \uc644\ub8cc\ub41c\ub2e4\uba74 \uc601\uad6c\uc801\uc73c\ub85c \uacb0\uacfc\uc5d0 \ubc18\uc601\ub418\uc5b4\uc57c \ud55c\ub2e4. \\n\\n### \ud2b8\ub79c\uc7ad\uc158 \uc8fc\uc758\uc0ac\ud56d\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc740 \uaf2d \ud544\uc694\ud55c \ucd5c\uc18c\uc758 \ucf54\ub4dc\uc5d0\ub9cc \uc801\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\ub2e4.(\ud2b8\ub79c\uc7ad\uc158\uc758 \ubc94\uc704\ub97c \ucd5c\uc18c\ud654\ud558\ub77c) \\n\uad6c\ud604\ud574\uc57c \ud558\ub294 \uc5c5\ubb34\uc5d0 \ub530\ub77c \ud2b8\ub79c\uc7ad\uc158\uc744 \ubb36\uac70\ub098 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \uc81c\uc678\ud558\uace0, \ub124\ud2b8\uc6cc\ud06c \uc791\uc5c5\uc774 \uc788\ub294 \uacbd\uc6b0 \ubc18\ub4dc\uc2dc \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ubc30\uc81c\ud574\uc57c \ud55c\ub2e4. \\n\\n:::info \uc65c \ub124\ud2b8\uc6cc\ud06c \uc791\uc5c5\uc774 \uc788\uc744 \ub54c \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ubc30\uc81c\ud574\uc57c \ud560\uae4c? \ud83e\udd14\\n\\n\ub370\uc774\ud130\uc758 \uc77c\uad00\uc131\uacfc \uc548\uc804\uc131\uc744 \ubcf4\uc7a5\ud558\uae30 \uc704\ud574 \ubc30\uc81c\ud574\uc57c \ud55c\ub2e4. \\n\ub124\ud2b8\uc6cc\ud06c \uc791\uc5c5\uc744 \ud2b8\ub79c\uc7ad\uc158 \ub0b4\ubd80\uc5d0 \ud3ec\ud568\ud55c\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud560 \uc218 \uc788\ub2e4. \\n- \ub124\ud2b8\uc6cc\ud06c \uc791\uc5c5\uc774 \uc911\uac04\uc5d0 \uc2e4\ud328\ud560 \uac00\ub2a5\uc131(\uc548\uc804\uc131 X)\\n- \ud1b5\uc2e0\uc73c\ub85c \uc778\ud574 \ub370\uc774\ud130\uac00 \ubcc0\uacbd\ub420 \uc218 \uc788\ub294 \ubd80\ubd84(\uc77c\uad00\uc131 X)\\n\\n:::\\n\\n## \uaca9\ub9ac \uc218\uc900(Isolation level)\\n\\n\uc5ec\ub7ec \ud2b8\ub79c\uc7ad\uc158\uc774 \ub3d9\uc2dc\uc5d0 \ucc98\ub9ac\ub420 \ub54c \ud2b9\uc815 \ud2b8\ub79c\uc7ad\uc158\uc774 \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ub370\uc774\ud130\uc758 \uc870\ud68c \ubc0f \ubcc0\uacbd\uc744 \ud5c8\uc6a9\ud560\uc9c0 \uacb0\uc815\ud558\ub294 \uac83\uc744 \ub9d0\ud55c\ub2e4. \\n\uaca9\ub9ac \uc218\uc900\uc774 \ub192\uc544\uc9c8 \uc218\ub85d \ub3d9\uc2dc \ucc98\ub9ac \uc131\ub2a5\uc774 \ub5a8\uc5b4\uc9c0\ub294 \uac83\uc774 \uc77c\ubc18\uc801\uc774\uc9c0\ub9cc, `SERIALIZABLE`\uc774 \uc544\ub2c8\ub77c\uba74 \ud06c\uac8c \uc131\ub2a5\uc758 \uc800\ud558\uac00 \ubc1c\uc0dd\ud558\uc9c0 \uc54a\ub294\ub2e4. \\n\\n### READ UNCOMMITTED\\n\\n\uac01 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c\uc758 \ubcc0\uacbd \ub0b4\uc6a9\uc774 `COMMIT`\uc774\ub098 `ROLLBACK` \uc5ec\ubd80\uc5d0 \uc0c1\uad00\uc5c6\uc774 \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ubcf4\uc778\ub2e4. \\n\ub354\ud2f0 \ub9ac\ub4dc \ud604\uc0c1\uc774 \ubc1c\uc0dd\ud558\uae30 \ub54c\ubb38\uc5d0 \uc815\ud569\uc131\uc758 \ubb38\uc81c\uac00 \ub9ce\uc740 \uaca9\ub9ac \uc218\uc900\uc774\ub2e4. \\nMySQL \uc0ac\uc6a9\uc2dc \ucd5c\uc18c `READ COMMITTED` \uc774\uc0c1\uc758 \uaca9\ub9ac \uc218\uc900 \uc0ac\uc6a9\uc744 \uad8c\uc7a5\ud55c\ub2e4. \\n\\n```mermaid\\n---\\ntitle: READ UNCOMMITTED\\n---\\nsequenceDiagram\\n Alice->>Database: BEGIN\\n Alice->>Database: INSERT(Alice)\\n Bob->>Database: SELECT\\n Database->>+Bob: Alice\\n Alice->>Database: COMMIT(Alice)\\n```\\n\\n### READ COMMITTED\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ub370\uc774\ud130\ub97c \ubcc0\uacbd\ud558\ub354\ub77c\ub3c4 `COMMIT`\uc774 \uc644\ub8cc\ub41c \ub370\uc774\ud130\ub9cc \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \uc870\ud68c\ud560 \uc218 \uc788\ub2e4. \\n\uc624\ub77c\ud074 DBMS\uc5d0\uc11c \uae30\ubcf8\uc73c\ub85c \uc0ac\uc6a9\ub418\ub294 \uaca9\ub9ac \uc218\uc900\uc774\uba70, \uc628\ub77c\uc778 \uc11c\ube44\uc2a4\uc5d0\uc11c \uac00\uc7a5 \ub9ce\uc774 \uc120\ud0dd\ub418\ub294 \uaca9\ub9ac \uc218\uc900\uc774\ub2e4. \\n`REPEATABLE READ`\uac00 \ubcf4\uc7a5\ub418\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0 `NON-REPEATABLE READ` \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud55c\ub2e4. \\n\\n```mermaid\\n---\\ntitle: READ COMMITTED\\n---\\nsequenceDiagram\\n Alice->>Database: BEGIN\\n Alice->>Database: UPDATE(Alice to Bob)\\n Bob->>Database: SELECT\\n Database->>+Bob: Alice(Undo log)\\n Alice->>Database: COMMIT\\n```\\n\\n### REPEATABLE READ\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc774 \uc2dc\uc791\ub418\uae30 \uc804\uc5d0 `COMMIT`\uc774 \uc644\ub8cc\ub41c \ub0b4\uc6a9\uc5d0 \ub300\ud574\uc11c\ub9cc \uc870\ud68c\ud560 \uc218 \uc788\ub2e4. \\nMySQL\uc758 InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc5d0\uc11c \uae30\ubcf8\uc73c\ub85c \uc0ac\uc6a9\ub418\ub294 \uaca9\ub9ac \uc218\uc900\uc774\ub2e4. \\nMVCC\ub97c \uc774\uc6a9\ud574 \uc5b8\ub450(Undo) \uc601\uc5ed\uc5d0 \ubc31\uc5c5\ub41c \uc774\uc804 \ub370\uc774\ud130\ub97c \uc774\uc6a9\ud574 \ub3d9\uc77c \ud2b8\ub79c\uc7ad\uc158 \ub0b4\uc5d0\uc11c\ub294 \ub3d9\uc77c\ud55c \uacb0\uacfc\ub97c \ubcf4\uc5ec\uc904 \uc218 \uc788\uac8c \ubcf4\uc7a5\ud55c\ub2e4. \\n\ub3d9\uc77c\ud55c \uacb0\uacfc\ub97c \ubcf4\uc7a5\ud558\ub294 \ubc29\ubc95\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n - \ubaa8\ub4e0 InnoDB \ud2b8\ub79c\uc7ad\uc158\uc740 \uc21c\ucc28\uc801\uc73c\ub85c \uc99d\uac00\ud558\ub294 \uace0\uc720\ud55c \ud2b8\ub79c\uc7ad\uc158 \ubc88\ud638\ub97c \uac00\uc9c4\ub2e4.\\n - Undo \uc601\uc5ed\uc5d0 \ubc31\uc5c5\ub41c \ub808\ucf54\ub4dc\uc5d0\ub294 \ubcc0\uacbd\uc744 \ubc1c\uc0dd\uc2dc\ud0a8 \ud2b8\ub79c\uc7ad\uc158\uc758 \ubc88\ud638\uac00 \ud3ec\ud568\ub418\uc5b4\uc788\ub2e4.\\n - Undo \uc601\uc5ed\uc758 \ubc31\uc5c5\ub41c \ub370\uc774\ud130\ub294 \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc774 \ubd88\ud544\uc694\ud558\ub2e4\uace0 \ud310\ub2e8\ud558\ub294 \uacbd\uc6b0 \uc0ad\uc81c\ub41c\ub2e4.\\n - `REPEATABLE READ` \uaca9\ub9ac \uc218\uc900\uc5d0\uc11c\ub294 MVCC\ub97c \ubcf4\uc7a5\ud558\uae30 \uc704\ud574 \uac00\uc7a5 \uc624\ub798\ub41c \ud2b8\ub79c\uc7ad\uc158 \ubc88\ud638\ubcf4\ub2e4 \uc55e\uc120 Undo \uc601\uc5ed\uc758 \ub370\uc774\ud130\ub294 \uc0ad\uc81c\ud558\uc9c0 \uc54a\ub294\ub2e4. \\n\\nInnoDB\uc5d0\uc11c\ub294 \uac2d \ub77d\uacfc \ub125\uc2a4\ud2b8 \ud0a4 \ub77d\uc744 \uc774\uc6a9\ud558\uc5ec \ud32c\ud140 \ub9ac\ub4dc \ud604\uc0c1\uc744 \ubc29\uc9c0\ud55c\ub2e4. \\n\\n```mermaid\\n---\\ntitle: REPEATABLE READ\\n---\\nsequenceDiagram\\n participant Alice\\n participant Database\\n participant Bob\\n Bob->>Database: BEGIN(TRX-ID: 1)\\n Bob->>Database: SELECT\\n Database->>+Bob: Alice\\n Alice->>Database: BEGIN(TRX-ID: 2)\\n Alice->>Database: UPDATE(Alice to Bob)\\n Alice->>Database: COMMIT\\n Bob->>Database: SELECT\\n Database->>+Bob: Alice(Undo log)\\n```\\n\\n:::note \uac2d \ub78d(Gap lock)\uacfc \ub125\uc2a4\ud2b8 \ud0a4 \ub77d(Next-key lock)\\n\\n\uac2d \ub77d: \ub808\ucf54\ub4dc\uc640 \ubc14\ub85c \uc778\uc811\ud55c \ub808\ucf54\ub4dc \uc0ac\uc774\uc758 \uac04\uaca9\ub9cc\uc744 \uc7a0\uadf8\ub294 \ub77d\uc774\ub2e4. \\n\ub125\uc2a4\ud2b8 \ud0a4 \ub77d: \ub808\ucf54\ub4dc \ub77d\uacfc \uac2d \ub77d\uc744 \ud569\uccd0\ub193\uc740 \ud615\ud0dc\uc758 \uc7a0\uae08\uc73c\ub85c \ub808\ucf54\ub4dc\uc640 \uadf8 \ub808\ucf54\ub4dc \uc55e\uc758 \uac2d \ub77d\uc744 \ud3ec\ud568\ud55c\ub2e4.\\n\\n:::\\n\\n:::note MVCC(Multi Version Concurrency Control)\\n\\n\ub3d9\uc2dc\uc131\uc744 \uc81c\uc5b4\ud558\ub294 \ubc29\ubc95 \uc911 \ud558\ub098\ub85c \ud558\ub098\uc758 \ub808\ucf54\ub4dc\uc5d0 \ub300\ud574 \uc5ec\ub7ec \uac1c\uc758 \ubc84\uc804\uc774 \ub3d9\uc2dc\uc5d0 \uad00\ub9ac\ub418\ub294 \uac83\uc774\ub2e4.\\n - PostgreSQL\uc740 \ub2e4\uc911 \ubc84\uc804\uc758 \ub370\uc774\ud130\ub97c \uc800\uc7a5\ud558\ub294 \uac83\uc73c\ub85c MVCC\ub97c \uad6c\ud604\ud55c\ub2e4.\\n - Oracle, InnoDB\ub294 `Undo log`\ub97c \uc774\uc6a9\ud574 \uc774 \uae30\ub2a5\uc744 \uad6c\ud604\ud55c\ub2e4.(\ucd5c\uc2e0 \ubc84\uc804\uc758 \ub370\uc774\ud130\ub9cc DB\uc5d0 \uc800\uc7a5)\\n\\n\uc7a0\uae08\uc744 \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 \uc77d\uad00\ub41c \uc77d\uae30\ub97c \uc81c\uacf5\ud558\ub294 \uac83\uc774 \ubaa9\uc801\uc774\ub2e4.\\n\\n:::\\n\\n### SERIALIZABLE\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc744 \uc21c\ucc28\uc801\uc73c\ub85c \uc9c4\ud589\uc2dc\ud0a4\ub294 \uaca9\ub9ac \uc218\uc900\uc774\uace0 \ub530\ub77c\uc11c \ub3d9\uc2dc \ucc98\ub9ac \uc131\ub2a5\ub3c4 \ub2e4\ub978 \uaca9\ub9ac \uc218\uc900\ubcf4\ub2e4 \ub5a8\uc5b4\uc9c4\ub2e4. \\n\ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \uc77d\uace0 \uc4f0\ub294 \ub808\ucf54\ub4dc\ub97c \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c\ub294 \uc811\uadfc\ud560 \uc218 \uc5c6\uace0 \ub2e8\uc21c\ud55c \uc77d\uae30 \uc791\uc5c5\ub3c4 \uacf5\uc720 \uc7a0\uae08(\uc77d\uae30 \uc7a0\uae08)\uc744 \ud68d\ub4dd\ud574\uc57c\ub9cc \ud55c\ub2e4. \\nInnoDB\uc5d0\uc11c\ub294 \ud32c\ud140 \ub9ac\ub4dc \ud604\uc0c1\uc774 `REPEATABLE READ` \uaca9\ub9ac \uc218\uc900\uc5d0\uc11c \ubc1c\uc0dd\ud558\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0 \uad73\uc774 \uc0ac\uc6a9\ud560 \ud544\uc694\ub294 \uc5c6\ub2e4. \\n\\n## \uaca9\ub9ac \uc218\uc900\uc5d0 \ub530\ub978 \ubd80\uc815\ud569 \ubb38\uc81c\\n\\n\uaca9\ub9ac \uc218\uc900\uc5d0 \ub530\ub77c \ub354\ud2f0 \ub9ac\ub4dc, \ubc18\ubcf5 \uac00\ub2a5\ud558\uc9c0 \uc54a\uc740 \uc870\ud68c, \ud32c\ud140 \ub9ac\ub4dc \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud55c\ub2e4. \\n\\n| \uaca9\ub9ac \uc218\uc900 / \ubd80\uc815\ud569 \ubb38\uc81c | \ub354\ud2f0 \ub9ac\ub4dc | \ubc18\ubcf5 \uac00\ub2a5\ud558\uc9c0 \uc54a\uc740 \uc870\ud68c | \ud32c\ud140 \ub9ac\ub4dc |\\n| --- | --- | --- | --- |\\n| READ UNCOMMITTED | O | O | O |\\n| READ COMMITTED | X | O | O |\\n| REPEATABLE READ | X | X | O(InnoDB\ub294 X) |\\n| SERIALIZABLE | X | X | X |\\n\\n### \ub354\ud2f0 \ub9ac\ub4dc(Dirty read)\\n\\n\uc5b4\ub5a4 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ucc98\ub9ac\ud55c \uc791\uc5c5\uc774 \uc644\ub8cc\ub418\uc9c0 \uc54a\uc558\uc5b4\ub3c4 \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ubcfc \uc218 \uc788\ub294 \ud604\uc0c1 \\n\ud2b8\ub79c\uc7ad\uc158 \uaca9\ub9ac \uc218\uc900\uc774 READ UNCOMMITTED\uc77c \ub54c \ubc1c\uc0dd\ud55c\ub2e4. \\n\uc608) B\uac00 \ub808\ucf54\ub4dc\ub97c \ucd94\uac00\ud558\uace0 \ucee4\ubc0b\uc744 \ud558\uc9c0 \uc54a\uc558\uc9c0\ub9cc, A\uac00 \ud574\ub2f9 \ub808\ucf54\ub4dc\ub97c \uc870\ud68c\ud560 \uc218 \uc788\ub294 \uacbd\uc6b0\\n\\n### \ubc18\ubcf5 \uac00\ub2a5\ud558\uc9c0 \uc54a\uc740 \uc870\ud68c(Non-repeatable read)\\n\\n\ud55c \ud2b8\ub79c\uc7ad\uc158 \ub0b4\uc758 \uac19\uc740 \ud589\uc5d0 \ub450 \ubc88 \uc774\uc0c1 \uc870\ud68c\uac00 \ubc1c\uc0dd\ud588\ub294\ub370, \uadf8 \uac12\uc774 \ub2e4\ub978 \ud604\uc0c1 \\n\uc608) A\uac00 \ub808\ucf54\ub4dc\ub97c \uc5ec\ub7ec \ubc88 \uc870\ud68c\ud558\ub358 \uc911 B\uac00 \ub808\ucf54\ub4dc\ub97c \ubcc0\uacbd\ud558\uc5ec A\uac00 \uc870\ud68c\ud55c \uac12\uc774 \ub2ec\ub77c\uc9c0\ub294 \uacbd\uc6b0 \\n\\n```mermaid\\n---\\ntitle: NON REPEATABLE READ\\n---\\nsequenceDiagram\\n participant Alice\\n participant Database\\n participant Bob\\n Bob->>Database: BEGIN\\n Bob->>Database: SELECT\\n Database->>+Bob: Alice\\n Alice->>Database: BEGIN\\n Alice->>Database: UPDATE(Alice to Bob)\\n Alice->>Database: COMMIT\\n Bob->>Database: SELECT\\n Database->>+Bob: Bob\\n```\\n\\n### \ud32c\ud140 \ub9ac\ub4dc(Phantom read, Phantom row)\\n\\n\ud55c \ud2b8\ub79c\uc7ad\uc158 \ub0b4\uc5d0\uc11c \ub3d9\uc77c\ud55c \ucffc\ub9ac \uc218\ud589\uc2dc, \uc218\ud589 \uacb0\uacfc\uac00 \ub2e4\ub978 \ud604\uc0c1 \\n\uc608) A\uac00 \ub808\ucf54\ub4dc\ub97c \uc870\ud68c\ud558\uace0 B\uac00 \ub808\ucf54\ub4dc\ub97c \ucd94\uac00\ud558\uc5ec A\uac00 \ub2e4\uc2dc \uc870\ud68c\ud560 \ub54c \uc874\uc7ac\ud558\uc9c0 \uc54a\uc740 \ub808\ucf54\ub4dc\uac00 \uc870\ud68c\ub418\ub294 \uacbd\uc6b0 \\n\\n```mermaid\\n---\\ntitle: PHANTOM READ\\n---\\nsequenceDiagram\\n participant Alice\\n participant Database\\n participant Bob\\n Bob->>Database: BEGIN(TRX-ID: 1)\\n Bob->>Database: SELECT COUNT\\n Database->>+Bob: 1\\n Alice->>Database: BEGIN(TRX-ID: 2)\\n Alice->>Database: INSERT(Bob)\\n Alice->>Database: COMMIT\\n Bob->>Database: SELECT COUNT\\n Database->>+Bob: 2\\n```\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\nReal My SQL 8.0 - 5\uc7a5 \ud2b8\ub79c\uc7ad\uc158\uacfc \uc7a0\uae08, \ubc31\uc740\ube48, \uc774\uc131\uc6b1 \\n[Isolation Level, MySQL](https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html)"},{"id":"test-double","metadata":{"permalink":"/test-double","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-04-\ud14c\uc2a4\ud2b8 \ub300\uc5ed.mdx","source":"@site/blog/2023-2/2023-04-04-\ud14c\uc2a4\ud2b8 \ub300\uc5ed.mdx","title":"\ud14c\uc2a4\ud2b8 \ub300\uc5ed","description":"\ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc774\ub780?","date":"2023-04-04T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 4\uc77c","tags":[{"label":"Test","permalink":"/tags/test"},{"label":"Mock","permalink":"/tags/mock"}],"readingTime":4.52,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ud14c\uc2a4\ud2b8 \ub300\uc5ed","slug":"test-double","tags":["Test","Mock"]},"prevItem":{"title":"\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900","permalink":"/transaction-and-isolation"},"nextItem":{"title":"\uc790\ubc14 \ud074\ub798\uc2a4 \ud30c\uc77c \uad6c\uc870","permalink":"/java-class-file"}},"content":"### \ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc774\ub780?\\n\\n\ubaa8\ub4e0 \uc720\ud615\uc758 \ud14c\uc2a4\ud2b8\ub97c \uc704\ud55c \uac00\uc9dc \uc758\uc874\uc131\uc744 \uc758\ubbf8\ud558\uace0, \ud14c\uc2a4\ud2b8\uac00 \uc2e4\ud589\ub420 \ub54c \ub2e4\ub978 \uac1d\uccb4\ub97c \ub300\uc2e0\ud55c\ub2e4. \\nGerard Meszaros\uc758 xUnit Test Patterns\ub77c\ub294 \ucc45\uc5d0\uc11c\ub294 \ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc744 \ub2e4\uc12f \uac00\uc9c0(\ub354\ubbf8, \uc2a4\ud141, \uc2a4\ud30c\uc774, \ubaa9, \ud398\uc774\ud06c)\ub85c \uad6c\ubd84\ud55c\ub2e4.\\n\\n\ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc758 \uae30\ubcf8 \uba54\ucee4\ub2c8\uc998\uc740 \ub2e4\ud615\uc131\uc744 \uc774\uc6a9\ud558\ub294 \ubc29\ubc95\uc774\ub2e4. \\n\uc678\ubd80 \uc11c\ube44\uc2a4\ub97c \uc0ac\uc6a9\ud558\ub294 \ucf54\ub4dc\ub97c \ud14c\uc2a4\ud2b8 \ud558\ub294 \uacbd\uc6b0, \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc815\uc758\ud558\uace0 \uc678\ubd80 \uc11c\ube44\uc2a4 \ub300\uc2e0 \ud14c\uc2a4\ud2b8 \uc6a9\ub3c4\uc758 \uad6c\ud604\uccb4\ub97c \uc0dd\uc131\ud558\ub294 \uac83\uc774\ub2e4.\\n\\n**\ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc758 \ud0c0\uc785 \uacc4\uce35 \uad6c\uc870**\\n\\n```mermaid\\nflowchart LR\\n Mock --\x3e Spy --\x3e Stub --\x3e Dummy --\x3e TestDouble\\n Fake --\x3e TestDouble\\n```\\n\\n### \ub354\ubbf8(Dummy)\\n\\n\uac00\uc7a5 \ub2e8\uc21c\ud558\uace0, \uc6d0\uc2dc\uc801\uc778 \uc720\ud615\uc758 \ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc774\ub2e4. \\n\uae30\ubcf8\uc801\uc73c\ub85c \uc544\ubb34 \uc77c\ub3c4 \ud558\uc9c0 \uc54a\ub294 \uad6c\ud604\uccb4\ub85c \uc778\uc2a4\ud134\uc2a4\ud654\uac00 \ud544\uc694\ud55c \uacbd\uc6b0 \uc0ac\uc6a9\ud55c\ub2e4. \\n\ub9cc\uc57d \uba54\uc11c\ub4dc\uac00 \ubb34\uc5b8\uac00 \ubc18\ud658\uc744 \ud574\uc57c\ud558\ub294 \uacbd\uc6b0 0, null\uacfc \uac19\uc740 \uac12\uc744 \ubc18\ud658\ud55c\ub2e4. \\n\\n### \uc2a4\ud141(Stub)\\n\\n\uc2dc\ub098\ub9ac\uc624\ub9c8\ub2e4 \ub2e4\ub978 \uac12(\ubbf8\ub9ac \uc900\ube44 \ub41c \uacb0\uacfc)\uc744 \ubc18\ud658\ud55c\ub2e4. \\n\uc774\ub97c \ud1b5\ud574 \ud2b9\uc815 \uc870\uac74\uc5d0\uc11c \uba54\uc11c\ub4dc\uac00 \uc608\uc0c1\ud55c\ub300\ub85c \ub3d9\uc791\ud558\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n### \uc2a4\ud30c\uc774(Spy)\\n\\n\uc2a4\ud141\uacfc \uc720\uc0ac\ud558\uc9c0\ub9cc \ud638\ucd9c \uc5ec\ubd80\ub97c \uae30\ub85d\ud558\uac70\ub098 \ud638\ucd9c\ud560 \ub54c \uc804\ub2ec\ud55c \uc778\uc790\uac12\uc744 \uae30\ub85d\ud560 \uc218 \uc788\ub2e4. \\n\uc608) \uba54\uc77c \uc804\uc1a1 \uae30\ub2a5\uc744 \uac00\uc9c4 \uac1d\uccb4\ub97c \ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc73c\ub85c \uad6c\ud604\ud588\uc744 \ub54c \uba54\uc77c \uc804\uc1a1 \ud69f\uc218\ub97c \uae30\ub85d\ud55c\ub2e4. \\n\\n### \ubaa9, \ubaa8\uc758 \uac1d\uccb4(Mock)\\n\\n\ubaa9\uc740 \ub354\ubbf8, \uc2a4\ud141, \uc2a4\ud30c\uc774\ub97c \ud3ec\ud568\ud55c\ub2e4. \\n\ud638\ucd9c \uc2dc \uc0ac\uc804\uc5d0 \uc815\uc758\ub41c \uacb0\uacfc\ub97c \ubc18\ud658\ud558\uace0, \uc608\uc0c1\uce58 \ubabb\ud55c \ud638\ucd9c\uc774 \uc788\uc744 \uacbd\uc6b0 \uc608\uc678\ub97c \ub358\uc9c8 \uc218 \uc788\ub2e4. \\n\ub610\ud55c \ud638\ucd9c\uc5d0 \ub300\ud55c \uac80\uc99d\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\\n### \uac00\uc9dc(Fake)\\n\\nDOC\uc640 \ub3d9\uc77c\ud55c \uae30\ub2a5\uc744 \uc81c\uacf5\ud558\uc9c0\ub9cc, \ub354\uc6b1 \uac04\ub2e8\ud55c \ubc29\ubc95\uc73c\ub85c \uad6c\ud604\ub41c \uac83\uc774\ub2e4. \\n\uc608) \uc2e4\uc81c \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc640 \uc720\uc0ac\ud558\uac8c \ub3d9\uc791\ud558\ub294 \uac00\uc9dc \uac1d\uccb4\ub97c \ub9cc\ub4e4\uc5b4 \ud14c\uc2a4\ud2b8\ud560 \uc218 \uc788\ub2e4. \\n\\n:::note DOC(depended-on component)\\n\\n\uc758\uc874 \uad6c\uc131 \uc694\uc18c, DOC\ub97c \ud14c\uc2a4\ud2b8 \ub354\ube14\ub85c \ub300\uccb4\ud560 \uc218 \uc788\ub2e4. \\n\ud14c\uc2a4\ud2b8 \ub354\ube14\uc740 DOC\uc640 \ub3d9\uc77c\ud55c API\ub97c \uc81c\uacf5\ud574\uc57c \ud55c\ub2e4. \\n\\n:::\\n\\n### \uc0c1\ud638\uc791\uc6a9\uc5d0 \ub530\ub978 \ubaa9\uacfc \uc2a4\ud141 \uad6c\ubd84\\n\\n\ub2e8\uc704 \ud14c\uc2a4\ud2b8 p.149 \uc5d0\uc11c\ub294 \ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc744 \ud06c\uac8c \ubaa9\uacfc \uc2a4\ud141\uc73c\ub85c \uad6c\ubd84\ud55c\ub2e4. \\n\ubaa9\uc740 SUT\uc640 \uad00\ub828\ub41c \uc0c1\ud638\uc791\uc6a9\uc744 \ubaa8\ubc29\ud558\uace0 \uac80\uc0ac\ud558\ub294 \ubc18\uba74, \uc2a4\ud141\uc740 \ub2e8\uc21c \ubaa8\ubc29\ub9cc \ud55c\ub2e4. \\n\\n| TestDouble | Mock | Stub |\\n| --- | --- | --- |\\n| \ud3ec\ud568 \uc720\ud615 | \ubaa9, \uc2a4\ud30c\uc774 | \uc2a4\ud141, \ub354\ubbf8, \ud398\uc774\ud06c |\\n| \uc6a9\ub3c4 | \uc678\ubd80\ub85c \ub098\uac00\ub294 \uc0c1\ud638\uc791\uc6a9\uc744 \ubaa8\ubc29\ud558\uace0 \uac80\uc0ac\ud558\ub294 \ub370 \uc0ac\uc6a9 | \ub0b4\ubd80\ub85c \ub4e4\uc5b4\uc624\ub294 \uc0c1\ud638\uc791\uc6a9\uc744 \ubaa8\ubc29\ud558\ub294 \ub370 \uc0ac\uc6a9 |\\n| \uc124\uba85 | SUT\uac00 \uc0c1\ud0dc\ub97c \ubcc0\uacbd\ud558\uae30 \uc704\ud55c \uc758\uc874\uc131\uc744 \ud638\ucd9c\ud558\ub294 \uac83\uc5d0 \ud574\ub2f9 | SUT\uac00 \uc785\ub825 \ub370\uc774\ud130\ub97c \uc5bb\uae30 \uc704\ud55c \uc758\uc874\uc131\uc744 \ud638\ucd9c\ud558\ub294 \uac83\uc5d0 \ud574\ub2f9\\n| \uc608\uc2dc | \uc774\uba54\uc77c \ubc1c\uc1a1 | \ub370\uc774\ud130 \uac80\uc0c9 |\\n\\n:::note SUT(system under test)\\n\\n\ud14c\uc2a4\ud2b8 \ub300\uc0c1 \uc2dc\uc2a4\ud15c \\n\ud14c\uc2a4\ud2b8\ub97c \ud558\ub824\ub294 \ub300\uc0c1\\n\\n:::\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc7a5\uc778 \uc815\uc2e0 \uc774\uc57c\uae30 - 3\uc7a5 \uace0\uae09 \ud14c\uc2a4\ud2b8 \uc8fc\ub3c4 \uac1c\ubc1c, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4 \\n\ub2e8\uc704 \ud14c\uc2a4\ud2b8 - 5\uc7a5 \ubaa9\uacfc \ud14c\uc2a4\ud2b8 \ucde8\uc57d\uc131, \ube14\ub77c\ub514\ubbf8\ub974 \ucf54\ub9ac\ucf54\ud504 \\n\ud14c\uc2a4\ud2b8 \uc8fc\ub3c4 \uac1c\ubc1c \uc2dc\uc791\ud558\uae30 - 7\uc7a5 \ub300\uc5ed, \ucd5c\ubc94\uade0 \\n[\ud14c\uc2a4\ud2b8 \ub354\ube14, Martin Fowler](https://www.martinfowler.com/bliki/TestDouble.html) \\n[\ud14c\uc2a4\ud2b8 \uad00\ub828 \uc6a9\uc5b4 \uc815\ub9ac, Johngrib](https://johngrib.github.io/wiki/test-terms/) \\n[Test Double, Gerard Meszaros](http://xunitpatterns.com/Test%20Double.html)"},{"id":"java-class-file","metadata":{"permalink":"/java-class-file","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-03-\uc790\ubc14 \ud074\ub798\uc2a4\ud30c\uc77c \uad6c\uc870.mdx","source":"@site/blog/2023-2/2023-04-03-\uc790\ubc14 \ud074\ub798\uc2a4\ud30c\uc77c \uad6c\uc870.mdx","title":"\uc790\ubc14 \ud074\ub798\uc2a4 \ud30c\uc77c \uad6c\uc870","description":"\ud074\ub798\uc2a4 \ud30c\uc77c","date":"2023-04-03T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 3\uc77c","tags":[{"label":"Java","permalink":"/tags/java"},{"label":"Class","permalink":"/tags/class"}],"readingTime":5.63,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc790\ubc14 \ud074\ub798\uc2a4 \ud30c\uc77c \uad6c\uc870","slug":"java-class-file","tags":["Java","Class"]},"prevItem":{"title":"\ud14c\uc2a4\ud2b8 \ub300\uc5ed","permalink":"/test-double"},"nextItem":{"title":"\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30","permalink":"/custom-jdbc-template"}},"content":"### \ud074\ub798\uc2a4 \ud30c\uc77c\\n\\n\uc790\ubc14 \uc18c\uc2a4\ucf54\ub4dc\uac00 \uc2e4\ud589\uc774 \ub418\ub824\uba74 \uc790\ubc14 \ucef4\ud30c\uc77c\ub7ec(javac)\ub97c \ud1b5\ud574 \uc18c\uc2a4\ucf54\ub4dc\ub97c \ud074\ub798\uc2a4\ud30c\uc77c\ub85c \ubcc0\ud658\ud574\uc57c \ud55c\ub2e4. \\n\ucef4\ud30c\uc77c\ub41c \ud074\ub798\uc2a4\ud30c\uc77c\uc740 \uc5b4\ub5a4 \uad6c\uc870\ub85c \ub418\uc5b4\uc788\uc744\uae4c?\\n\\n### \ud074\ub798\uc2a4 \ud30c\uc77c\uc758 \ub370\uc774\ud130 \ud615\uc2dd\\n\\n8\ube44\ud2b8 \ubc14\uc774\ud2b8\uc758 \uc2a4\ud2b8\ub9bc\uc73c\ub85c \uad6c\uc131\ub41c\ub2e4. \\n16\ube44\ud2b8 \ubc0f 32\ube44\ud2b8\uc758 \ub370\uc774\ud130\ub294 \uac01\uac01 2\uac1c, 4\uac1c\uc758 \uc5f0\uc18d\ub41c 8\ube44\ud2b8\ub97c \uc77d\uc5b4\uc11c \uad6c\uc131\ub41c\ub2e4. \\n\uba40\ud2f0\ubc14\uc774\ud2b8\uc758 \uacbd\uc6b0 \ud56d\uc0c1 big endian \uc21c\uc11c\ub85c \uc800\uc7a5\ub41c\ub2e4. \\n\\nu1 \u2192 unsigned 1byte \\nu2 \u2192 unsigned 2byte \\nu4 \u2192 unsigned 4byte \\n\\n### \ud074\ub798\uc2a4 \ud30c\uc77c \uad6c\uc870\\n\\n```\\nClassFile {\\n u4 magic;\\n u2 minor_version;\\n u2 major_version;\\n u2 constant_pool_count;\\n cp_info constant_pool[constant_pool_count-1];\\n u2 access_flags;\\n u2 this_class;\\n u2 super_class;\\n u2 interfaces_count;\\n u2 interfaces[interfaces_count];\\n u2 fields_count;\\n field_info fields[fields_count];\\n u2 methods_count;\\n method_info methods[methods_count];\\n u2 attributes_count;\\n attribute_info attributes[attributes_count];\\n}\\n```\\n\\n### \ub9e4\uc9c1\ub118\ubc84\\n\\n\ubaa8\ub4e0 \ud074\ub798\uc2a4 \ud30c\uc77c\uc740 0xCAFEBABE\ub77c\ub294 \ub9e4\uc9c1\ub118\ubc84\ub85c \uc2dc\uc791\ud55c\ub2e4. \\n\ubcf4\ud1b5 \ub9e4\uc9c1\ub118\ubc84\ub294 \ud30c\uc77c \uc885\ub958\ub97c \uc2dd\ubcc4\ud558\ub294 \uc6a9\ub3c4\ub85c \uc0ac\uc6a9\ub41c\ub2e4. \\n\\n### \ud074\ub798\uc2a4 \ud30c\uc77c \ud3ec\ub9f7 \ubc84\uc804\\n\\n\ud074\ub798\uc2a4 \ud30c\uc77c \ubc84\uc804 \uac12\uc740 \ud074\ub798\uc2a4\ub85c\ub354\uc758 \ud638\ud658\uc131 \ubcf4\uc7a5\uc744 \uc704\ud574 \uaf2d \ud544\uc694\ud55c \uac12\uc774\ub2e4. \\n- Java 17 \ubc84\uc804\uc73c\ub85c \ube4c\ub4dc\ud55c\ub2e4\uba74 class version 61 ex) 00 00 00 3D\\n\\n\ud638\ud658\ub418\uc9c0 \uc54a\ub294 \ubc84\uc804\uc758 \ud074\ub798\uc2a4 \ud30c\uc77c\uc744 \ub85c\ub529\ud558\ub824\uace0 \ud558\ub294 \uacbd\uc6b0 \ub7f0\ud0c0\uc784\uc5d0 `UnsupportedClassVersionError` \uc608\uc678\uac00 \ubc1c\uc0dd\ud55c\ub2e4. \\n\\n**class\xa0file format major versions**\\n\\n| Java SE | Released | Major | Supported majors |\\n| --- | --- | --- | --- |\\n| 8 | March 2014 | 52 | 45 .. 52 |\\n| 9 | September 2017 | 53 | 45 .. 53 |\\n| 10 | March 2018 | 54 | 45 .. 54 |\\n| 11 | September 2018 | 55 | 45 .. 55 |\\n| 12 | March 2019 | 56 | 45 .. 56 |\\n| 13 | September 2019 | 57 | 45 .. 57 |\\n| 14 | March 2020 | 58 | 45 .. 58 |\\n| 15 | September 2020 | 59 | 45 .. 59 |\\n| 16 | March 2021 | 60 | 45 .. 60 |\\n| 17 | September 2021 | 61 | 45 .. 61 |\\n\\n### \uc0c1\uc218 \ud480\\n\\n2\ubc14\uc774\ud2b8\uc758 \uc0c1\uc218\uc758 \uac1c\uc218\uac12\uc774 \uba3c\uc800\uc624\uace0 \uadf8 \ub4a4\ub85c \ucf54\ub4dc\uc5d0 \ub4f1\uc7a5\ud558\ub294 \uc0c1\uc218\uac12\uc774 \ubaa8\uc5ec\uc788\ub2e4. \\n\ud074\ub798\uc2a4\uba85, \uc0c1\uc218\uba85, \uc0c1\uc218 \uac12, \ud544\ub4dc\uba85, \uba54\uc11c\ub4dc\uba85\uacfc \uac19\uc740 \uac12\ub4e4\uc774 \uc874\uc7ac\ud55c\ub2e4. \\nJVM\uc740 \ucf54\ub4dc \uc2e4\ud589 \uc2dc \ub7f0\ud0c0\uc784\uc5d0 \ubc30\uce58\ub41c \uba54\ubaa8\ub9ac\uac00 \uc544\ub2c8\ub77c, \ud574\ub2f9 \uc0c1\uc218 \ud480 \ud14c\uc774\ube14\uc744 \ucc3e\uc544\ubcf4\uace0 \ud544\uc694\ud55c \uac12\uc744 \ucc38\uc870\ud55c\ub2e4.\\n\\n### \uc561\uc138\uc2a4 \ud50c\ub798\uadf8\\n\\n\ud074\ub798\uc2a4, \uc778\ud130\ud398\uc774\uc2a4\uc640 \uac19\uc740 \ud30c\uc77c\uc758 \uc18d\uc131\uc744 \ud45c\uc2dc\ud55c\ub2e4. \\n\uc608\ub97c \ub4e4\uc5b4 public interface\ub85c \uc815\uc758\ub41c \uc778\ud130\ud398\uc774\uc2a4\uc758 \ud50c\ub798\uadf8\ub294 0x0601\uc774\ub2e4. \\n- \uacc4\uc0b0\uc740 \ub2e4\uc74c\uacfc \uac19\uc774 \uc774\ub8e8\uc5b4\uc9c4\ub2e4. `ACC_PUBLIC` xor `ACC_INTERFACE` xor `ACC_ABSTRACT`\\n\\n\uacf5\uc2dd\ubb38\uc11c\uc5d0 \ub4e4\uc5b4\uac00\uba74 \uac01 \ud50c\ub798\uadf8\uc5d0 \ub300\ud55c \uc124\uba85 + \ud50c\ub798\uadf8 \uc124\uc815\uc2dc \ub3d9\uc2dc\uc5d0 \uc124\uc815\ub418\uba74 \uc548\ub418\ub294 \ud50c\ub798\uadf8\uc640 \uac19\uc740 \uc124\uba85\uc774 \uc790\uc138\ud558\uac8c \ub098\uc640\uc788\ub2e4.\\n\\n**Class access and property modifiers**\\n\\n| Flag Name | Value | Interpretation |\\n| --- | --- | --- |\\n| ACC_PUBLIC | 0x0001 | Declared\xa0public; may be accessed from outside its package. |\\n| ACC_FINAL | 0x0010 | Declared\xa0final; no subclasses allowed. |\\n| ACC_SUPER | 0x0020 | Treat superclass methods specially when invoked by the\xa0invokespecial\xa0instruction. |\\n| ACC_INTERFACE | 0x0200 | Is an interface, not a class. |\\n| ACC_ABSTRACT | 0x0400 | Declared\xa0abstract; must not be instantiated. |\\n| ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |\\n| ACC_ANNOTATION | 0x2000 | Declared as an annotation type. |\\n| ACC_ENUM | 0x4000 | Declared as an\xa0enum\xa0type. |\\n| ACC_MODULE | 0x8000 | Is a module, not a class or interface. |\\n\\n### this_class\\n\\n\ud074\ub798\uc2a4\uba85\uacfc \uac19\uc740 \uc774\ub984\uc744 \ud45c\ud604\ud558\ub294 \uac12\uc73c\ub85c, \uc0c1\uc218 \ud480\uc5d0\uc11c \ud074\ub798\uc2a4\uba85\uacfc \uc77c\uce58\ud558\ub294 \ud56d\ubaa9\uc758 \uc778\ub371\uc2a4\ub97c \ucc38\uc870\ud55c\ub2e4. \\n\ud574\ub2f9 \uc778\ub371\uc2a4\uc758 \ud56d\ubaa9\uc740 `CONSTANT_Class_infoclass` \ud615\uc2dd\uc758 \uac12\uc774\uc5b4\uc57c \ud55c\ub2e4. \\n\\n### super_class\\n\\n\uc0c1\uc218 \ud480\uc5d0\uc11c \uc288\ud37c \ud074\ub798\uc2a4\uc758 \uc774\ub984\uacfc \uc77c\uce58\ud558\ub294 \ud56d\ubaa9\uc758 \uc778\ub371\uc2a4\ub97c \ucc38\uc870\ud55c\ub2e4. \\n\uc544\ubb34\uac83\ub3c4 \uc0c1\uc18d\ud558\uc9c0 \uc54a\ub294 \ud074\ub798\uc2a4\uc758 \uacbd\uc6b0 `java.lang.Object`\uc758 \uc778\ub371\uc2a4 \uac12\uc774 \ub4e4\uc5b4\uc788\ub2e4.\\n\\n### interface, field, method\\n\\n\uac01\uac01\uc758 \uac1c\uc218\uc640, \uc815\ubcf4\uc5d0 \ub300\ud55c \uac12\uc774 \ub4e4\uc5b4\uc788\ub2e4. \\ninterface, field, method\ub97c \ud45c\uc2dc\ud558\ub294 \ubc29\ubc95\uc774 \uac01\uac01 \ub2e4\ub974\uace0, \uc811\uadfc\uc790\uc5d0 \ub300\ud55c \ud50c\ub798\uadf8\ub3c4 \uac01\uac01 \ub2e4\ub974\ub2e4.\\n\\n### attributes\\n\\n\ud574\ub2f9 \ud074\ub798\uc2a4 \ud30c\uc77c\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \ucd94\uac00 \uc815\ubcf4\uc758 \ubaa8\uc74c\uc774\ub2e4. \uc608) \uc18c\uc2a4\ud30c\uc77c\uba85 \\n\uc815\ud574\uc9c4 \ud074\ub798\uc2a4 \ud30c\uc77c\uc758 \uad6c\uc870\ub97c \ud655\uc7a5\ud558\ub294 \uc5ed\ud560\uc744 \ud55c\ub2e4. \\n\\n### \ud074\ub798\uc2a4 \ud30c\uc77c \ud655\uc778\ud558\uba74\uc11c \uc0ac\uc6a9\ud55c \ud234\\n\\nIntelliJ plugin - BinEd \\nIntelliJ plugin - jclasslib Bytecode Viewer\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n2\uc7a5 JVM \uc774\uc57c\uae30, \uc790\ubc14 \ucd5c\uc801\ud654 \\n[Class file in Java, File Format](https://docs.fileformat.com/ko/programming/class/) \\n[java se11 Class \ud30c\uc77c \ud615\uc2dd, Oracle](https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html) \\n[java se17 Class \ud30c\uc77c \ud615\uc2dd, Oracle](https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html)"},{"id":"custom-jdbc-template","metadata":{"permalink":"/custom-jdbc-template","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-02-\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30.mdx","source":"@site/blog/2023-2/2023-04-02-\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30.mdx","title":"\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30","description":"\uccb4\uc2a4 \ubbf8\uc158\uc5d0\uc11c\ub294 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \uac12\uc744 \uac00\uc838\uc624\uae30 \uc704\ud574 DAO\ub97c \uc0ac\uc6a9\ud588\ub2e4.","date":"2023-04-02T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 2\uc77c","tags":[{"label":"JDBC","permalink":"/tags/jdbc"},{"label":"Java","permalink":"/tags/java"}],"readingTime":9.025,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30","slug":"custom-jdbc-template","tags":["JDBC","Java"]},"prevItem":{"title":"\uc790\ubc14 \ud074\ub798\uc2a4 \ud30c\uc77c \uad6c\uc870","permalink":"/java-class-file"},"nextItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 1 \ud68c\uace0","permalink":"/woowacourse-level1-retrospective"}},"content":"import Tabs from \\"@theme/Tabs\\";\\nimport TabItem from \\"@theme/TabItem\\";\\n\\n\uccb4\uc2a4 \ubbf8\uc158\uc5d0\uc11c\ub294 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \uac12\uc744 \uac00\uc838\uc624\uae30 \uc704\ud574 DAO\ub97c \uc0ac\uc6a9\ud588\ub2e4. \\n\uc774 \ub54c JDBC\ub97c \uc0ac\uc6a9\ud560 \ub54c \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc758 \ucee4\ub125\uc158\uc744 \uc5bb\uace0, try-with-resource\ub97c \uc0ac\uc6a9\ud558\ub294 \ubd80\ubd84\uc774 \ubc18\ubcf5\ub418\uc5c8\ub2e4. \\n\ud15c\ud50c\ub9bf \ucf5c\ubc31 \ud328\ud134\uc744 \uc774\uc6a9\ud558\uc5ec \ub098\ub9cc\uc758 JdbcTemplate\uc744 \ub9cc\ub4e4\uc5b4\ubcf4\uc558\ub2e4. \\n\\n### \uae30\uc874 \ucf54\ub4dc\\n\\n\\n\\n\\n```java\\npublic class User {\\n private final int id;\\n private final String name;\\n\\n public User(final int id, final String name) {\\n this.id = id;\\n this.name = name;\\n }\\n\\n public int getId() {\\n return id;\\n }\\n\\n public String getName() {\\n return name;\\n }\\n}\\n```\\n\\n\\n\\n\\n\\n```java\\npublic class UserDao {\\n private final ConnectionPool connectionPool;\\n\\n public UserDao(final ConnectionPool connectionPool) {\\n this.connectionPool = connectionPool;\\n }\\n\\n public void insert(final String name) {\\n final Connection connection = connectionPool.getConnection();\\n final String query = \\"INSERT INTO User (name) VALUES (?)\\";\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n preparedStatement.setString(1, name);\\n preparedStatement.executeUpdate();\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n\\n public void delete(final int userId) {\\n final Connection connection = connectionPool.getConnection();\\n final String query = \\"DELETE FROM user WHERE id = ?\\";\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n preparedStatement.setInt(1, userId);\\n preparedStatement.executeUpdate();\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n\\n public User findById(final int userId) {\\n final Connection connection = connectionPool.getConnection();\\n final String query = \\"SELECT * FROM user WHERE id = ?\\";\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n preparedStatement.setInt(1, userId);\\n final ResultSet resultSet = preparedStatement.executeQuery();\\n if (resultSet.next()) {\\n return new User(\\n resultSet.getInt(\\"id\\"),\\n resultSet.getString(\\"name\\")\\n );\\n }\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n return null;\\n }\\n\\n public List findAll() {\\n final Connection connection = connectionPool.getConnection();\\n final String query = \\"SELECT * FROM user\\";\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n final ResultSet resultSet = preparedStatement.executeQuery();\\n final List result = new ArrayList<>();\\n while (resultSet.next()) {\\n result.add(new User(\\n resultSet.getInt(\\"id\\"),\\n resultSet.getString(\\"name\\")\\n ));\\n }\\n return result;\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n}\\n```\\n\\n\\n\\n\\n\\n```java\\npublic class ConnectionPool {\\n private static final String SERVER = \\"localhost:13306\\";\\n private static final String DATABASE = \\"chess\\";\\n private static final String OPTION = \\"?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true\\";\\n private static final String URL = \\"jdbc:mysql://\\" + SERVER + \\"/\\" + DATABASE + OPTION;\\n private static final String USERNAME = \\"root\\";\\n private static final String PASSWORD = \\"root\\";\\n\\n private final AtomicInteger index = new AtomicInteger();\\n private final List connections;\\n\\n public ConnectionPool(final int connectionCount) {\\n connections = generateConnections(connectionCount);\\n }\\n\\n private List generateConnections(final int connectionCount) {\\n return Stream.generate(this::generateConnection)\\n .limit(connectionCount)\\n .collect(toList());\\n }\\n\\n private Connection generateConnection() {\\n try {\\n return DriverManager.getConnection(URL, USERNAME, PASSWORD);\\n } catch (SQLException e) {\\n throw new IllegalStateException(\\"\ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.\\");\\n }\\n }\\n\\n public Connection getConnection() {\\n int currentIndex = index.getAndIncrement();\\n return connections.get(currentIndex % connections.size());\\n }\\n}\\n```\\n\\n\\n\\n\\n### SELECT, DELETE \uc911\ubcf5 \uc81c\uac70\\n\\n\ubcc0\ud558\uc9c0 \uc54a\ub294 \ubd80\ubd84: try-with-resource, preparedStatement\ub97c \uc0ac\uc6a9\ud558\ub294 \ubd80\ubd84, executeUpdate\ub85c \uc2e4\ud589 \ub4f1\ub4f1 \\n\ubcc0\ud558\ub294 \ubd80\ubd84: SQL Query, \ub9e4\uac1c\ubcc0\uc218 \\n\\n\ub2e4\uc74c\uacfc \uac19\uc774 \ucffc\ub9ac\ub97c \uc2e4\ud589\ud558\ub294 \ubd80\ubd84\uc744 \ubd84\ub9ac\ud558\uace0 \uac00\ubcc0\uc778\uc218\ub97c \uc0ac\uc6a9\ud55c\ub2e4\uba74 SELECT\uc640 DELETE\uc758 \uc911\ubcf5\uc744 \uc81c\uac70\ud560 \uc218 \uc788\ub2e4. \\n\\n```java\\npublic void insert(final String name) {\\n final String query = \\"INSERT INTO User (name) VALUES (?)\\";\\n executeUpdate(query, name);\\n}\\n\\npublic void delete(final int userId) {\\n final String query = \\"DELETE FROM user WHERE user_id = ?\\";\\n executeUpdate(query, userId);\\n}\\n\\nprivate void executeUpdate(final String query, final Object... parameters) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n for (int i = 1; i <= parameters.length; i++) {\\n preparedStatement.setObject(i, parameters[i - 1]);\\n }\\n preparedStatement.executeUpdate();\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n}\\n```\\n\\n### \uc870\ud68c \ubd84\ub9ac\ud558\uae30 - 1. \ucf5c\ubc31\uc744 \uc704\ud55c \uc778\ud130\ud398\uc774\uc2a4 \uc815\uc758\\n\\n\uc870\ud68c\ub294 INSERT, DELETE\uc640 \ub2ec\ub9ac \uac12\uc744 \ubc18\ud658\ubc1b\uc544\uc57c \ud558\uae30 \ub54c\ubb38\uc5d0 \ub2e4\ub978 \ubc29\ubc95\uc744 \uc0ac\uc6a9\ud574\uc57c \ud55c\ub2e4. \\n\uc774 \ub54c \ucf5c\ubc31\uc774\ub77c\ub294 \uac83\uc744 \uc0ac\uc6a9\ud558\uc5ec \uc911\ubcf5\uc744 \uc81c\uac70\ud560 \uc218 \uc788\ub2e4. \\n\\n:::note \ucf5c\ubc31(Callback)\\n\\n\ud504\ub85c\uadf8\ub798\ubc0d\uc5d0\uc11c \ucf5c\ubc31\uc740 \ub2e4\ub978 \ucf54\ub4dc\uc758 \uc778\uc218\ub85c \ub118\uaca8\uc8fc\ub294 \uc2e4\ud589 \uac00\ub2a5\ud55c \ucf54\ub4dc\ub97c \ub73b\ud55c\ub2e4. \\n\uc790\ubc14\uc5d0\uc11c\ub294 \ub78c\ub2e4\ub098 \uc775\uba85 \ud074\ub798\uc2a4\ub97c \ub118\uaca8\uc11c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n```mermaid\\nflowchart LR\\n \ud074\ub77c\uc774\uc5b8\ud2b8 -- \ucf5c\ubc31\uc804\ub2ec --\x3e \uba54\uc11c\ub4dc\\n \uba54\uc11c\ub4dc -- \ub0b4\ubd80\ud638\ucd9c --\x3e \uc804\ub2ec\ubc1b\uc740\ucf5c\ubc31\\n```\\n\\n:::\\n\\n\\n\ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \uac12\uc744 \uc870\ud68c\ud558\uace0, \ud574\ub2f9 \uac12\uc744 \uac1d\uccb4\ub85c \ub9e4\ud551\ud558\uc5ec \uac12\uc744 \ubc18\ud658\ud574\uc57c \ud55c\ub2e4. \\nexecuteQuery\ub85c \uc870\ud68c\ud55c \uac12\uc740 ResultSet \uc548\uc5d0 \ub4e4\uc5b4\uac00\uc788\ub2e4. \\n\uc774\ub97c \uc6d0\ud558\ub294 \ud0c0\uc785\uc758 \uac12\uc73c\ub85c \ubcc0\ud658\ud574\uc57c\ud558\ub2c8 \uc77c\ub2e8 \ucf5c\ubc31\uc744 \uc704\ud55c \uc778\ud130\ud398\uc774\uc2a4\ub97c \ub9cc\ub4e4\uc5b4\uc57c \ud55c\ub2e4. \\n\\n```java\\n@FunctionalInterface\\npublic interface RowMapper {\\n User mapRow(final ResultSet resultSet) throws SQLException;\\n}\\n```\\n\\n### \uc870\ud68c \ubd84\ub9ac\ud558\uae30 - 2. \ub2e8\uac74 \uc870\ud68c\\n\\n\uc704\uc5d0\uc11c \uc815\uc758\ud55c RowMapper\ub97c \uba54\uc11c\ub4dc\uc5d0\uc11c \uc5b4\ub5bb\uac8c \uc0ac\uc6a9\ud574\uc57c \ud560\uae4c? \\n\uc544\ub798\uc640 \uac19\uc774 SQL \ucffc\ub9ac, RowMapper, \ud30c\ub77c\ubbf8\ud130\ub97c \ubd84\ub9ac\ud55c \uba54\uc11c\ub4dc\uc5d0 \ub118\uaca8\uc8fc\uace0 \ucffc\ub9ac \uc2e4\ud589 \ud6c4 \ub9e4\ud551\ud55c \uac12\uc744 \ubc18\ud658\ud558\ub3c4\ub85d \ud55c\ub2e4. \\n\\n```java\\npublic User findById(final int userId) {\\n final String query = \\"SELECT * FROM user WHERE id = ?\\";\\n return queryForSingleResult(query, resultSet -> {\\n final int id = resultSet.getInt(\\"id\\");\\n final String name = resultSet.getString(\\"name\\");\\n return new User(id, name);\\n }, userId);\\n}\\n\\nprivate User queryForSingleResult(\\n final String query,\\n final RowMapper rowMapper,\\n final Object... parameters\\n) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query);\\n final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {\\n if (resultSet.next()) {\\n return rowMapper.mapRow(resultSet);\\n }\\n return null;\\n } catch (SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n}\\n\\nprivate ResultSet executeQuery(\\n final PreparedStatement preparedStatement,\\n final Object[] parameters) throws SQLException {\\n for (int i = 1; i <= parameters.length; i++) {\\n preparedStatement.setObject(i, parameters[i - 1]);\\n }\\n return preparedStatement.executeQuery();\\n}\\n```\\n\\n### \uc870\ud68c \ubd84\ub9ac\ud558\uae30 - 3. \ub2e4\uac74 \uc870\ud68c\\n\\n\ub2e8\uac74 \uc870\ud68c\uc640 \uc720\uc0ac\ud558\ub2e4.\\n\\n```java\\npublic List findAll() {\\n final String query = \\"SELECT * FROM user\\";\\n return query(query, resultSet -> {\\n final int id = resultSet.getInt(\\"id\\");\\n final String name = resultSet.getString(\\"name\\");\\n return new User(id, name);\\n });\\n}\\n\\nprivate List query(final String query, final RowMapper rowMapper, final Object... parameters) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query);\\n final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {\\n final List result = new ArrayList<>();\\n while (resultSet.next()) {\\n result.add(rowMapper.mapRow(resultSet));\\n }\\n return result;\\n } catch (SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n}\\n\\nprivate ResultSet executeQuery(\\n final PreparedStatement preparedStatement,\\n final Object[] parameters) throws SQLException {\\n for (int i = 1; i <= parameters.length; i++) {\\n preparedStatement.setObject(i, parameters[i - 1]);\\n }\\n return preparedStatement.executeQuery();\\n}\\n```\\n\\n### \uc81c\ub124\ub9ad \uc0ac\uc6a9\ud558\uae30\\n\\n\uc704\uc758 \ucf54\ub4dc\ub294 User\ub97c \uc870\ud68c\ud560 \ub54c\ub9cc \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. \\n\uc544\ub798\uc640 \uac19\uc774 \uc81c\ub124\ub9ad\uc744 \uc801\uc6a9\ud558\uc5ec \ub2e4\ub978 Dao\uc5d0\uc11c\ub3c4 \uc0ac\uc6a9 \uac00\ub2a5\ud558\ub3c4\ub85d \ubcc0\uacbd\ud560 \uc218 \uc788\ub2e4.\\n\\n```java\\n@FunctionalInterface\\npublic interface RowMapper {\\n T mapRow(final ResultSet resultSet) throws SQLException;\\n}\\n\\nprivate List query(final String query, final RowMapper rowMapper, final Object... parameters) {...}\\nprivate T queryForSingleResult(final String query, final RowMapper rowMapper, final Object... parameters) {...}\\n```\\n\\n### \uba54\uc11c\ub4dc \ubd84\ub9ac\ud55c \ubd80\ubd84 \ud074\ub798\uc2a4\ub85c \ubd84\ub9ac\ud558\uae30 + Optional \uc0ac\uc6a9\ud558\uae30\\n\\n\uba54\uc11c\ub4dc\ub85c \ubd84\ub9ac\ud55c \ubd80\ubd84\uc744 JdbcTemplate\uc774\ub77c\ub294 \ud074\ub798\uc2a4\ub97c \ub9cc\ub4e4\uc5b4 \uc62e\uae34\ub2e4. \\n\ub610\ud55c null\uc744 \ubc18\ud658\ud558\uae30 \ubcf4\ub2e8 Optional\ub85c \uac10\uc2f8\uc11c \ubc18\ud658\ud558\ub3c4\ub85d \ubcc0\uacbd\ud55c\ub2e4. \\n\ucd5c\uc885\uc801\uc73c\ub85c \uc544\ub798\uc640 \uac19\uc740 \ucf54\ub4dc\uac00 \uc644\uc131\ub41c\ub2e4.\\n\\n\\n\\n\\n```java\\npublic class UserDao {\\n private final RowMapper rowMapper = resultSet -> {\\n final int id = resultSet.getInt(\\"id\\");\\n final String name = resultSet.getString(\\"name\\");\\n return new User(id, name);\\n };\\n private final JdbcTemplate jdbcTemplate;\\n\\n public UserDao(final JdbcTemplate jdbcTemplate) {\\n this.jdbcTemplate = jdbcTemplate;\\n }\\n\\n public void insert(final String name) {\\n final String query = \\"INSERT INTO User (name) VALUES (?)\\";\\n jdbcTemplate.executeUpdate(query, name);\\n }\\n\\n public void delete(final int userId) {\\n final String query = \\"DELETE FROM user WHERE user_id = ?\\";\\n jdbcTemplate.executeUpdate(query, userId);\\n }\\n\\n public Optional findById(final int userId) {\\n final String query = \\"SELECT * FROM user WHERE id = ?\\";\\n return jdbcTemplate.queryForSingleResult(query, rowMapper, userId);\\n }\\n\\n public List findAll() {\\n final String query = \\"SELECT * FROM user\\";\\n return jdbcTemplate.query(query, rowMapper);\\n }\\n}\\n```\\n\\n\\n\\n\\n```java\\npublic class JdbcTemplate {\\n private final ConnectionPool connectionPool;\\n\\n public JdbcTemplate(final ConnectionPool connectionPool) {\\n this.connectionPool = connectionPool;\\n }\\n\\n public void executeUpdate(final String query, final Object... parameters) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n for (int i = 1; i <= parameters.length; i++) {\\n preparedStatement.setObject(i, parameters[i - 1]);\\n }\\n preparedStatement.executeUpdate();\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n\\n public Optional queryForSingleResult(\\n final String query,\\n final RowMapper rowMapper,\\n final Object... parameters\\n ) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query);\\n final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {\\n if (resultSet.next()) {\\n return Optional.of(rowMapper.mapRow(resultSet));\\n }\\n return Optional.empty();\\n } catch (SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n\\n private ResultSet executeQuery(\\n final PreparedStatement preparedStatement,\\n final Object[] parameters\\n ) throws SQLException {\\n for (int i = 1; i <= parameters.length; i++) {\\n preparedStatement.setObject(i, parameters[i - 1]);\\n }\\n return preparedStatement.executeQuery();\\n }\\n\\n public List query(\\n final String query,\\n final RowMapper rowMapper,\\n final Object... parameters\\n ) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query);\\n final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {\\n final List result = new ArrayList<>();\\n while (resultSet.next()) {\\n result.add(rowMapper.mapRow(resultSet));\\n }\\n return result;\\n } catch (SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n}\\n```\\n\\n\\n"},{"id":"woowacourse-level1-retrospective","metadata":{"permalink":"/woowacourse-level1-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-01-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca81 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-04-01-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca81 \ud68c\uace0.mdx","title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 1 \ud68c\uace0","description":"\ub808\ubca8 1\uc774 \ub05d\ub0ac\ub2e4.","date":"2023-04-01T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 1\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":7.48,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 1 \ud68c\uace0","slug":"woowacourse-level1-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30","permalink":"/custom-jdbc-template"},"nextItem":{"title":"\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0","permalink":"/chess-retrospective"}},"content":"\ub808\ubca8 1\uc774 \ub05d\ub0ac\ub2e4. \\n\uc6b0\ud14c\ucf54\ub97c \uc2dc\uc791\ud558\uae30 \uc804 \ub0b4\uac00 \uc815\ud574\ub450\uc5c8\ub358 \ubaa9\ud45c \uc774\uc0c1\uc73c\ub85c \ub2ec\uc131\ud588\uae30 \ub54c\ubb38\uc5d0 \ub9e4\uc6b0 \ub9cc\uc871\uc2a4\ub7fd\ub2e4. \\n\ud63c\uc790 \ub3c5\ud559\uc744 \ud560 \ub550 \uc774 \ubc29\ud5a5\uc73c\ub85c \uacf5\ubd80\ud558\ub294 \uac8c \ub9de\ub294\uc9c0 \uacc4\uc18d \ubc18\ucd94\ud558\ub2e4 \uacb0\uad6d \ubb34\uae30\ub825\ud568\uc5d0 \ube60\uc838\ub4e4\uc5c8\ub2e4. \\n\ud558\uc9c0\ub9cc \uc774\uc81c\ub294 \uac19\uc774 \uacf5\ubd80\ud560 \uc0ac\ub78c\ub3c4 \uc788\uace0, \uc774\uc57c\uae30\ud560 \uc0ac\ub78c\ub3c4 \uc788\uae30 \ub54c\ubb38\uc5d0 \uc990\uae30\ub294 \uc77c\ub9cc \ub0a8\uc740 \uac83 \uac19\ub2e4. \\n\\n### Keep\\n\\n**\ub098\ub9cc\uc758 \ub8e8\ud2f4 \ub9cc\ub4e4\uae30** \\n\\n\uc2a4\uc2a4\ub85c\uac00 \uc678\ubd80\uc758 \uc601\ud5a5\uc744 \ub9ce\uc774 \ubc1b\ub294\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\ucd5c\ub300\ud55c \uafb8\uc900\ud788 \ud560 \uc218 \uc788\ub294 \uc2dc\uac04\uc744 \ub9cc\ub4dc\ub294 \uac83\uc774 \uc911\uc694\ud558\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\\n\ub9e4\uc77c 8\uc2dc\uc5d0 \ub3c4\ucc29\ud558\uc5ec \uc544\uce68\uc5d0 \ud574\uc57c \ud560 \uc77c\uc744 \uc815\ub9ac\ud558\uac70\ub098, \uc6b0\uc120\uc21c\uc704\uc5d0 \ub530\ub77c \ucc98\ub9ac\ud558\uace0 \\n\uc18c\ud654\ub2a5\ub825\uc774 \ubd80\uc871\ud558\uae30 \ub54c\ubb38\uc5d0 \uc810\uc2ec\uc740 \ub3c4\uc2dc\ub77d(\uadf8\ub798\ubd24\uc790 \uacc4\ub7802\uac1c)\uc744 \uc900\ube44\ud558\uace0 \\n\ud56d\uc0c1 \ub611\uac19\uc740 \ucee8\ub514\uc158\uc744 \uc720\uc9c0\ud558\uae30 \uc704\ud574 \ud56d\uc0c1 6\uc2dc\uc5d0 \uc9d1\uc5d0 \uac04\ub2e4. \\n\uc774\uc81c \ubc14\ube60\uc9c8 \ud14c\ub2c8 \uc77c\ucc0d \uc9d1\uc5d0 \uac00\ub294 \uc77c\uc740 \uc5b4\uca54 \uc218 \uc5c6\uc774 \uc904\uc5b4\ub4e4\uaca0\uc9c0\ub9cc\ud83d\ude22 \\n\\n\uc120\ud0dd\ub3c4 \ube44\uc6a9\uc774\ub2e4. \uc55e\uc73c\ub85c \uc758\uc0ac\uacb0\uc815\uc774 \ud544\uc694 \uc5c6\ub294 \ubd80\ubd84\uc744 \ucd5c\ub300\ud55c \ub9ce\uc774 \ub9cc\ub4e4\uc5b4\uc57c\uaca0\ub2e4. \\n\\n**\ud06c\ub8e8\ub4e4\uacfc \uce5c\ud558\uac8c \uc9c0\ub0b4\uae30** \\n\\n10\uba85 \uc815\ub3c4\uc758 \ud06c\ub8e8\uc758 \ub2c9\ub124\uc784\uc744 \uc678\uc6b0\uace0 \uce5c\ud558\uac8c \uc9c0\ub0b8\ub2e4\uba74 \uc131\uacf5\uc801\uc774\ub77c\uace0 \uc0dd\uac01\ud588\uc5c8\ub2e4. \\n\ud558\ub2e4 \ubcf4\ub2c8 \ub354 \ub9ce\uc740 \ud06c\ub8e8\ub4e4\uc758 \ub2c9\ub124\uc784\uc744 \uc678\uc6b4 \uac83 \uac19\ub2e4. \\n\uc55e\uc73c\ub85c\ub3c4 \ud06c\ub8e8\ub4e4\uacfc \uce5c\ud558\uac8c \uc9c0\ub0b4\uace0 \uc544\ubb34 \ub54c\ub098 \ub9d0\uc744 \uac78 \uc218 \uc788\ub294 \ud06c\ub8e8\uac00 \ub298\uc5b4\ub098\uae38 :) \\n\\n**\uae00\uc4f0\uae30** \\n\\n\uae00\uc744 \uc798 \uc4f0\ub294 \ud3b8\uc740 \uc544\ub2c8\uc9c0\ub9cc \uafb8\uc900\ud788 \uc791\uc131\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. \\n\ub9e4 \ubbf8\uc158\ub9c8\ub2e4 \ud68c\uace0\ub97c \uc791\uc131\ud558\ub2c8 \uc0dd\uac01\ub3c4 \uc815\ub9ac\ub418\uace0 \uac1c\uc120\uc810\ub3c4 \ucc3e\uc744 \uc218 \uc788\uc5b4\uc11c \uc88b\uc558\ub2e4. \\n\\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\uc5d0\ub294 \ub808\ubca8\ub9c8\ub2e4 \uae00\uc4f0\uae30\ub97c \uc9c4\ud589\ud558\ub294\ub370, \uc6b4\uc774 \uc88b\uac8c \uae00\uc4f0\uae30 \uc0c1\uc744 \ubc1b\uc558\ub2e4. \\n\uc0ac\uc2e4 \uac89\uc73c\ub85c \ub4dc\ub7ec\ub0b4\uc9c0 \uc54a\uc558\uc9c0\ub9cc \uaf2d \ubc1b\uc544\ubcf4\uace0 \uc2f6\uc5c8\ub2e4. \\n\uae00\uc4f0\uae30 \uc870\uc6d0, \ud22c\ud45c\ud574 \uc900 \ud06c\ub8e8\ub4e4\uc5d0\uac8c \ub108\ubb34 \uac10\uc0ac\ud558\ub2e4. \\n\\n**\ucf54\ub4dc \ub9ac\ubdf0 \uc2a4\ud130\ub514** \\n\\n\ub204\ub204, \uc8fc\ub178, \ub2e4\uc990, \ub9d0\ub791, \ubc15\uc2a4\ud130, \uc624\uc789, \uae43\uc9f1\uc640 \ucf54\ub4dc \ub9ac\ubdf0 \uc2a4\ud130\ub514\ub97c \uc9c4\ud589\ud588\ub2e4. \\n\uacfc\uc5f0 \ub3c4\uc6c0\uc774 \ub420\uae4c \uc0dd\uac01\ud588\uc9c0\ub9cc \uacb0\uacfc\uc801\uc73c\ub85c\ub294 \ucf54\ub4dc \ub9ac\ubdf0\ub97c \ud558\uba74\uc11c \uc131\uc7a5\uc744 \ub9ce\uc774 \ud55c \uac83 \uac19\ub2e4. \\n\ud22c\uc790\ud55c \uc2dc\uac04 \ub300\ube44 \uac00\uc131\ube44\uac00 \uc88b\uc740 \ud65c\ub3d9\uc774\uc5c8\ub2e4. \\n\ub204\ub204\uac00 \uc2a4\ud130\ub514\uc7a5\uc778\ub370 \uacfc\uc5f0 \uafb8\uc900\ud788 \uc774\uc5b4\ub098\uac00\ub824\ub098? \\n\\n**\ub808\ubca8 \uc778\ud130\ubdf0**\\n\\n\uc778\ud130\ubdf0\ud560 \ub54c \ub9ce\uc774 \ub5a8\uc9c0 \uc54a\uc544\uc11c \uc88b\uc558\ub2e4. \\n\ub0a8\ub4e4 \uc55e\uc5d0\uc11c \uc774\uc57c\uae30\ub97c \ud558\uac70\ub098, \uba74\uc811\uc744 \ubcf4\uba74 \ud56d\uc0c1 \uc5c4\uccad \ub5a8\uc5b4\uc11c \uac71\uc815\ud588\ub294\ub370 \\n\uae30\uc220\uc801\uc778 \uc9c8\ubb38\uc744 \ubc1b\uc558\uc744 \ub54c \ub5a8\uc9c0 \uc54a\uace0 \uc798 \ub300\ub2f5\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \uc0dd\ud65c\uc744 \ud558\uba74\uc11c \ub2e4\ub978 \ud06c\ub8e8\uac00 \uc9c8\ubb38\ud588\uc744 \ub54c, \ucd5c\ub300\ud55c \uc774\ud574\ud558\uae30 \uc27d\uac8c \uc124\uba85\ud558\ub824\uace0 \ud588\ub358 \uacbd\ud5d8\uc774 \ub3c4\uc6c0\uc774 \ub41c \uac83 \uac19\ub2e4. \\n\uc774\ud6c4 \ub808\ubca8 \uc778\ud130\ubdf0\ub97c \uc9c4\ud589\ud560 \ub54c \ub2e4\uc74c\uacfc \uac19\uc740 \ubd80\ubd84\uc744 \uace0\ub824\ud558\uba74 \ub354 \uc88b\uc744 \uac83 \uac19\ub2e4. \\n- \ub300\ub2f5\ud558\uba74\uc11c \uc9c8\ubb38\uc744 \uacc4\uc18d \uc0dd\uac01\ud558\uba70 \uc78a\uc5b4\ubc84\ub9ac\uc9c0 \ub9d0\uae30 \\n- \ub450\uad04\uc2dd \ud45c\ud604\\n- \uc124\uba85\ud558\ub2e4\uac00 \uc798\ubabb \uc124\uba85\ud55c \uac83 \uac19\uc73c\uba74 \ub2e4 \ub04a\uace0 \ub2e4\uc2dc \uc774\uc57c\uae30\ud574\ub3c4 \ub420\uc9c0 \ubb3c\uc5b4\ubcf4\uae30 \\n- \uc124\uba85\ud560 \uc218 \uc788\uc744\ub9cc\ud07c \uc2dc\uac04 \ucda9\ubd84\ud788 \uac00\uc9c0\uae30\\n- \uc778\ud130\ubdf0\uc5b4\uc758 \uc9c8\ubb38 \uc758\ub3c4\ub97c \uba85\ud655\ud788 \uc774\ud574\ud558\uc9c0 \ubabb\ud588\ub2e4\uba74 \uc758\ub3c4 \ub2e4\uc2dc \ubb3c\uc5b4\ubcf4\uae30\\n- \ub05d\ub9fa\ub294 \ubd80\ubd84 \uc5f0\uc2b5\ud558\uae30(\uc790\uc2e0\uac10 \uc788\uac8c)\\n- \uae30\uc220\uc801\uc778 \uc9d1\ucc29\uac00\uc9c0\uae30\\n- \uae30\uc220\uc801\uc778 \ubd80\ubd84\uc744 \uaf3c\uaf3c\ud788 \uc900\ube44\ud588\uc73c\uba74 \ud611\uc5c5 \uad00\ub828 \uc9c8\ubb38\ub3c4 \uc900\ube44\ud558\uae30\\n\\n### Problem\\n\\n**\ud398\uc5b4\ud504\ub85c\uadf8\ub798\ubc0d** \\n\\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\ub97c \uc9c4\ud589\ud558\uba74\uc11c \uac00\uc7a5 \uc5b4\ub824\uc6b4 \ud65c\ub3d9 \uc911 \ud558\ub098\ub77c\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\ud398\uc5b4\ub294 \ub9e4\ubc88 \ubc14\ub00c\uace0, \ubbf8\uc158\uc758 \ubcf5\uc7a1\ub3c4\ub3c4 \uc99d\uac00\ud558\uae30 \ub54c\ubb38\uc778 \uac83 \uac19\ub2e4. \\n\uc18c\ud1b5 \ub2a5\ub825, \uc2dc\uac04\uad00\ub9ac\uac00 \ubd80\uc871\ud588\uace0, \ub9cc\uc871\uc2a4\ub7fd\uc9c0 \uc54a\uc558\ub2e4. \\n\ud558\uc9c0\ub9cc \ud398\uc5b4\ub97c \uc9c4\ud589\ud558\uace0, \ud68c\uace0\ub97c \ud558\ub2e4 \ubcf4\ub2c8 \ub098\ub9cc\uc758 \ub178\ud558\uc6b0\uac00 \uc313\uc774\ub294 \ub290\ub08c\uc774\ub2e4. \\n\ub808\ubca8 2\uc5d0\uc11c\ub294 \ubd80\uc871\ud588\ub358 \ubd80\ubd84\uc744 \uac1c\uc120\ud558\uc5ec \ud568\uaed8\ud558\uace0 \uc2f6\uc740 \ud398\uc5b4\uac00 \ub418\uace0 \uc2f6\ub2e4. \\n\\n**\uc9d1\uc911\ud558\ub294 \uc2dc\uac04\u23f1\ufe0f \ubd80\uc871** \\n\\n\ub808\ubca8 1\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc9d1\uc911\ud558\ub294 \uc2dc\uac04\uc774 \ub9ce\uc774 \ubd80\uc871\ud588\ub2e4. \\n\uc774\ub978 \uc544\uce68\uacfc \uc624\ud6c4\uc5d0 \uac1c\uc778\uc801\uc73c\ub85c \uc9d1\uc911\ud560 \uc218 \uc788\ub294 \uacf5\uac04\uc744 \uc608\uc57d\ud574\uc11c \uc628\uc804\ud788 \ub098\ub9cc\uc758 \uc2dc\uac04\uc744 \uac00\uc838\uc57c\uaca0\ub2e4. \\n\\n### Try\\n\\n**\ud5c8\ube0c\ud83c\udf3f\uc640\uc758 \ud2f0\ud0c0\uc784?** \\n\\n\uc18c\ud504\ud2b8 \uc2a4\ud0ac\uc744 \ub298\ub9b4 \ubc29\ubc95\uc744 \uc0dd\uac01\ud558\ub2e4\uac00 \ub300\ud654\ub97c \ub098\ub204\uc9c0 \ubabb\ud55c \ub2e4\ub978 \ud06c\ub8e8\ub4e4\uacfc \uae5c\uc9dd \ucee4\ud53c\ucc57\uc744 \ud558\uba74 \uc5b4\ub5a8\uae4c \uc0dd\uac01\ud588\ub2e4. \\n\uc608\ub97c \ub4e4\uc5b4 \uc7a1\ub2f4\ubc29\uc5d0 `\uc800\uc640 \ucee4\ud53c\ucc57 \ud558\uc2e4 \ubd84 :)` \ud558\uba74\uc11c \uc62c\ub9b4 \uc218 \uc788\uc744 \uac83 \uac19\ub2e4. \\n\ucc38\uc5ec\ud558\ub294 \uc0ac\ub78c\uc774 \uc788\uc744\uc9c0, \uc548 \uc88b\uac8c \ubcf4\ub294 \uac8c \uc544\ub2d0\uc9c0 \uac71\uc815\ub418\uc9c0\ub9cc \uadf8\ub798\ub3c4 \uc7ac\ubc0c\uc744 \uac83 \uac19\ub2e4. \\n\uc800\ub791 \ud5c8\ube0c\ud2f0 \ud55c\uc794 \ud558\uc2e4\ub798\uc694? \\n\\n**\uae30\uc220\uc801\uc778 \ubd80\ubd84** \\n\\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \uc0dd\ud65c\uc744 \ud558\uba74\uc11c \uc18c\ud504\ud2b8 \uc2a4\ud0ac\uc5d0 \uc870\uae08 \ub354 \ubb34\uac8c\ub97c \ub450\ub2e4 \ubcf4\ub2c8 \uc774\ub860\uc801\uc778 \ubd80\ubd84\uc774 \ubd80\uc871\ud560 \uc218 \uc788\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uc2dc\uac04\uc758 \uc5ec\uc720\uac00 \ub420 \ub54c \ucc45\uc744 \uc870\uae08\uc529 \uc77d\uc5b4\uc57c\uaca0\ub2e4. \\n\ube14\ub85c\uadf8\uc5d0 \uae30\uc220\uc801\uc778 \ubd80\ubd84\uc744 \ub9ce\uc774 \uc815\ub9ac\ud558\uc9c0 \uc54a\uc558\ub294\ub370, \uc870\uae08 \ub354 \uae4a\uac8c \uacf5\ubd80\ud558\uace0 \uc815\ub9ac\ud558\ub294 \uc2dc\uac04\ub3c4 \uac00\uc838\uc57c\uaca0\ub2e4. \\n\\n### \ub808\ubca8 1\uc744 \ub9c8\ubb34\ub9ac\ud558\uba70 \\n\\n\uc2dc\uac04\uc774 \ube60\ub974\uac8c \ud758\ub7ec\uac14\ub2e4. \\n\ud0c0\uc778\uc5d0\uac8c \uc88b\uc740 \uc601\ud5a5\uc744 \uc8fc\uae30\uc704\ud574, \ubc29\ud559\ub3d9\uc548 \ub098\ub97c \ucc59\uae30\ub294 \uc2dc\uac04\uc744 \uac00\uc838\uc57c\uaca0\ub2e4. \\n\ub610\ud55c \ud568\uaed8 \uc77c\ud558\uace0 \uc2f6\uc740 \uc0ac\ub78c\uc744 \ubaa9\ud45c\ub85c \uc55e\uc73c\ub85c\ub3c4 \uafb8\uc900\ud788 \uc758\uc2dd\uc801 \ub178\ub825\uc744 \ud574\uc57c\uaca0\ub2e4."},{"id":"chess-retrospective","metadata":{"permalink":"/chess-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-03-31-\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-1/2023-03-31-\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0","description":"\uccb4\uc2a4","date":"2023-03-31T00:00:00.000Z","formattedDate":"2023\ub144 3\uc6d4 31\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":7.63,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0","slug":"chess-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 1 \ud68c\uace0","permalink":"/woowacourse-level1-retrospective"},"nextItem":{"title":"\uc77c\ubc18\uc801\uc778 \ucc45\uc784 \ud560\ub2f9\uc744 \uc704\ud55c \ud328\ud134","permalink":"/grasp"}},"content":"### \uccb4\uc2a4\\n\uccb4\uc2a4 \ubbf8\uc158\uc5d0\ub294 \uac00\ube44\uc640 \ud398\uc5b4\uac00 \ub9e4\uce6d\ub418\uc5c8\ub2e4! \\n\uccb4\uc2a4\ub294 \uc774\uc804 \ubbf8\uc158\ub4e4\ubcf4\ub2e4 \ud6e8\uc52c \ubcf5\uc7a1\ud55c \ub3c4\uba54\uc778\uc774\uc5c8\ub2e4. \\n\ud558\uc9c0\ub9cc \uac00\ube44\uc640 \ub098\ub294 \uccb4\uc2a4 \ub3c4\uba54\uc778\uc774 \uc775\uc219\ud574\uc11c \ub354 \ud3b8\ud55c \ub9c8\uc74c\uc73c\ub85c \uc2dc\uc791\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc5b4\ub824\uc6e0\ub358 \ubd80\ubd84\uc740 \uae30\ubb3c\uc758 \uc774\ub3d9, \uc774\ub3d9\uc2dc \uacbd\ub85c\uc5d0 \uae30\ubb3c\uc774 \uc874\uc7ac\ud558\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ubd80\ubd84\uc774\uc5c8\ub2e4. \\n \\n\uac00\ube44\uac00 \uc9d1\uc5d0\uac00\uc11c\ub3c4 \uae30\ubb3c\uc758 \uc774\ub3d9 \uad00\ub828\ud574 \uc0dd\uac01 \uc815\ub9ac\ud55c \uae00\uc744 \ubcf4\ub0b4\uc918\uc11c \ub354\uc6b1 \ube68\ub9ac \uc9c4\ud589\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\ucd5c\uc885\uc801\uc73c\ub85c \uacb0\uc815\ud55c \ubd80\ubd84\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\\n**\uac01 \uae30\ubb3c\uc758 \uc774\ub3d9 \uac00\ub2a5\uc5ec\ubd80** \\nRank\uc640 File\uc740 \uac01\uac01 \uc704\uce58\uac12\uc744 \uac00\uc9c0\uace0 \uc788\uace0, \uac12\uc758 \ucc28\uc774\ub97c \uc774\uc6a9\ud574\uc11c \uac01 \uae30\ubb3c\uc758 \uc774\ub3d9 \uac00\ub2a5 \uc5ec\ubd80\ub97c \uacc4\uc0b0\ud588\ub2e4. \\n\uc9c1\uc120 \u2192 Rank\uc640 File \ucc28\uc774 \uc911 \ud558\ub098\uac00 0\uc774\uc5b4\uc57c \ud55c\ub2e4. \\n\ub300\uac01\uc120 \u2192 Rank\uc640 File \ucc28\uc774\uc758 \uc808\ub300\uac12\uc774 \uac19\uc544\uc57c \ud55c\ub2e4. ex) abs(-2) == abs(2) \\n\ub098\uc774\ud2b8 \u2192 \ucc28\uc774\uc758 \uc808\ub300\uac12\uc774 \ud558\ub098\ub294 2 \ub098\uba38\uc9c0 \ud558\ub098\ub294 1\uc774\uc5b4\uc57c \ud55c\ub2e4.\\n\\n**\ub3c4\ucc29 \uce78\uc758 \uae30\ubb3c \uc5ec\ubd80** \\n\uc544\uad70 \u2192 \uc774\ub3d9\uc774 \ubd88\uac00\ub2a5\ud558\ub2e4. \\n\uc801\uad70 \u2192 \uc774\ub3d9\uc774 \uac00\ub2a5\ud558\ub2e4. \uc801\uad70\uc744 \uc7a1\ub294\ub2e4. \\n\\n**\uc911\uac04\uc5d0 \uae30\ubb3c \uc874\uc7ac \uc5ec\ubd80** \\n\uc774\ub3d9 \uacbd\ub85c\uc5d0 \uae30\ubb3c\uc774 \uc874\uc7ac\ud558\uba74 \uc548\ub41c\ub2e4. \\n\\n**\ub370\uc774\ud130\ubca0\uc774\uc2a4 \uc0ac\uc6a9** \\n\uccb4\uc2a4 \ubbf8\uc158\uc740 \ud2b9\ubcc4\ud558\uac8c \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc640 \uc5f0\uacb0\ud558\ub294 \ubd80\ubd84\uc774 \uc788\uc5c8\ub2e4. \\n\uccb4\uc2a4 \uac8c\uc784\uc758 \uc0c1\ud0dc\ub97c \ub2e4\uc74c\uc758 \ub450\uac00\uc9c0 \ubc29\ubc95\uc73c\ub85c \uc815\ud560 \uc218 \uc788\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n- \uae30\ubb3c \uc804\uccb4\ub97c \uc800\uc7a5\ud558\ub294 \ubc29\ubc95 \\n- \uae30\ubcf4\ub97c \uc800\uc7a5\ud558\uace0 \uac8c\uc784\uc744 \ubd88\ub7ec\uc640 \uae30\ubcf4\ub300\ub85c \uc774\ub3d9\uc2dc\ud0a4\ub294 \ubc29\ubc95 \\n\\n\uae30\ubb3c\uc774 \uc774\ub3d9\ud560 \ub54c\ub9c8\ub2e4 \uac12\uc744 \uc800\uc7a5\ud558\uace0 \uc2f6\uc5c8\uace0, \uae30\ubcf4\ub97c \uc800\uc7a5\ud558\ub294 \ubc29\ubc95\uc744 \uc120\ud0dd\ud588\ub2e4. \\n\uae30\ubb3c \uc804\uccb4\ub97c \uc800\uc7a5\ud558\uc9c0 \uc54a\uc740 \uc774\uc720\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n- \ud134\uacfc \uac19\uc740 \ubd80\uac00\uc801\uc778 \uc694\uc18c\ub97c \uc800\uc7a5\ud574\uc57c \ud55c\ub2e4. \\n- \uc774\ub3d9\uc744 \ud560 \ub54c \uae30\ubb3c\uc774 \uc7a1\ud788\ub294 \uacbd\uc6b0 update \ucffc\ub9ac(\uc774\ub3d9 \uae30\ubb3c)\uc640 delete(\uc7a1\ud78c \uae30\ubb3c) 2\uac1c\uc758 \ucffc\ub9ac\ub97c \ub0a0\ub824\uc57c \ud55c\ub2e4. \\n- \ud604\uc7ac \uad6c\uc870\uc5d0\uc11c \ub3c4\uba54\uc778\uc758 \ubcc0\uacbd\uc774 \ud06c\uac8c(\ucd08\uae30 \uc0c1\ud0dc\ub97c \uad6c\uc131\ud558\ub294 \ubd80\ubd84) \uc77c\uc5b4\ub098\uc57c \ud55c\ub2e4. \\n\\n\uc815\ub9ac\ud558\uc790\uba74 \uae30\ubb3c \uc804\uccb4 \uc800\uc7a5\uacfc \uae30\ubcf4 \uc800\uc7a5\uc740 \ub2e4\uc74c\uacfc \ucc28\uc774\uac00 \uc788\ub2e4. \\n\ubcf4\ub4dc\uc800\uc7a5: \ucd08\uae30\uc0c1\ud0dc\uc5d0\uc11c 32\uac1c\uc758 Insert \ucffc\ub9ac(\uae30\ubb3c\uc758 \uc704\uce58) + \uae30\ubb3c \uc774\ub3d9 \uc2dc \uc6c0\uc9c1\uc784 \ubcc0\uacbd(\uc7a1\ud788\ub294 \uacbd\uc6b0 2\uac1c\uc758 \ucffc\ub9ac) \\n\uae30\ubcf4\uc800\uc7a5: \ucd08\uae30\uc0c1\ud0dc \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0\uc11c \uad6c\uc131 + \uc800\uc7a5\ub41c \uae30\ubcf4\ub97c select \ucffc\ub9ac\ub85c \uc870\ud68c\ud574\uc11c \uc0ac\uc6a9(1\ud68c) + insert \ucffc\ub9ac(\uc774\ub3d9 \ub2f9 1\ud68c)\\n\\n\ucd94\uac00\ub85c \uae30\ubcf4\uc800\uc7a5\uc774 \uad6c\ud604\ub3c4 \ub354\uc6b1 \uac04\ub2e8\ud558\ub2e4. \ud83d\udc4d \\n\\n**\ubd80\uac00\uc801\uc778 \ubd80\ubd84**\\n\\n\ub9ac\ubdf0\uc5b4\uc778 \ucc30\ub9ac\ud83c\udf6b\uac00 \ub3d9\uc2dc\uc5d0 \uc5ec\ub7ec \uac8c\uc784\uc774 \uc9c4\ud589\ub41c\ub2e4\uba74 \uc5b4\ub5a8\uc9c0? \uc5d0 \ub300\ud55c \ucf54\uba58\ud2b8\ub97c \ub0a8\uaca8\uc8fc\uc154\uc11c \ub2e4\uc591\ud55c \uc2dc\ub3c4\ub97c \ud574\ubd24\ub2e4. \\n- \ub204\ub204\uc758 \ub3c4\uc6c0\uc73c\ub85c ConnectionPool \uad6c\ud604 \\n- ThreadLocal \uc0ac\uc6a9\ud574\uc11c \uc4f0\ub808\ub4dc \ubcc4 \uc138\uc158 \uad00\ub9ac \\n- \uc2e4\uc81c\ub85c \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ub0b4\uc5d0\uc11c \uccb4\uc2a4 \uac8c\uc784\uc774 \uc9c4\ud589\ub418\ub294 Board\ub97c ConcurrentHashMap\uc73c\ub85c \uc800\uc7a5(\uc0ac\uc2e4 \uc774 \ubd80\ubd84\uc740 \ud604\uc7ac \uad6c\uc870\uc5d0\uc11c \ud544\uc694\uc5c6\uc9c0\ub9cc 2\uba85\uc774 \uc11c\ub85c \uac8c\uc784\ud558\ub294 \uacbd\uc6b0\ub97c \uc0dd\uac01\ud574\uc11c \ub123\uc5b4\ubcf4\uc558\ub2e4.) \\n\\n\ub450 \uba85\uc774 \uc11c\ub85c \uac19\uc740 \ubc29\uc5d0 \uc785\uc7a5\ud558\uc5ec \uac8c\uc784\uc744 \uc9c4\ud589\ud55c\ub2e4\uba74 \ucd9c\ub825\ud558\ub294 \ubd80\ubd84\uc774 \uae4c\ub2e4\ub85c\uc6cc\uc9c8 \uac83 \uac19\ub2e4\uace0 \uc608\uc0c1\ub418\uc5b4(Board\uc5d0 \uc635\uc800\ubc84 \ud328\ud134\uc744 \uc0ac\uc6a9\ud574\uc57c\ub418\ub098?) \ud574\ubcfc \uc5c4\ub450\uac00 \ub098\uc9c0 \uc54a\uc558\ub2e4. \\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n**\uaf3c\uaf3c\ud558\uac8c \ucf54\ub4dc\ub97c \uc791\uc131\ud558\uc9c0 \ubabb\ud55c \ubd80\ubd84** \\nDB \uad00\ub828 \ubd80\ubd84\uc744 \uaf3c\uaf3c\ud558\uac8c \ucf54\ub529\uc744 \ud558\uc9c0 \ubabb\ud588\ub2e4. \\n\ub3c4\uba54\uc778 \ub85c\uc9c1\uc5d0\ub9cc \uc9d1\uc911\ud558\ub2e4\ubcf4\ub2c8 \uc815\uc801 \uc911\uc694\ud55c DB\uc758 \ucf54\ub4dc\uc758 \uc608\uc678\ucc98\ub9ac, \ube48 \uac12\uc744 \ubc18\ud658 \ud558\ub294 \ubd80\ubd84\uc744 \uaf3c\uaf3c\ud558\uac8c \ucc98\ub9ac\ud558\uc9c0 \ubabb\ud588\ub2e4. \\n\ud558\uc9c0\ub9cc \ucc30\ub9ac\uc758 \uaf3c\uaf3c\ud55c \ub9ac\ubdf0\ub85c DB\ubd80\ubd84\uacfc \ub098\ub9cc\uc758 JdbcTemplate\uc744 \uae54\ub054\ud558\uac8c \uad6c\ud604\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n**\uc2dc\uac04\uc5d0 \ub300\ud55c \ubd80\ub2f4\uac10** \\n\ucd08\ubc18\uc5d0\ub294 \uc5ec\uc720\ub86d\uc9c0\ub9cc \uc81c\ucd9c \ub9c8\uac10\uc5d0 \uac00\uae4c\uc6cc\uc9c8 \uc218\ub85d \uc0ac\ub78c\uc774 \uae09\ud574\uc9c0\ub294 \uac83 \uac19\ub2e4. \\n\ub2e4\uc74c \ud398\uc5b4\ud504\ub85c\uadf8\ub798\ubc0d\ud560 \ub550 \uc18d\ub3c4\ub97c \uc870\uc808\ud558\uace0, \ub9c8\uc74c\uc5d0 \uc5ec\uc720\ub97c \uac00\uc838\uc57c\uaca0\ub2e4. \\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**DAO \uc911\ubcf5 \uc81c\uac70**\\n\\n\ud504\ub864\ub85c\uadf8\uc5d0 [\uae00](https://prolog.techcourse.co.kr/studylogs/2947)\uc744 \uc791\uc131\ud588\ub2e4. \\nDAO\ub97c \uc791\uc131\ud558\ub294\ub370 try-catch-resources\uc640 \uc5ec\ub7ec \ucf54\ub4dc\uac00 \uc911\ubcf5\ub418\uc11c \uc81c\uac70\ud558\uace0\uc2f6\uc5c8\ub2e4. \\n\ud15c\ud50c\ub9bf \ucf5c\ubc31 \ud328\ud134\uc73c\ub85c \uae54\ub054\ud558\uac8c \uc911\ubcf5\uc744 \uc81c\uac70\ud560 \uc218 \uc788\uc5c8\ub2e4.\ud83d\udc4d\\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n**\ud398\uc5b4 \uc0dd\uac01\ud558\uae30** \\n\uac00\ube44\ub294 \ub204\uad6c\ubcf4\ub2e4 \ud398\uc5b4\ub97c \uc0dd\uac01\ud558\uace0, \ubc30\ub824\ud574\uc8fc\ub294 \ud398\uc5b4\uc600\ub2e4. \\n\uc911\uac04 \uc911\uac04 \ub2f9 \ub5a8\uc5b4\uc9c8\uae4c\ubd10 \uac71\uc815\ub3c4 \ud574\uc8fc\uace0, \ub098\uc758 \ucee8\ub514\uc158\ub3c4 \ud655\uc778\ud574\uc92c\ub2e4! \\n\\n**\ubbf8\uc158 \ubab0\uc785\ud558\uae30** \\n\ucd5c\uadfc\uc5d0 \ubbf8\uc158\uc5d0 \uc798 \ubab0\uc785\ud558\uc9c0 \ubabb\ud588\ub2e4. \\n\uac00\ube44\ub294 \ud398\uc5b4\ub97c \uc9c4\ud589\ud560 \ub54c \ubbf8\uc158\uc5d0 \ub300\ud55c \ubab0\uc785\ub3c4\uac00 \ub9e4\uc6b0 \uc88b\uc558\ub2e4. \\n\uc9d1\uc5d0\uac00\uc11c\ub3c4 \uccb4\uc2a4 \uc774\ub3d9\uc5d0 \ub300\ud55c \ub85c\uc9c1\uc744 \uc5b4\ub5bb\uac8c \uad6c\ud604\ud560 \uc9c0 \uc0dd\uac01\ud55c \ub4a4 \uaf3c\uaf3c\ud574\uc11c \uc815\ub9ac\ud574\uc11c \ub098\uc5d0\uac8c \ubcf4\ub0b4\uc8fc\uc5c8\ub2e4. \\n\ub355\ubd84\uc5d0 \ub098\ub3c4 \uac00\ube44\uc758 \uc0dd\uac01\uc744 \uc54c \uc218 \uc788\uc5b4\uc11c \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\ub294\ub370 \uac00\uc18d\ub3c4\uac00 \ubd99\uc740 \uac83 \uac19\ub2e4. \\n\ub610\ud55c \ubbf8\uc158\uc744 \uc798 \ub9c8\ubb34\ub9ac\ud558\uace0 \uc2f6\uc740 \ub9c8\uc74c\uc774 \uc804\ub2ec\ub418\uc11c \uadf8\ub7f0\uc9c0 \ub098\ub3c4 \ub369\ub2ec\uc544 \uc5f4\uc2ec\ud788 \ubbf8\uc158\uc744 \ud560 \uc218 \uc788\uc5c8\ub2e4.\ud83d\ude04 \\n\\n**\uc194\uc9c1\ud568** \\n\uba3c\uc800 \ud68c\uace0\ud558\uc790\uace0 \ub9d0 \uac78\uc5b4\uc918\uc11c \uc815\ub9d0 \uace0\ub9c8\uc6e0\ub2e4\uace0 \ud45c\ud604\ud574\uc8fc\ub294 \ubd80\ubd84 \\n\ubaa8\ub974\ub294\uac8c \uc788\uc73c\uba74 \uc194\uc9c1\ud558\uac8c \ub9d0\ud574\uc8fc\ub294 \ubd80\ubd84 \\n\ub098\uc758 \uc758\uacac\uc744 \uc815\ub9ac\ud558\uc9c0 \ubabb\ud55c \uc0c1\ud0dc\ub85c \uc804\ub2ec\ud560 \ub54c \uc774\ud574\uac00 \uc548\ub418\uc5c8\ub2e4\uace0 \uc815\ud655\ud788 \uc804\ub2ec\ud574\uc8fc\ub294 \ubd80\ubd84 \\n\uc194\uc9c1\ud568\uc740 \ud398\uc5b4\ud560 \ub54c \uc911\uc694\ud55c \ubd80\ubd84\uc778 \uac83 \uac19\ub2e4. \\n\\n\ub9c8\uc9c0\ub9c9\uc73c\ub85c \ucc30\ub9ac\ud83c\udf6b \uccb4\uc2a4 \ubbf8\uc158\ub54c \uaf3c\uaf3c\ud558\uac8c \ub9ac\ubdf0 \ub0a8\uaca8\uc8fc\uc154\uc11c \uac10\uc0ac\ud569\ub2c8\ub2e4!"},{"id":"grasp","metadata":{"permalink":"/grasp","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-03-30-GRASP.mdx","source":"@site/blog/2023-1/2023-03-30-GRASP.mdx","title":"\uc77c\ubc18\uc801\uc778 \ucc45\uc784 \ud560\ub2f9\uc744 \uc704\ud55c \ud328\ud134","description":"GRASP(General Responsibility Assignment Software Pattern)","date":"2023-03-30T00:00:00.000Z","formattedDate":"2023\ub144 3\uc6d4 30\uc77c","tags":[{"label":"GRASP","permalink":"/tags/grasp"},{"label":"OOP","permalink":"/tags/oop"}],"readingTime":8.085,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc77c\ubc18\uc801\uc778 \ucc45\uc784 \ud560\ub2f9\uc744 \uc704\ud55c \ud328\ud134","slug":"grasp","tags":["GRASP","OOP"]},"prevItem":{"title":"\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0","permalink":"/chess-retrospective"},"nextItem":{"title":"\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0","permalink":"/blackjack-retrospective"}},"content":"### GRASP(General Responsibility Assignment Software Pattern)\\n\\n\ud06c\ub808\uc774\uadf8 \ub77c\ub9cc\uc758 Applying UML and Patterns\uc774\ub77c\ub294 \ucc45\uc5d0\uc11c \ub098\uc628 \ucc45\uc784 \ud560\ub2f9\uc744 \uc704\ud55c \ud328\ud134\\n\\n\uac01 \ud328\ud134\ub9c8\ub2e4 Solution\uacfc Problem\ub85c \uad6c\uc131\ub418\uc5b4 \uc788\ub2e4.\\n\\n### \uc815\ubcf4 \uc804\ubb38\uac00 \ud328\ud134(Information Expert)\\n\\nQ: \uac1d\uccb4\uc5d0 \ucc45\uc784\uc744 \ud560\ub2f9\ud558\ub294 \uae30\ubcf8 \uc6d0\uce59\uc740 \ubb34\uc5c7\uc778\uac00?\\n\\nA: \ucc45\uc784\uc744 \uc218\ud589\ud558\ub294 \ub370 \ud544\uc694\ud55c \uc815\ubcf4\ub97c \uac00\uc9c4 \ud074\ub798\uc2a4(\uc815\ubcf4 \uc804\ubb38\uac00)\uc5d0\uac8c \ucc45\uc784\uc744 \ud560\ub2f9\ud55c\ub2e4.\\n\\n\uc815\ubcf4\uc640 \ud589\ub3d9\uc744 \uac00\uae4c\uc6b4 \uacf3\uc5d0 \uc704\uce58\uc2dc\ud0a4\uae30 \ub54c\ubb38\uc5d0 \ucea1\uc290\ud654\ub97c \uc720\uc9c0\ud560 \uc218 \uc788\ub2e4.\\n\\n\ud544\uc694\ud55c \uc815\ubcf4\ub97c \uac00\uc9c4 \uac1d\uccb4\ub4e4\ub85c \ucc45\uc784\uc774 \ubd84\uc0b0\ub41c\ub2e4.\\n\\n### \ucc3d\uc870\uc790 \ud328\ud134(Creator)\\n\\nQ: \ub204\uac00 \uac1d\uccb4 A\ub97c \uc0dd\uc131\ud558\ub294\uac00?\\n\\nA: \ub2e4\uc74c\uc758 \uc870\uac74\uc744 \ucd5c\ub300\ud55c \ub9ce\uc774 \ub9cc\uc871\ud558\ub294 \uac1d\uccb4\uc5d0\uac8c \uac1d\uccb4 \uc0dd\uc131 \ucc45\uc784\uc744 \ud560\ub2f9\ud574\uc57c \ud55c\ub2e4.\\n\\n- B\uac00 A \uac1d\uccb4\ub97c \ud3ec\ud568 \ub610\ub294 \ucc38\uc870\ud55c\ub2e4.\\n- B\uac00 A \uac1d\uccb4\ub97c \uae30\ub85d\ud55c\ub2e4.\\n- B\uac00 A \uac1d\uccb4\ub97c \uae34\ubc00\ud558\uac8c \uc0ac\uc6a9\ud55c\ub2e4.\\n- B\uac00 A \uac1d\uccb4\uc758 \ucd08\uae30\uac12\uc744 \uac00\uc9c0\uace0 \uc788\ub2e4.\\n\\n\uc0dd\uc131 \uc608\uc815\uc778 \uac1d\uccb4\uc640 \uc5f0\uad00\ub418\uc5b4 \uc788\ub294 \uac1d\uccb4\uac00 \uc0dd\uc131 \ucc45\uc784\uc744 \uac00\uc9c0\uace0 \uc788\uac8c \ub41c\ub2e4\uba74, \uc774\ubbf8 \ud574\ub2f9 \uac1d\uccb4\uc640 \uacb0\ud569\ub418\uc5b4\uc788\ub2e4\uace0 \uc0dd\uac01\ud560 \uc218 \uc788\ub2e4. \ub530\ub77c\uc11c \uc804\uccb4\uc801\uc778 \uacb0\ud569\ub3c4\ub97c \ub0ae\uac8c \uc720\uc9c0\ud560 \uc218 \uc788\ub2e4.\\n\\n### \ub0ae\uc740 \uacb0\ud569\ub3c4 \ud328\ud134(Low Coupling)\\n\\nQ: \uc758\uc874\uc131\uc744 \ub0ae\ucd94\uace0 \ubcc0\ud654\uc758 \uc601\ud5a5\uc744 \uc904\uc774\uba70 \uc7ac\uc0ac\uc6a9\uc131\uc744 \uc99d\uac00\uc2dc\ud0a4\ub294 \ubc29\ubc95\uc740?\\n\\nA: \uc804\uccb4\uc801\uc778 \uacb0\ud569\uc774 \ub0ae\uac8c \uc720\uc9c0\ub418\ub3c4\ub85d \ucc45\uc784\uc744 \ud560\ub2f9\ud574\uc57c \ud55c\ub2e4.\\n\\n> \uacb0\ud569\ub3c4(Coupling)\\n\uac1d\uccb4 \uc0ac\uc774\uc758 \uc758\uc874\uc131\uc774 \uacfc\ud55c \uacbd\uc6b0 \uacb0\ud569\ub3c4\uac00 \ub192\ub2e4\uace0 \ub9d0\ud55c\ub2e4.\\n- \uc624\ube0c\uc81d\ud2b8 p.17\\n> \\n\\n\uacb0\ud569\ub3c4\ub97c \ub0ae\ucd98\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uc774\uc810\uc774 \uc788\ub2e4.\\n\\n- \ub2e4\ub978 \uad6c\uc131 \uc694\uc18c\uc758 \ubcc0\ud654\uc5d0 \uc601\ud5a5\uc744 \ubc1b\uc9c0 \uc54a\ub294\ub2e4.\\n- \uc7ac\uc0ac\uc6a9\uc774 \ud3b8\ub9ac\ud574\uc9c4\ub2e4.\\n- \ud574\ub2f9 \ud074\ub798\uc2a4\uc5d0 \ub300\ud55c \uc774\ud574\uac00 \uc26c\uc6cc\uc9c4\ub2e4. (\uc758\uc874\ud558\ub294 \ud074\ub798\uc2a4\uac00 \uc801\uae30 \ub54c\ubb38\uc5d0)\\n\\n### \ub192\uc740 \uc751\uc9d1\ub3c4 \ud328\ud134(High Cohesion)\\n\\nQ. \uac1d\uccb4\ub97c \uad00\ub9ac\ud558\uae30 \uc27d\uac8c \ud558\ub824\uba74 \uc5b4\ub5bb\uac8c \ud574\uc57c \ud560\uae4c?\\n\\nA. \ub192\uc740 \uc751\uc9d1\ub3c4\ub97c \uc720\uc9c0\ud560 \uc218 \uc788\uac8c \ucc45\uc784\uc744 \ud560\ub2f9\ud574\uc57c \ud55c\ub2e4.\\n\\n> \uc751\uc9d1\ub3c4(Cohesion)\\n\uc5f0\uad00\ub41c \uc791\uc5c5\ub9cc\uc744 \uc218\ud589\ud558\uace0 \uc5f0\uad00\uc131 \uc5c6\ub294 \uc791\uc5c5\uc740 \ub2e4\ub978 \uac1d\uccb4\uc5d0\uac8c \uc704\uc784\ud558\ub294 \uac1d\uccb4\ub97c \uac00\ub9ac\ucf1c \uc751\uc9d1\ub3c4\uac00 \ub192\ub2e4\uace0 \ub9d0\ud55c\ub2e4.\\n- \uc624\ube0c\uc81d\ud2b8 p.26\\n> \\n\\n\ubcc0\uacbd\uc758 \uc774\uc720\uc5d0 \ub530\ub77c \ud074\ub798\uc2a4\ub97c \ubd84\ub9ac\ud55c\ub2e4\uba74 \uc751\uc9d1\ub3c4\ub97c \ub192\uc77c \uc218 \uc788\uace0, \uc751\uc9d1\ub3c4\uac00 \ub192\uc544\uc9c4\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uc774\uc810\uc774 \uc788\ub2e4.\\n\\n- \ud574\ub2f9 \ud074\ub798\uc2a4\uc5d0 \ub300\ud55c \uc774\ud574\uac00 \uc26c\uc6cc\uc9c4\ub2e4. (\ud560\ub2f9\ub41c \ucc45\uc784\ub9cc\uc744 \uc218\ud589\ud558\uace0 \uc788\uae30 \ub54c\ubb38\uc5d0)\\n- \uc720\uc9c0\ubcf4\uc218\uac00 \uc26c\uc6cc\uc9c4\ub2e4.\\n- \ub0ae\uc740 \uacb0\ud569\ub3c4 \ub610\ud55c \uc9c0\uc6d0\ud55c\ub2e4.\\n- \uc751\uc9d1\ub3c4\uac00 \ub192\uc740 \ud074\ub798\uc2a4\ub294 \ud2b9\uc815\ud55c \ubaa9\uc801\uc5d0 \uc0ac\uc6a9\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \uc7ac\uc0ac\uc6a9\ud558\uae30 \uc88b\ub2e4.\\n\\n### \ucee8\ud2b8\ub864\ub7ec \ud328\ud134(Controller)\\n\\nQ. \uc0ac\uc6a9\uc790\uc758 \uc694\uccad\uc744 \ucc98\ub9ac\ud558\ub294 \uac83\uc740 \ub204\uac00 \ub2f4\ub2f9\ud574\uc57c \ud558\ub294\uac00?\\n\\nA. \uc0ac\uc6a9\uc790\uc758 \uc694\uccad\uc744 \ucc98\ub9ac\ud558\ub294 Controller \uac1d\uccb4\ub97c \ub9cc\ub4e4\uc5b4\uc11c \uc0ac\uc6a9\ud574\uc57c \ud55c\ub2e4.\\n\\n\uc5b4\ub5a4 \uc11c\ube0c\uc2dc\uc2a4\ud15c\uc774 \uc874\uc7ac\ud55c\ub2e4\uace0 \uac00\uc815\ud560 \ub54c\\n\\n- \uc9c1\uc811\uc801\uc73c\ub85c \uac1d\uccb4\uc5d0 \uc811\uadfc\ud558\uc5ec \ud504\ub85c\uadf8\ub7a8\uc744 \uc0ac\uc6a9\ud55c\ub2e4\uba74 \uacb0\ud569\ub3c4\uac00 \uc0c1\uc2b9\ud55c\ub2e4.\\n- \uc11c\ube0c \uc2dc\uc2a4\ud15c\uc5d0 \ub4e4\uc5b4\uc624\ub294 \uc694\uccad\uc744 \ucc98\ub9ac\ud574\uc8fc\ub294 \ucee8\ud2b8\ub864\ub7ec\uac00 \uc788\ub2e4\uba74 \uc0ac\uc6a9\ud558\ub294 \uc785\uc7a5\uc5d0\uc11c\ub294 \ud574\ub2f9 \ucee8\ud2b8\ub864\ub7ec\ub9cc \uc54c\uba74 \ub41c\ub2e4.\\n- \ub9cc\uc57d \uc11c\ube0c \uc2dc\uc2a4\ud15c\uc758 \ubcc0\uacbd\uc774 \uc0dd\uacbc\uc744 \ub54c \uc678\ubd80\uc5d0 \ubbf8\uce58\ub294 \uc601\ud5a5\ub3c4 \uc904\uc5b4\ub4e0\ub2e4.\\n\\n### \ub2e4\ud615\uc131 \ud328\ud134(Polymorphism)\\n\\nQ. \uac1d\uccb4\uc758 \ud0c0\uc785\uc5d0 \ub530\ub77c \ud589\ub3d9\uc774 \ubc14\ub010\ub2e4\uba74 \ucc45\uc784\uc744 \uc5b4\ub5bb\uac8c \ud560\ub2f9\ud574\uc57c \ud560\uae4c?\\n\\nA. OOP\uac00 \uc9c0\uc6d0\ud558\ub294 \ub2e4\ud615\uc131\uc744 \uc801\uadf9\uc801\uc73c\ub85c \ud65c\uc6a9\ud55c\ub2e4. (\uc778\ud130\ud398\uc774\uc2a4\ub97c \ub450\uace0 \ud589\ub3d9\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \uad6c\ud604)\\n\\n\uac1d\uccb4\uc758 \uc885\ub958\uc5d0 \ub530\ub77c \ubd84\uae30\ud558\ub294 \uc870\uac74\ubb38\uc774 \uc544\ub2cc \ub2e4\ud615\uc131\uc744 \uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\uc740 \ubc29\ubc95\uc774\ub2e4.\\n\\n\uc0c8\ub85c\uc6b4 \ud0c0\uc785\uc774 \ucd94\uac00\ub418\uc5c8\uc744 \ub54c \uc870\uac74\ubb38\uc744 \uc0ac\uc6a9\ud55c\ub2e4\uba74 \uae30\uc874\uc758 \uc870\uac74\ubb38\uc744 \uc218\uc815\ud574\uc57c \ud558\uc9c0\ub9cc \ub2e4\ud615\uc131\uc744 \ud65c\uc6a9\ud558\uba74 \uc27d\uac8c \ud655\uc7a5\ud560 \uc218 \uc788\ub2e4.\\n\\n### \ubcc0\uacbd \ubcf4\ud638 \ud328\ud134(Protected Variations)\\n\\nQ. \uc5b4\ub5bb\uac8c \ud558\uba74 \ubcc0\uacbd\uc774 \ub2e4\ub978 \uc694\uc18c\uc5d0 \uc601\ud5a5\uc744 \ubbf8\uce58\uc9c0 \uc54a\ub3c4\ub85d \ubc29\uc9c0\ud560 \uc218 \uc788\uc744\uae4c?\\n\\nA. \ubcc0\ud654\uac00 \uc608\uc0c1\ub418\ub294 \uc9c0\uc810\uc744 \uc2dd\ubcc4\ud558\uace0, \uc8fc\uc704\uc5d0 \uc548\uc815\ub41c \uc778\ud130\ud398\uc774\uc2a4\ub97c \ud615\uc131\ud558\ub3c4\ub85d \ucc45\uc784\uc744 \ud560\ub2f9\ud574\uc57c \ud55c\ub2e4.\\n\\n### \uac04\uc811 \ucc38\uc870 \ud328\ud134(Indirection)\\n\\nQ. \ub450 \uac1d\uccb4 \uc0ac\uc774\uc758 \uc9c1\uc811\uc801\uc778 \uc5f0\uacb0\uc744 \ud53c\ud558\uace0 \uc2f6\ub2e4\uba74 \uc5b4\ub5bb\uac8c \ud574\uc57c \ud560\uae4c?\\n\\nA. \ub450 \uac1d\uccb4 \uc0ac\uc774\uc5d0 \ub610 \ub2e4\ub978 \uac1d\uccb4\ub97c \ub450\uc5b4 \uc9c1\uc811\uc801\uc778 \uc5f0\uacb0\uc744 \ud53c\ud560 \uc218 \uc788\ub2e4.\\n\\n\uc911\uc7ac\uc790 \ud328\ud134\uc744 \uc0ac\uc6a9\ud558\uc5ec \ub450 \uac1d\uccb4 \uc0ac\uc774\uc5d0 \ub610 \ud558\ub098\uc758 \uac1d\uccb4\ub97c \ucd94\uac00\ud558\uc5ec \ubcf5\uc7a1\ud55c \uad00\uacc4\ub97c \ub2e8\uc21c\ud654\ud560 \uc218 \uc788\ub2e4.\\n\\n\uc911\uac04\uc5d0 \uc778\ud130\ud398\uc774\uc2a4\ub97c \ub454\ub2e4\uba74 \ubcc0\uacbd \ubcf4\ud638 \ud328\ud134(Protected Variations)\uc5d0 \ud574\ub2f9\ub41c\ub2e4.\\n\\n### \uc21c\uc218\ud55c \uac00\uacf5\ubb3c \ud328\ud134(Pure Fabrication)\\n\\nQ. \ucc45\uc784\uc744 \ud560\ub2f9\ud55c \ub3c4\uba54\uc778 \uac1d\uccb4\uac00 Low Coupling, High Cohesion, \uc7ac\uc0ac\uc6a9\uc131 \ub4f1\uc758 \ubaa9\uc801\uc744 \uc704\ubc18\ud55c\ub2e4\uba74 \uc5b4\ub5bb\uac8c \ud574\uc57c \ud560\uae4c?\\n\\nA. \ub3c4\uba54\uc778 \uac1c\ub150\uc744 \ud3ec\ud568\ud558\uc9c0 \uc54a\ub294 \ud074\ub798\uc2a4\ub97c \ud558\ub098 \ub9cc\ub4e4\uace0 \ub9e4\uc6b0 \uc751\uc9d1\ub41c \ucc45\uc784\uc744 \ud560\ub2f9\ud560 \uc218 \uc788\ub2e4.\\n\\n\ud589\ub3d9\uc744 \ucd94\uac00\ud560 \ub54c, \ud574\ub2f9 \ucc45\uc784\uc744 \uc218\ud589\ud560 \ub3c4\uba54\uc778 \uac1c\ub150\uc774 \uc874\uc7ac\ud558\uc9c0 \uc54a\ub294\ub2e4\uba74 \ub3c4\uba54\uc778\uacfc \ubb34\uad00\ud55c \uc778\uacf5\uc801\uc778 \uac1d\uccb4\ub97c \ub9cc\ub4e0\ub2e4\uc74c \ud574\ub2f9 \uac1d\uccb4\uc5d0\uac8c \ucc45\uc784\uc744 \ud560\ub2f9\ud55c\ub2e4.\\n\\n\uac1d\uccb4\uac00 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud574\uc57c \ud560 \uac12\uc744 \uac00\uc9c0\uace0 \uc788\ub2e4\uace0, \uc815\ubcf4 \uc804\ubb38\uac00 \ud328\ud134\uc744 \uc801\uc6a9\ud558\uc5ec \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud558\ub77c\ub294 \ucc45\uc784\uc744 \uac00\uc9c0\ub77c\uace0 \ud558\uc9c0 \uc54a\ub294\ub2e4.\\n\\n\uc608) \uc0c1\uc810\uacfc \uace0\uac1d \ud074\ub798\uc2a4\uac00 \uc788\uace0 \uc11c\ub85c \ub2e4\ub978 \ud1b5\ud654\ub97c \uc0ac\uc6a9\ud558\uace0 \uc788\ub2e4\uace0 \uac00\uc815\\n\\n- \uc11c\ub85c \ub2e4\ub978 \ud1b5\ud654\ub97c \uc0ac\uc6a9\ud558\uace0 \uc788\uae30 \ub54c\ubb38\uc5d0 \uac70\ub798\ub97c \ud558\ub824\uba74 \ud658\uc804\uc744 \ud574\uc57c\ud55c\ub2e4.\\n- \ub450 \ud074\ub798\uc2a4 \ub2e4 \ud658\uc804\uc5d0 \ub300\ud55c \ucc45\uc784\uc744 \ubd80\uc5ec\ud558\uae30 \uc560\ub9e4\ud558\ub2e4\uba74 \ud658\uc804\uc744 \ucc45\uc784\ud558\ub294 \ud074\ub798\uc2a4\ub97c \ucd94\uac00\ud558\uace0 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n\uc624\ube0c\uc81d\ud2b8 5\uc7a5. \ucc45\uc784 \ud560\ub2f9\ud558\uae30, [\uc870\uc601\ud638](http://aeternum.egloos.com/)\\n\\nApplying UML and Patterns Chapter 16, Chapter 21 GRASP, Craig Larman\\n\\n[GRASP, \ud55c\ube5b \ub124\ud2b8\uc6cc\ud06c](https://www.hanbit.co.kr/network/category/category_view.html?cms_code=CMS8586826397)"},{"id":"blackjack-retrospective","metadata":{"permalink":"/blackjack-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-03-14-\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-1/2023-03-14-\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0.mdx","title":"\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0","description":"\ube14\ub799\uc7ad","date":"2023-03-14T00:00:00.000Z","formattedDate":"2023\ub144 3\uc6d4 14\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":5.105,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0","slug":"blackjack-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc77c\ubc18\uc801\uc778 \ucc45\uc784 \ud560\ub2f9\uc744 \uc704\ud55c \ud328\ud134","permalink":"/grasp"},"nextItem":{"title":"\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0","permalink":"/ladder-retrospective"}},"content":"### \ube14\ub799\uc7ad\\n\\n\ube14\ub799\uc7ad \ubbf8\uc158\uc5d0\uc11c\ub294 \ud6c4\ucd94\uc640 \ud398\uc5b4(\uc870\ubbf8\ub8cc \ub4c0\uc624?)\uac00 \ub9e4\uce6d\ub418\uc5c8\ub2e4. \\n\uc774\ubc88\uc5d0\ub294 \uc2e4\uc218\ud558\uc9c0 \uc54a\uace0, \ubc14\ub85c \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uc9c0 \uc54a\uace0 \uce5c\ud574\uc9c0\uae30 \ubd80\ud130 \uc2dc\uc791\ud588\ub2e4. \\n\\n\ube14\ub799\uc7ad\uc740 \uad6c\ud604\ud574\uc57c \ub420 \ub0b4\uc6a9\uc774 \ub9ce\uc544 \uc2dc\uac04\uc774 \ub9ce\uc774 \ubd80\uc871\ud560 \uac83 \uac19\uc558\uc9c0\ub9cc \\n\ud6c4\ucd94\uc640 \ud568\uaed8 \uc804\ub7b5\uc801(\uc0bc\uc77c\uc808\uc5d0 \ubbf8\uc158 \uc774\uc57c\uae30 \ub098\ub204\uae30)\uc73c\ub85c \ubbf8\uc158\uc744 \uc9c4\ud589\ud574 \uc2dc\uac04 \ub0b4\uc5d0 \uc81c\ucd9c\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n\ubbf8\uc158\uc744 \ub05d\ub098\uace0 \ud68c\uace0\ub97c \ud588\uc744 \ub54c \ud6c4\ucd94\uac00 \uace0\ubbfc\uac70\ub9ac\ub97c \ud558\ub098 \ub0b4\uc92c\ub2e4. \\n\\"\ud398\uc5b4\ub97c \uc9c4\ud589\ud560 \ub54c \uc555\ubc15\uac10\uc744 \ub290\ub07c\ub294 \ud398\uc5b4\uac00 \uc788\ub2e4\uba74 \ud5c8\ube0c\uac00 \ud574\uc904 \uc218 \uc788\ub294\uac8c \ubb50\uac00 \uc788\uc744\uae4c?\\" \\n\\n\uacf0\uacf0\ud788 \uc0dd\uac01\ud574\ubd24\uc9c0\ub9cc \uc27d\uac8c \ub2f5\uc744 \ub0b4\ub9b4 \uc218 \uc5c6\uc5c8\ub2e4. \\n\uc911\uac04 \uc911\uac04 \ud68c\uace0\ub97c \ud558\uace0, \ub098\uc758 \uc18c\ud504\ud2b8\uc2a4\ud0ac\uc744 \ub192\ud788\ub294\uac8c \ub2f5\uc77c\uae4c? \\n\ubd80\ub2f4\uac10\uc744 \ub290\ub07c\uc9c0 \uc54a\uace0 \uac19\uc774 \uc77c\ud558\uace0 \uc2f6\uc740 \uc0ac\ub78c\uc774 \ub420 \uc218 \uc788\ub3c4\ub85d \uacc4\uc18d \uc0dd\uac01\ud574\ubd10\uc57c\uaca0\ub2e4. \\n\\n\uc774 \ubd80\ubd84\uc5d0 \ub300\ud574 \uc0dd\uac01\uc774 \ub9ce\uc544\uc838\uc11c \uc804 \ub9ac\ubdf0\uc5b4\uc778 \ud130\ud2c0\ud83d\udc22\uacfc\ub3c4 \ub300\ud654\ub97c \ub098\ub204\uc5c8\ub2e4. \\n\ud130\ud2c0\uc740 \uc81c\uc5b4\ud560 \uc218 \uc5c6\ub294 \ubd80\ubd84\ubcf4\ub2e4 \uc81c\uc5b4\ud560 \uc218 \uc788\ub294 \ubd80\ubd84(\uad81\uadf9\uc801\uc778 \ubaa9\ud45c\uc778 \uc88b\uc740 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\ub294 \uac83)\uc5d0 \uc9d1\uc911\ud574\ubcf4\ub77c\uace0 \ud558\uc168\ub2e4. \\n\\n\uc88b\uc740 \ucf54\ub4dc, \uc88b\uc740 \ud398\uc5b4\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \uc77c\ub2e8 \uc9c0\uc18d\uc801\uc73c\ub85c \uc0dd\uac01\ud574\ubd10\uc57c\uaca0\ub2e4.\\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n**\ud398\uc5b4 \uc2e0\uacbd\uc4f0\uae30** \\n\uc774\ubc88 \ud398\uc5b4\ud560 \ub54c \uc801\uadf9\uc801\uc73c\ub85c \uc758\uacac\uc744 \ub0b4\ubcf4\ub3c4\ub85d \ud588\ub2e4. \uadf8\ub807\uae30\uc5d0 \ub108\ubb34 \uc758\uacac\uc744 \uac15\ud558\uac8c \ubc00\uc5b4\ubd99\uc778 \ub290\ub08c\uc774 \ub4e4\uc5b4\uc11c \ubbf8\uc548\ud588\ub2e4. \\n\ud6c4\ucd94\uac00 \uc555\ubc15\uc744 \ub290\uaf08\uc744 \uc218\ub3c4 \uc788\uc744 \uac83 \uac19\ub2e4\ub294 \uc0dd\uac01\uc774 \ub4e0\ub2e4. \\n\uc911\uac04 \uc911\uac04 \uc791\uc740 \ud68c\uace0\ub97c \uc9c4\ud589\ud574\ubcf4\ub294 \uac83\uc774 \uc88b\uc744\uae4c?\\n\\n**\uccb4\ub825 \uad00\ub9ac** \\n\uc694\uc998 \uc798 \ubabb\uba39\ub294 \uac83 \uac19\ub2e4. \\n\uc55e\uc73c\ub85c \uc0b4 \ub0a0\uc774 \ub9ce\uc740\ub370 \uc798 \ucc59\uaca8\uba39\uace0, \ud798\ub0b4\uc57c\uaca0\ub2e4.\\n\\n**\uc911\uac04 \uc911\uac04 \ub3cc\uc544\ubcf4\uae30** \\n\uc774\ubc88 \ubbf8\uc158\uacfc \uad00\ub828\ub41c \ub0b4\uc6a9\uc740 \uc544\ub2c8\uc9c0\ub9cc \uc6b0\ud14c\ucf54\ub97c \uc798 \ud65c\uc6a9 \ud558\uace0 \uc788\ub294\uc9c0 \uc0dd\uac01\uc744 \ud574\ubd10\uc57c\uaca0\ub2e4. \\n\ub0b4\uac00 \uc6b0\ud14c\ucf54\uc5d0 \uc9c0\uc6d0\ud55c \uc774\uc720\ub97c \ud56d\uc0c1 \uc78a\uc9c0 \uc54a\uc544\uc57c\uaca0\ub2e4. \\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**\uc0c1\ud0dc \ud328\ud134** \\n\uac1d\uccb4\uc758 \ub0b4\ubd80 \uc0c1\ud0dc\uc5d0 \ub530\ub77c \uc2a4\uc2a4\ub85c \ud589\ub3d9\uc744 \ubcc0\uacbd\ud558\ub3c4\ub85d \ud558\ub294 \ud328\ud134\uc73c\ub85c if/else/switch\uc640 \uac19\uc740 \uc870\uac74\ubb38\uc744 \ud6a8\uacfc\uc801\uc73c\ub85c \uc81c\uac70\ud560 \uc218 \uc788\ub2e4. \\n\ube14\ub799\uc7ad \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc0c1\ud0dc \ud328\ud134\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \ucc98\uc74c \uc801\uc6a9\ud574\ubcf4\uc558\ub2e4. \\n\ucc98\uc74c \uc801\uc6a9\ud558\uae30 \uc804\uc5d0\ub294 \ubcc4\ub85c\ub77c\uace0 \uc0dd\uac01\ud588\ub294\ub370, \uc0dd\uac01\ubcf4\ub2e4 \uad1c\ucc2e\uc740 \uac83 \uac19\ub2e4. \\n\\n**\uc77c\uad00\uc131, \uac00\ub3c5\uc131, \ucd94\uc0c1\ud654** \\n\uc774\ubc88 \ub9ac\ubdf0\uc5b4\ub294 \uac80\ud504\ud83c\udf6b \uc600\ub2e4! \\n\uac80\ud504\uc758 \ub9ac\ubdf0\ub294 \uac04\uacb0\ud568\uc5d0 \uad00\ub828\ub41c \ub0b4\uc6a9\uc774 \ub9ce\uc558\ub2e4. \\n\uc77c\uad00\uc131\uc774 \uc788\ub294 \ucf54\ub4dc, \uac00\ub3c5\uc131\uc774 \uc88b\uc740 \ucf54\ub4dc, \ucd94\uc0c1\ud654\uac00 \uc798 \ub418\uc5b4\uc788\ub294 \ucf54\ub4dc \\n\uc77d\uae30 \uc88b\uace0, \uac04\uacb0\ud55c \ubc29\ud5a5\uc73c\ub85c \ucf54\ub4dc\ub97c \uc791\uc131\ud558\ub294 \ubc29\ubc95\uc744 \ubc30\uc6b4 \uac83 \uac19\ub2e4. \\n\ucf54\ub4dc\ub97c \ubc14\ub77c\ubcf4\ub294 \uc2dc\uc810\uc774 \ud558\ub098 \ub298\uc5b4\ub09c \uae30\ubd84\uc774\ub2e4!(\uc55e\uc73c\ub85c \uc801\uc6a9\ud558\ub294 \uac83\uc740 \ub098\uc758 \ubaab\uc774\uc9c0\ub9cc) \\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n**\uc0dd\uac01 \uc815\ub9ac** \\n\uc911\uac04 \uc911\uac04 \ud604\uc7ac \uc0c1\ud669\uc5d0 \ub300\ud574 \uadf8\ub9bc\uc744 \uadf8\ub9ac\uac70\ub098, \uae00\uc744 \uc801\uc73c\uba74\uc11c \uc815\ub9ac\ud55c\ub2e4. \\n\ud398\uc5b4\uc640 \ub3d9\uc77c\ud55c \ubd80\ubd84\uc744 \uc774\ud574\ud558\uace0 \uc788\ub294\uc9c0 \ud655\uc778\ud55c\ub2e4. \\n\uc9c4\ud589\ud558\ub294\ub370 \ub9e4\uc6b0 \ub3c4\uc6c0\uc774 \ub418\uc5c8\ub358 \uac83 \uac19\ub2e4. \\n\ub098\ub3c4 \ub2e4\uc74c \ud398\uc5b4\ub54c\ubd80\ud130 \ud39c\uc774\ub791 \uc885\uc774\ub97c \uc900\ube44\ud574\uc57c\uaca0\ub2e4.\\n\\n**\uac00\uac10\uc5c6\uc774 \uc758\uacac\uc744 \ub9d0\ud574\uc8fc\ub294 \ubd80\ubd84** \\n\uc9c4\ud589 \uc0c1\ud669\uc5d0 \ub300\ud55c \ubd80\ubd84, \uc9c4\ud589 \uc18d\ub3c4, \uc9c0\uae08 \uc790\uc2e0\uc774 \uc774\ud574\ud558\uace0 \uc788\ub294 \ubd80\ubd84\uc744 \ub9d0\ud574\uc918\uc11c \ud3b8\ud588\ub2e4. \\n\ud68c\uace0\ub54c\ub3c4 \uc11c\ub85c \uc194\uc9c1\ud558\uac8c \uc758\uacac\uc744 \uc8fc\uace0 \ubc1b\uc544\uc11c \uc88b\uc558\ub2e4. \\n\\n**\ub3c4\uba54\uc778 \uc5b8\uc5b4\uc5d0 \uc2e0\uacbd\uc4f0\ub294 \ubd80\ubd84** \\n\ud074\ub798\uc2a4\uba85, \ubcc0\uc218\uba85\uacfc \uac19\uc740 \uc5b8\uc5b4\ub97c \uc138\uc2ec\ud558\uac8c \uc2e0\uacbd\uc4f4\ub2e4. \\n\uc694\uad6c\uc0ac\ud56d \uc815\ub9ac\ub3c4 \uae54\ub054\ud558\uac8c \uc798\ud558\ub294 \uac83 \uac19\ub2e4. \\n\\n\ud6c4\ucd94 \ucd5c\uace0 \ud83d\udc4d"},{"id":"ladder-retrospective","metadata":{"permalink":"/ladder-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-02-26-\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-1/2023-02-26-\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0","description":"\uc0ac\ub2e4\ub9ac \ud0c0\uae30","date":"2023-02-26T00:00:00.000Z","formattedDate":"2023\ub144 2\uc6d4 26\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":10.22,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0","slug":"ladder-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0","permalink":"/blackjack-retrospective"},"nextItem":{"title":"\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0","permalink":"/racing-car-retrospective"}},"content":"### \uc0ac\ub2e4\ub9ac \ud0c0\uae30\\n\\n\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc6b0\uac00\uc640 \ud398\uc5b4\uac00 \ub9e4\uce6d\ub418\uc5c8\ub2e4. \\n\uc774\uc804 \ubbf8\uc158\uacfc \ub2ec\ub9ac TDD\ub85c \uc9c4\ud589\ud558\ub294 \uac83\uc774 \ud544\uc218\uc600\uae30 \ub54c\ubb38\uc5d0 \uc775\uc219\ud558\uc9c0 \uc54a\uc558\uc9c0\ub9cc, \uc6b0\uac00\uc640 \ubbf8\uc158\uc5d0 \uad00\ud55c \uc18c\ud1b5\uc774 \uc798 \ub418\uc5b4\uc11c \ud070 \ubb38\uc81c \uc5c6\uc774 \ubbf8\uc158\uc744 \ub9c8\ubb34\ub9ac\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n\uc6b0\uac00\uc640 \uc774\uc57c\uae30\uac00 \uc798 \ud1b5\ud574\uc11c \uadf8\ub7f0\uc9c0 1\ub2e8\uacc4\ub294 \ud06c\uac8c \uc5b4\ub835\uc9c0 \uc54a\uac8c \uc9c4\ud589\ud560 \uc218 \uc788\uc5c8\ub294\ub370, 2\ub2e8\uacc4\uc5d0\uc11c \ub9ce\uc774 \uace0\uc804\ud55c \uac83 \uac19\ub2e4.\\n\\n2\ub2e8\uacc4\uc5d0\uc11c\ub294 2\uac00\uc9c0 \ubc29\ubc95\uc73c\ub85c \uad6c\ud604\ud574\ubd24\ub2e4.\\n\\n1. LadderGame\uc5d0\uc11c Position \uae30\uc900\uc73c\ub85c \uc0ac\ub2e4\ub9ac \uac8c\uc784\uc744 \uc9c4\ud589\ud558\ub294 \ubc29\ubc95\\n2. Player\uc5d0\uac8c Ladder\ub97c \ub118\uaca8\uc11c Ladder\uc5d0\uac8c Position\uc744 \ub118\uaca8\uc8fc\uba70 \uba54\uc2dc\uc9c0\ub97c \ubcf4\ub0b4\ub294 \ubc29\ubc95\\n\\n### Position \uae30\uc900\uc73c\ub85c \uc0ac\ub2e4\ub9ac \uac8c\uc784\uc744 \uc9c4\ud589\ud558\ub294 \ubc29\ubc95\\n\\n\uc0ac\uc2e4\uc0c1 index\ub97c Ladder\uc5d0\uac8c \ub118\uaca8\uc8fc\uace0, \ud574\ub2f9 index\uc5d0 \ub300\ud55c \uacb0\uacfc\ub97c \ubc1b\ub294 \ubc29\ubc95\uacfc \uc720\uc0ac\ud588\ub2e4. \\n\uad6c\ud604\ud558\uace0 \ub098\ub2c8 \ub2e4\ub978 \ud074\ub798\uc2a4\ub4e4\uc774 Position\uc5d0 \ub300\ud55c \uc758\uc874\ub3c4\uac00 \ub108\ubb34 \ub192\uc740 \uac83 \uac19\uc558\ub2e4. \\n\ub610\ud55c Players\uac00 \ubcc4\ub2e4\ub978 \ucc45\uc784\uc744 \uac00\uc9c0\uace0 \uc788\uc9c0 \uc54a\ub2e4\uace0 \ub290\uaf08\ub2e4. \\n\\n```mermaid\\ngraph TD\\n\\n LadderGameController --\x3e LadderGame\\n LadderGame --\x3e Ladder\\n LadderGame --\x3e Players\\n LadderGame --\x3e Items\\n\\n Ladder --\x3e Line\\n Line --\x3e LineStatus\\n\\n LadderGame --\x3e Position\\n Ladder --\x3e Position\\n Items --\x3e Position\\n Line --\x3e Position\\n Players --\x3e Position\\n\\n LadderGame --\x3e LadderGameResult\\n\\n Items --\x3e Item\\n Players --\x3e Player\\n\\n LadderGameController --\x3e InputView\\n LadderGameController --\x3e OutputView\\n\\n```\\n\\n```java\\npublic LadderGameResult play() {\\n final Map result = new LinkedHashMap<>();\\n // \uc0ac\uc6a9\uc790 \uc218\ub9cc\ud07c Position\uc744 \uac00\uc838\uc640\uc11c \uc0ac\ub2e4\ub9ac \uac8c\uc784\uc744 \uc9c4\ud589\ud55c\ub2e4.\\n for (Position position : Position.range(players.count())) {\\n final Position resultPosition = ladder.play(position);\\n result.put(players.get(position), items.get(resultPosition));\\n }\\n return new LadderGameResult(result);\\n}\\n```\\n\\n### Player\uc5d0\uac8c Ladder\ub97c \uc804\ub2ec\ud558\uc5ec \uac8c\uc784\uc744 \uc9c4\ud589\ud558\ub294 \ubc29\ubc95\\n\\nPosition\uc5d0 \ub300\ud55c \uac12\uc744 \uac00\uc9c0\uace0 \uc788\ub294 Player\uc5d0\uac8c Ladder\ub97c \ub118\uaca8\uc11c, Player\uac00 Ladder\uc5d0\uac8c \uba54\uc2dc\uc9c0\ub97c \ubcf4\ub0b4\ub3c4\ub85d \uad6c\ud604\ud558\uc600\ub2e4. \\n\uc774 \ubc29\ubc95\uc774 \uc0ac\ub2e4\ub9ac \uac8c\uc784\uc744 \uc704\ud574\uc11c \uac1d\uccb4\ub4e4\uc774 \uae34\ubc00\ud558\uac8c \ud611\ub825\ud558\uace0, \uc870\uae08 \ub354 \ucc45\uc784\uc758 \ubd84\ubc30\uac00 \uc798 \ub418\uc5b4\uc788\ub2e4\uace0 \uc0dd\uac01\uc774 \ub418\uc5c8\ub2e4. \\n\\n```mermaid\\ngraph TD\\n\\n LadderGameController --\x3e LadderGame\\n LadderGame --\x3e Ladder\\n LadderGame --\x3e Players\\n LadderGame --\x3e Items\\n\\n Ladder --\x3e Line\\n Line --\x3e LineStatus\\n Line --\x3e Position\\n\\n Players --\x3e Ladder\\n Player --\x3e Ladder\\n\\n Item --\x3e Position\\n Player --\x3e Position\\n\\n\\n LadderGame --\x3e LadderGameResult\\n\\n Items --\x3e Item --\x3e ItemName\\n Players --\x3e Player --\x3e PlayerName\\n\\n LadderGameController --\x3e InputView\\n LadderGameController --\x3e OutputView\\n\\n OutputView --\x3e LadderMessageGenerator\\n```\\n\\n```java\\npublic LadderGameResult play() {\\n // \ucc38\uac00\uc790\ub4e4\uc5d0\uac8c \uc0ac\ub2e4\ub9ac\ub97c \uc804\ub2ec\ud574\uc11c \uc0ac\ub2e4\ub9ac\uc5d0\uac8c \uba54\uc2dc\uc9c0\ub97c \ubcf4\ub0b4\ub3c4\ub85d \ud55c\ub2e4.\\n final Map playResult = players.play(ladder);\\n\\n final Map result = new LinkedHashMap<>();\\n for (Player player : playResult.keySet()) {\\n result.put(player, toItem(playResult.get(player)));\\n }\\n return new LadderGameResult(result);\\n}\\n```\\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n**\uc720\ube44\ucffc\ud130\uc2a4 \uc5b8\uc5b4\uc5d0 \uc2dc\uac04\uc744 \ub4e4\uc774\uae30** \\n\uc720\ube44\ucffc\ud130\uc2a4 \uc5b8\uc5b4\ub97c \uc815\ud558\ub294\ub370 \uc2dc\uac04\uc744 \uc870\uae08 \ub354 \ub4e4\uc5ec\uc57c\uaca0\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uc0ac\ub2e4\ub9ac \ud0c0\uae30\uc758 \uc2e4\ud589 \uacb0\uacfc\ub97c Item\uc73c\ub85c \uc9d3\ub2e4\ub2c8.. \ubb54\uac00 \ub9cc\uc871\uc2a4\ub7fd\uc9c0 \uc54a\ub2e4. \\n\uc774\uc804 \ubbf8\uc158\uacfc \ub9c8\ucc2c\uac00\uc9c0\ub85c, \uba85\uba85\ud558\ub294 \ubd80\ubd84\uc5d0\uc11c \ubd80\uc871\ud568\uc744 \ub9ce\uc774 \ub290\uaf08\ub2e4. \\n\\n**\ud398\uc5b4\uc640 \uc870\uae08 \ub354 \uce5c\ud574\uc9c0\uae30** \\n\uccab\ub0a0\uc740 \ud398\uc5b4\uc640 \uce5c\ud574\uc9c0\ub294 \uc2dc\uac04\uc744 \uc870\uae08 \ub354 \uac00\uc838\uc57c\uaca0\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uc6b0\uac00\ub791 \ud68c\uace0\ud560 \ub54c \ub0b4\uac00 \uc2dc\uc791\ud558\uc790\ub9c8\uc790 \ucee8\ubca4\uc158 \uc815\ud558\uc790\uace0 \ud574\uc11c \ub9ce\uc774 \ub2f9\ud669\uc2a4\ub7ec\uc6e0\ub2e4\uace0 \ud55c\ub2e4. \uc6b0\uac00 \ubbf8\uc548.. \ud83e\udd72\\n\\n**README\ub97c \uc870\uae08 \ub354 \uaf3c\uaf3c\ud558\uac8c** \\n\uc774\uc0c1\ud558\uac8c \ucf54\ub529\uc5d0 \uc9d1\uc911\ud558\uba74 README\ub97c \uc5c5\ub370\uc774\ud2b8\ud558\uba74\uc11c \uac19\uc774 \ucee4\ubc0b \ud558\ub294 \uac78 \ud56d\uc0c1 \uae4c\uba39\ub294\ub2e4. \\n\ub2e4\uc74c \ubbf8\uc158\uc5d0\ub294 \uc870\uae08 \ub354 \uc2e0\uacbd \uc368\uc57c\uaca0\ub2e4.\\n\\n**\uc88b\uc740 \uc9c8\ubb38\uc744 \uc0dd\uac01\ud558\uae30** \\n\uccab PR\ub54c \ub9ac\ubdf0\uc5b4\uc5d0\uac8c \uc9c8\ubb38\uc744 \ub0a8\uae30\uc9c0 \ubabb\ud588\ub2e4. \\n\ub9ac\ubdf0\uc5b4\uc640\uc758 \uc2dc\uac04\uc774 \uc18c\uc911\ud55c \uc2dc\uac04\uc774\ub77c\ub294 \uac83\uc744 \uae4c\uba39\uc9c0 \ub9d0\uace0, \ub098\uc758 \uc131\uc7a5\uc5d0 \ub3c4\uc6c0\uc774 \ub420 \uc218 \uc788\ub294 \uc9c8\ubb38\uc744 \uc0dd\uac01\ud574\uc57c\uaca0\ub2e4. \\n\\n**PR \ud6c4\uc5d0\ub3c4 \uaf3c\uaf3c\ud558\uac8c \ud655\uc778\ud558\uae30** \\n\ubd84\uba85 \uc54c\uace0 \uc788\ub294 \ubd80\ubd84\uc774\uc9c0\ub9cc, \ub193\uce5c \ubd80\ubd84\uc774 \ub9ce\uc740 \uac83 \uac19\uc558\ub2e4. \\nPR \ud558\uae30 \uc804\uc5d0\ub3c4 \uacc4\uc18d \ud655\uc778\uc744 \ud588\uc9c0\ub9cc, \uc544\ubb34\ub798\ub3c4 IntelliJ\uc5d0\uc11c \ubcf4\ub2c8 \ucf54\ub4dc\uc5d0 \uc775\uc219\ud574\uc838\uc11c \uadf8\ub7f0\uc9c0 \ubcc0\uacbd\ud574\uc57c \ud560 \ubd80\ubd84\uc774 \uc798 \uc548\ubcf4\uc600\ub2e4. \\ngithub pr\uc5d0\uc11c\ub294 \uc804\uccb4 \ubcc0\uacbd\uc0ac\ud56d\uc744 \ud655\uc778\ud560 \uc218 \uc788\uc73c\ub2c8 PR \ud6c4\uc5d0\ub3c4 \uaf2d \ud655\uc778\ud574\uc57c\uaca0\ub2e4.\\n\\n**\uc801\uadf9\uc801\uc73c\ub85c \ub098\uc758 \uc758\uacac\uc744 \ub9d0\ud558\uae30** \\n\uc758\uacac\uc744 \uc801\uadf9\uc801\uc73c\ub85c \ub0b4\ub294 \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c \ud398\uc5b4\uc758 \uc758\uacac\uc774 \uad1c\ucc2e\ub2e4\uace0 \uc0dd\uac01\ud558\uba74 \uc218\uc6a9 \ud6c4 \uac1c\uc120\uc744 \ud558\ub294 \ubc29\ud5a5\uc73c\ub85c \uc9c4\ud589\uc744 \ud588\uc5c8\ub294\ub370, \uc870\uae08 \ub354 \uac1c\uc120\ud560 \uc218 \uc788\ub294 \ubc29\ud5a5\uc774 \uc788\ub2e4\uba74 \ub098\ub3c4 \uc801\uadf9\uc801\uc73c\ub85c \uc758\uacac\uc744 \ub9d0\ud574\uc57c\uaca0\ub2e4\uace0 \uc0dd\uac01\uc774 \ub4e0\ub2e4. \\n\ub098\ub3c4 \uc124\ub4dd\ud558\ub294 \ud798\uc744 \uae30\ub974\uace0, \ud398\uc5b4\ub3c4 \uc88b\uc740 \ubc29\ud5a5\uc744 \uc54c \uc218 \uc788\uace0, \uacb0\uacfc\ubb3c\ub3c4 \uc88b\uc740 \ubc29\ud5a5\uc73c\ub85c \ub098\uc624\uc9c0 \uc54a\uc744\uae4c? (\uace0\ubbfc \ub4e4\uc5b4\uc8fc\uc2e0 \ub9ac\ubdf0\uc5b4 \ud130\ud2c0\ud83d\udc22 \uac10\uc0ac\ud569\ub2c8\ub2e4.)\\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**\uac1d\uccb4\uc758 \uc0dd\uc131 \ucc45\uc784** \\nPlayers\uac00 Position\uc744 \uc0dd\uc131\ud558\uace0 Player\uc758 \uc0dd\uc131\uc790\uc5d0 \ub123\uc5b4\uc8fc\uc5c8\ub2e4. \ud558\uc9c0\ub9cc \uc774 \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c \uc0dd\uc131 \ucc45\uc784\uc5d0 \uad00\ub828\ub41c \ucf54\uba58\ud2b8\uac00 \ub2ec\ub838\ub2e4.\\n\uc2dc\uac04\uc744 \uac00\uc9c0\uace0 \uc0dd\uac01\ud574 \ubcf4\ub2c8 Position\uc744 \uac00\uc9c0\uace0 \uc788\ub294 \uac74 Player\uae30 \ub54c\ubb38\uc5d0 \uc0dd\uc131 \ucc45\uc784\uc744 Player\uac00 \ub2f4\ub2f9\ud558\ub294 \uac83\uc774 \uc88b\ub2e4\uace0 \uc0dd\uac01\ub418\uc5c8\ub2e4. \\n\\n\uc0dd\uc131 \ucc45\uc784\uc5d0 \uad00\ud55c \ud328\ud134\uc73c\ub85c GRASP\uc758 Creator \ud328\ud134\uc774 \uc788\ub294\ub370 \ub2e4\uc74c\uc758 \uc694\uc18c\ub97c \ucd5c\ub300\ud55c \ub9cc\uc871\ud558\ub294 \ud074\ub798\uc2a4\uc5d0 \uc0dd\uc131 \ucc45\uc784\uc744 \ud560\ub2f9\ud558\ub294 \uac83\uc774 \uc88b\ub2e4. \\n- B\uac00 A \uac1d\uccb4\ub97c \ud3ec\ud568 \ub610\ub294 \ucc38\uc870\ud55c\ub2e4.\\n- B\uac00 A \uac1d\uccb4\ub97c \uae30\ub85d\ud55c\ub2e4.\\n- B\uac00 A\ub97c \uae34\ubc00\ud558\uac8c \uc0ac\uc6a9\ud55c\ub2e4.\\n- B\uac00 A\uc758 \ucd08\uae43\uac12\uc744 \uac00\uc9c0\uace0 \uc788\ub2e4. \\n\\n\uc2e4\uc81c\ub85c \uac1d\uccb4\uc758 \uc0dd\uc131 \ucc45\uc784\uc5d0 \uad00\ud574\uc11c \uae4a\uc774 \uc0dd\uac01\ud558\uba74\uc11c \ucf54\ub529\uc744 \ud558\uc9c0 \uc54a\uc558\ub294\ub370, \uc774\ubc88 \ubbf8\uc158\uc744 \ud1b5\ud574 \uc2dc\uc57c\uac00 \ub113\uc5b4\uc9c4 \uac83 \uac19\ub2e4.\\n\\n**\ud328\ud0a4\uc9c0 \ubd84\ub9ac \uae30\uc900** \\n\ud328\ud0a4\uc9c0 \ubd84\ub9ac\uc5d0 \ub300\ud55c \ub098\ub9cc\uc758 \uae30\uc900\uc774 \uc544\uc9c1 \uba85\ud655\ud558\uc9c0 \uc54a\uc544 \uc9c8\ubb38\uc774 \ub4e4\uc5b4\uc640\ub3c4 \uba85\ud655\ud558\uac8c \ub2f5\ubcc0\uc744 \ud558\uc9c0 \ubabb\ud588\ub2e4. \\n\ub9c8\uc9c0\ub9c9 \uc81c\ucd9c \uc804\uc5d0 \ub3c4\uba54\uc778 \ud328\ud0a4\uc9c0 \ub0b4\ubd80\ub97c \ubd84\ub9ac\ud574 \ubd24\ub294\ub370, \uae30\uc900\uc774 \uba85\ud655\ud558\uc9c0 \uc54a\uc558\uae30 \ub54c\ubb38\uc5d0 \uc88b\uc9c0 \uc54a\uc740 \uc120\ud0dd\uc774\uc5c8\ub358 \uac83 \uac19\ub2e4.\\n\ud604\uc7ac \uc9c4\ud589\ud558\ub294 \ubbf8\uc158\uc758 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ud06c\uae30\uac00 \uadf8\ub807\uac8c \ud06c\uc9c0 \uc54a\uc73c\ub2c8, domain \ud328\ud0a4\uc9c0\uc5d0\uc11c \uc138\ubd80 \ud328\ud0a4\uc9c0\ub85c \ubd84\ub9ac\ud558\uc9c0 \uc54a\uc544\ub3c4 \ub420 \uac83 \uac19\ub2e4. \\n\\n**\uc0ac\uc6a9\ud558\ub294 \ucabd\uc5d0\uc11c \uc0dd\uac01\ud558\uae30 & \uc608\uce21\uac00\ub2a5\ud55c \ucf54\ub4dc \uc791\uc131\ud558\uae30** \\nPosition\uc5d0\uc11c \ub2e4\uc74c \uc704\uce58\ub098 \uc774\uc804 \uc704\uce58\ub97c \ubc18\ud658\ud558\ub294 \uba54\uc11c\ub4dc\ub97c \ud5c8\uc6a9 \ubc94\uc704(0~19)\uac00 \ubc97\uc5b4\ub09c\ub2e4\uba74, \uc758\ubbf8 \uc5c6\ub294 \uac12\uc774 \ub4e4\uc5b4\uac04 Position\uc744 \ubc18\ud658\ud558\ub3c4\ub85d \ud588\ub2e4. \\n\uc774\uac74 Position\uc744 \uc0ac\uc6a9\ud558\ub294 \uc785\uc7a5\uc744 \uace0\ub824\ud558\uc9c0 \ubabb\ud55c \ucf54\ub529\uc774\uc5c8\ub294\ub370, \uc0ac\uc6a9\ud558\ub294 \uc785\uc7a5\uc5d0\uc11c\ub294 0~19\uc758 \uac12\uc774 \ubcf4\uc7a5\ub418\uc5b4 \uc788\ub2e4\uace0 \uc0dd\uac01\ud560 \uac83\uc774\uae30 \ub54c\ubb38\uc774\ub2e4. \\n\ub530\ub77c\uc11c hasNext, hasPrevious\ub77c\ub294 \uc774\uc804 \uac12, \uc774\ud6c4 \uac12\uc774 \ubc94\uc704 \ub0b4\uc5d0 \uc788\ub294\uc9c0 \ud655\uc778\ud558\ub294 \uba54\uc11c\ub4dc\ub97c \ucd94\uac00\ud558\uace0, \uae30\uc874\uc758 \uac12\uc744 \uac00\uc838\uc624\ub294 \uba54\uc11c\ub4dc\ub294 \ubc94\uc704\uac00 \ubc97\uc5b4\ub098\uba74 \uc608\uc678\ub97c \ub358\uc9c0\ub294 \ubc29\ud5a5\uc73c\ub85c \ud574\uacb0\ud558\uc600\ub2e4. \\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n\ubc1d\uc740 \uae30\uc6b4\uc744 \uac00\uc9c0\uace0 \uc788\uace0 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uacfc \uce5c\ud654\ub825\uc774 \uc88b\uc740 \uac83 \uac19\uc558\ub2e4. \\n\uc774\ubc88\uc5d0 \ud398\uc5b4 \ud560 \ub54c \ucee8\ub514\uc158 \uad00\ub9ac\ub97c \uc81c\ub300\ub85c \ubabb\ud574\uc11c \ub9ce\uc774 \ubbf8\uc548\ud588\ub2e4. \ub2e4\uc74c\uc5d0\ub294 \ucd5c\uc0c1\uc758 \ucee8\ub514\uc158\uc73c\ub85c \ud398\uc5b4\ub97c \uc900\ube44\ud574 \ubd10\uc57c\uaca0\ub2e4. \\n\uadf8\ub9ac\uace0 \uc6b0\uac00\ub791 \ud398\uc5b4\ub97c \ud558\uace0 \ub098\uc11c, \ub098\ub3c4 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uacfc \ub354 \uc798 \uc9c0\ub0b4\ubd10\uc57c\uaca0\ub2e4\ub294 \uc0dd\uac01\uc774 \ub4e4\uc5b4 \uc870\uae08 \ub354 \uc6a9\uae30\ub97c \ub0b4 \uc7a1\ub2f4 \uc911\uc774\ub2e4! \\n\\n\uc758\uacac\uc744 \uc801\uadf9\uc801\uc73c\ub85c \ub0b4\uc918\uc11c \ud398\uc5b4\ud504\ub85c\uadf8\ub798\ubc0d \uc9c4\ud589\uc774 \uc798 \ub418\uc5c8\ub2e4. \\n\ub610\ud55c \ud398\uc5b4 \uc9c4\ud589\uc774 \ub290\ub9b0 \uac83 \uac19\ub2e4\uace0 \ub9d0\ud574\uc918\uc11c \uc548\uc815\uc801\uc73c\ub85c \uc2dc\uac04 \uc548\uc5d0 \ubbf8\uc158\uc744 \uc644\ub8cc\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\ud398\uc5b4\ud504\ub85c\uadf8\ub798\ubc0d \uc9c4\ud589 \uc18d\ub3c4\uc5d0 \ub300\ud574 \uc870\uae08 \ub354 \uc0dd\uac01\uc744 \ud574\ubd10\uc57c\uaca0\ub2e4!\\n\\n\ud56d\uc0c1 \uc9c0\ub098\uac08 \ub54c\ub9c8\ub2e4 \uc6c3\uc5b4\uc8fc\ub294\ub370, \ub098\ub3c4 \uc790\uc8fc \uc6c3\uc5b4\uc57c\uaca0\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uc6c3\ub294 \uac83\ub9cc\uc73c\ub85c\ub3c4 \uc0ac\ub78c\uc774 \ubc1d\uc544 \ubcf4\uc5ec\uc11c \ub108\ubb34 \uc88b\uc740 \uac83 \uac19\ub2e4!"},{"id":"racing-car-retrospective","metadata":{"permalink":"/racing-car-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-02-14-\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-1/2023-02-14-\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0","description":"\uc790\ub3d9\ucc28 \uacbd\uc8fc","date":"2023-02-14T00:00:00.000Z","formattedDate":"2023\ub144 2\uc6d4 14\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":7.56,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0","slug":"racing-car-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0","permalink":"/ladder-retrospective"},"nextItem":{"title":"Parameterized Tests","permalink":"/parameterized-tests"}},"content":"### \uc790\ub3d9\ucc28 \uacbd\uc8fc\\n\\n\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158\uc5d0\uc11c\ub294 \ub2e4\uc990\uacfc \ud398\uc5b4\uac00 \ub9e4\uce6d\ub418\uc5c8\ub2e4. \\n\uc6b0\ud14c\ucf54 \ub4e4\uc5b4\uc640\uc11c \uccab \ud398\uc5b4\ud504\ub85c\uadf8\ub798\ubc0d\uc774\ub77c \ub9ce\uc774 \ub5a8\ub838\uc9c0\ub9cc, \ub2e4\uc990\uc774 \ub300\ud654\ub97c \uc798 \uc774\ub04c\uc5b4\uc918 \ub108\ubb34 \uc990\uac70\uc6e0\ub2e4. \\n\\n\uccab\ub0a0\uc740 \uac04\ub2e8\ud788 \ucee8\ubca4\uc158\uacfc \ud658\uacbd\uc744 \uc124\uc815\ud558\ub294 \uc2dc\uac04\uc744 \uac00\uc84c\uace0 \ub2e4\uc74c \ub0a0\ubd80\ud130 \uc790\ub3d9\ucc28 \uacbd\uc8fc\ub97c \uc2dc\uc791\ud588\ub2e4. \\n\uc2dc\uc791\uc740 \uac04\ub2e8\ud558\uac8c \uc694\uad6c\uc0ac\ud56d\uc744 \uc815\ub9ac\ud558\uace0, \uc5b4\ub5bb\uac8c \ucf54\ub4dc\ub97c \uc791\uc131\ud560\uc9c0 \uac19\uc774 \uace0\ubbfc\ud588\ub2e4. \\n\\n\uc2dc\uc791\ud558\uae30 \uc804 \uc544\ub798\uc640 \uac19\uc774 mermaid\ub97c \uc774\uc6a9\ud558\uc5ec \uc758\uc874\uc131 \ubc29\ud5a5\uc5d0 \ub300\ud574\uc11c \uac04\ub2e8\ud55c \ub2e4\uc774\uc5b4\uadf8\ub7a8\uc744 \ub9cc\ub4e4\uace0 \uc2dc\uc791\ud588\ub2e4. \\nmermaid\ub294 \ucf54\ub4dc\ub85c \ub2e4\uc774\uc5b4\uadf8\ub7a8\uc744 \uc0dd\uc131 \ud574\uc8fc\ub294 \ub3c4\uad6c\ub85c \ub2e4\uc74c\uacfc \uac19\uc740 \uc7a5\uc810\uc774 \uc788\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4.\\n\\n- \ucf54\ub4dc \uae30\ubc18\uc774\ub77c \ube60\ub978 \uc2dc\uac04 \uc548\uc5d0 \uc0dd\uac01\ud55c \uac83\uc744 \uc2dc\uac01\ud654\ud560 \uc218 \uc788\ub2e4. \\n- github\uc5d0\uc11c mermaid\ub97c \uc9c0\uc6d0\ud558\uae30 \ub54c\ubb38\uc5d0 \ub9ac\ubdf0\uc5b4\uc5d0\uac8c \ucf54\ub4dc\ub97c \uc774\ud574\ud560 \uc218 \uc788\ub294 \ubd80\uac00\uc801\uc778 \uc815\ubcf4\ub97c \uc81c\uacf5\ud560 \uc218 \uc788\ub2e4.\\n\\n```mermaid\\n---\\ntitle: \uc790\ub3d9\ucc28 \uacbd\uc8fc \uccab \ub9ac\ubdf0 \uc694\uccad\uc2dc \uad6c\uc870\\n---\\ngraph TD\\n Cars --\x3e Car\\n Car --\x3e Name\\n Car --\x3e Position\\n RacingGame --\x3e Count\\n RacingGame --\x3e NumberGenerator\\n RacingGame --\x3e Cars\\n RacingCarController --\x3e RacingGame\\n RandomNumberGenerator -.-> NumberGenerator\\n RacingCarController --\x3e InputView\\n InputView --\x3e InputValidator\\n RacingCarController --\x3e OutputView\\n```\\n\\n\ubbf8\uc158\uc744 \uc9c4\ud589\ud558\ub294 \ub370 \ud070 \uc5b4\ub824\uc6c0\uc774 \uc788\uc9c0\ub294 \uc54a\uc558\uace0, \ud398\uc5b4\ub97c \ub9c8\uce58\uae30 \uc804 \uc11c\ub85c \uace0\ubbfc\ub418\ub294 \ubd80\ubd84\uc744 \uc815\ub9ac\ud588\uc744 \ub54c \uc88b\uc558\ub2e4.\\n\\n\ud398\uc5b4\ud558\uba74\uc11c \uc798\ud588\ub2e4\uace0 \uc0dd\uac01\ud588\ub358 \uc810\uc740 \uc11c\ub85c\uc758 \uc0dd\uac01\uacfc \ub9ac\ubdf0 \ubc1b\uc740 \uac83\uc744 \uacf5\uc720\ud55c \uac83\uc774\ub2e4. \\n\ub9ac\ud329\ud130\ub9c1\uc744 \uc5b4\ub5bb\uac8c \ud588\ub294\uc9c0? \uc774\ub7f0 \ub9ac\ubdf0\uc5d0 \ub300\ud574 \uc5b4\ub5bb\uac8c \uc0dd\uac01\ud558\ub294\uc9c0 \uae4a\uac8c \uace0\ubbfc\ud558\ub294 \uc2dc\uac04\uc744 \uac00\uc9c8 \uc218 \uc788\uc5c8\ub2e4.\\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n\ub9ac\ud329\ud130\ub9c1\uc774 \ub05d\ub09c \ud6c4 \uba54\uc11c\ub4dc\uba85, \ud14c\uc2a4\ud2b8\uc2dc \ucd9c\ub825\ud558\ub294 \uba54\uc2dc\uc9c0\uc5d0 \ub300\ud55c \ucf54\uba58\ud2b8\uac00 \ub9ce\uc774 \ub2ec\ub838\ub2e4. \\n\uac1d\uccb4\uac00 \uc5b4\ub5a4 \ucc45\uc784\uacfc \uc5ed\ud560\uc744 \uac00\uc9c0\ub294\uc9c0 \uc0dd\uac01\ud558\ub294 \uc2dc\uac04\uc744 \uac00\uc9c0\uace0 \uba85\ud655\ud55c \uba54\uc11c\ub4dc\uba85\uc744 \uc791\uc131\ud574\uc57c\uaca0\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\\n\ud3c9\uc18c\uc5d0 \ud504\ub85c\uadf8\ub798\ubc0d \uc774\uc57c\uae30\uac00 \uc544\ub2cc \ub2e4\ub978 \uc8fc\uc81c\ub85c \uc774\uc57c\uae30\ud558\uba74 \uc798 \ub4e4\uc73c\ub824\uace0 \ud558\ub294 \ud3b8\uc774\uc9c0\ub9cc \\n\ub0b4\uac00 \uc88b\uc544\ud558\ub294 \uc8fc\uc81c, \uad00\uc2ec\uac00\ub294 \uc8fc\uc81c\uc778 \ud504\ub85c\uadf8\ub798\ubc0d\uc5d0 \ub300\ud55c \uc774\uc57c\uae30\ub97c \ud560 \ub550 \ub9d0\uc774 \ub9ce\uc544\uc9c4\ub2e4. \\n\ub2e4\uc74c \ubbf8\uc158\ubd80\ud130\ub294 \ub354 \ub9ce\uc740 \uc2dc\uac04\uc744 \ud398\uc5b4\uc758 \uc758\uacac\uacfc \uc774\uc57c\uae30\ub97c \ub4e3\ub294 \uacf3\uc5d0 \uc0ac\uc6a9\ud574\uc57c\uaca0\ub2e4.\\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**Assertions extracting**\\n\\n\uacb0\uacfc \ub0b4\ubd80\uc5d0 \uc788\ub294 \uac12\uc744 \ud655\uc778\ud558\uace0 \uc2f6\uc744 \ub54c extracting \ud0a4\uc6cc\ub4dc\ub97c \uc774\uc6a9\ud574\uc11c \ub0b4\ubd80\uc758 \uac12\uc744 \uac80\uc99d\ud560 \uc218 \uc788\ub2e4. \\n\uc774\uc804\uc5d0\ub294 \ud544\uc694\uc5d0 \ub530\ub77c stream\uc744 \uc774\uc6a9\ud558\uc5ec \uac80\uc99d\ud560 \uac12\uc744 \uc0dd\uc131\ud588\uc9c0\ub9cc, \ud574\ub2f9 \ubc29\ubc95\uc744 \uc774\uc6a9\ud574\uc11c \uc808\ucc28\ub97c \uc904\uc77c \uc218 \uc788\uc5c8\ub2e4.\\n\\n```java\\n@Test\\nvoid extracting() {\\n final Cars cars = new Cars(List.of(\\"car1\\", \\"car2\\"));\\n\\n assertThat(cars.getCars())\\n .extracting(Car::getName)\\n .containsExactly(\\"car1\\", \\"car2\\");\\n}\\n```\\n\\n---\\n\\n\uc544\ub798\ub294 \ub9ac\ubdf0\uc5b4\ub2d8\uacfc \ub300\ud654\ub97c \ub098\ub204\uba74\uc11c \uc5bb\uc740 \ub2f5\ubcc0 + \ub098\uc758 \uc758\uacac\uc774\ub2e4.\\n\\n**\uc81c\uc5b4\ud560 \uc218 \uc5c6\ub294 \ubd80\ubd84\uc5d0 \ub300\ud55c \ud14c\uc2a4\ud2b8**\\n\\n\ud14c\uc2a4\ud2b8 \ub300\uc0c1\uc774 \uac80\uc99d\ub41c \uac83\uc774\ub77c\uba74 \uc791\uc131\ud558\uc9c0 \uc54a\uac70\ub098, \uc81c\uc5b4\ud560 \uc218 \uc788\ub294 \ubd80\ubd84\uc5d0 \ub300\ud55c \ud14c\uc2a4\ud2b8\ub97c \ub354\uc6b1 \uaf3c\uaf3c\ud558\uac8c \uc791\uc131\ud55c\ub2e4. \\n\uc774\uac74 \uac1c\uc778\uc801\uc778 \uc0dd\uac01\uc774\uc9c0\ub9cc \ub0b4\uac00 \uc548\uc815\uac10\uc774 \ub4e4 \uc218 \uc788\uc744 \uc815\ub3c4\ub85c \ucd9c\ub825 \ubc94\uc704 \ub0b4\uc758 \uacb0\uacfc\ub97c \ubc18\ud658\ud558\ub294\uc9c0 \uc815\ub3c4 \ud14c\uc2a4\ud2b8\ud560 \uc218 \uc788\uc9c0 \uc54a\uc744\uae4c?\\n\\n**\ub2e8\uc21c \uc704\uc784\uc744 \ud558\ub294 \uba54\uc11c\ub4dc\uc5d0 \ub300\ud55c \ud14c\uc2a4\ud2b8**\\n\\n\uc704\uc784\uc774\ub77c\ub294 \uac83\uc740 \uc5ed\ud560\uacfc \ucc45\uc784\uc744 \ub118\uaca8\uc900\ub2e4\ub294 \uac83\uc774\ub2e4. \\n\ud638\ucd9c \ud69f\uc218\ub97c \uac80\uc99d\ud558\ub294 \uac83\ubcf4\ub2e4 \uacb0\uacfc\uc5d0 \ub300\ud55c \ud14c\uc2a4\ud2b8\ud558\ub294 \uac83\uc774 \uc88b\ub2e4. \\n\ub2e8\uc21c\ud788 \uc704\uc784\ub9cc \ud558\ub294 \ud14c\uc2a4\ud2b8\uc758 \uacbd\uc6b0 \uacb0\uacfc\ub97c \uac80\uc99d\ud55c\ub2e4\uba74 \ud14c\uc2a4\ud2b8\uac00 \uc911\ubcf5\ub418\uc9c0 \uc54a\uc744\uae4c \uc0dd\uac01\ud588\uc5c8\ub2e4. \\n\ub530\ub77c\uc11c \uc911\ubcf5\ub41c \ud14c\uc2a4\ud2b8\ub97c \uc904\uc774\uae30 \uc704\ud574 \ub0b4\ubd80\uc758 \uba54\uc11c\ub4dc\ub97c \ud638\ucd9c\ud558\ub294\uc9c0 \uac80\uc99d\ud558\ub294 \ubc29\ubc95\ub3c4 \uc788\ub2e4\ub294 \uac83\uc744 \uc54c\uac8c \ub418\uc5c8\uc9c0\ub9cc \\n\uc548\uc815\uc801\uc73c\ub85c \uacb0\uacfc\ub97c \ud14c\uc2a4\ud2b8 \ud558\ub294 \uac83\uc774 \ub354 \uc88b\uc740 \ubc29\ubc95\uc778 \uac83 \uac19\ub2e4.\\n\\n**\ud14c\uc2a4\ud2b8\ub97c \uc704\ud55c getter \uc0ac\uc6a9**\\n\\n\ud14c\uc2a4\ud2b8 \uc6a9\ub3c4\ub85c \ub3c4\uba54\uc778\uc5d0 \uc0c8\ub85c\uc6b4 \uba54\uc11c\ub4dc\uac00 \uc0dd\uc131\ub418\ub294 \uac83\uc740 \uc88b\uc9c0 \ubabb\ud558\ub2e4. \\n\ud544\uc694\uc758 \uacbd\uc6b0 \uc0dd\uc131\ud574\uc11c \uc0ac\uc6a9\ud560 \uc218 \uc788\uc9c0\ub9cc, \uae30\uc874\uc5d0 \uc788\ub294 \uba54\uc11c\ub4dc\ub4e4\uc744 \ud65c\uc6a9\ud574\ubcf4\ub294 \uac83\uc774 \ub354 \uc88b\uc740 \ubc29\ubc95\uc774\ub2e4. \\n\uc774 \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c \ub9e4\uc6b0 \ub3d9\uc758\ud558\uace0, \uc55e\uc73c\ub85c\ub3c4 \ucd5c\ub300\ud55c \ud14c\uc2a4\ud2b8\ub97c \uc704\ud55c \ucf54\ub4dc\ub97c \ub3c4\uba54\uc778\uc5d0 \uc791\uc131\ud558\uc9c0 \uc54a\uc744 \uac83 \uac19\ub2e4.\\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n\uc9c8\ubb38\uc774\ub098 \uc0dd\uac01\ud560 \uc810\uc774 \uc788\uc744 \ub54c \ub9e4\uc6b0 \uae4a\uac8c \uace0\ubbfc\ud558\ub294 \uac83 \uac19\uc558\ub2e4. \\n\uc0dd\uac01\uc744 \uc815\ub9ac\ud55c \ud6c4 \uc790\uc2e0\uc758 \uc758\uacac\uc744 \uba85\ub8cc\ud558\uac8c \uc804\ub2ec\ud574\uc8fc\uc5c8\ub2e4. \\n\uadf8\ub807\uae30 \ub54c\ubb38\uc5d0 \uc9c0\uc2dd\uc744 \ud6a8\uc728\uc801\uc73c\ub85c \uc2b5\ub4dd\ud55c\ub2e4. \\n\ub09c \uc0dd\uac01\uc744 \uc798 \uc815\ub9ac\ud558\uc9c0 \uc54a\uc740 \ucc44\ub85c \ub0b4\ubc84\ub824 \ub454 \uc595\uc740 \uc9c0\uc2dd\uc774 \ub9ce\uc740 \uac83 \uac19\ub2e4. (\uc774\ub7f0 \uac83\ub3c4 \uc544\ub294 \uac83\uc774\ub77c\uace0 \ud560 \uc218 \uc788\uc744\uae4c?) \\n\uc55e\uc73c\ub85c \uc870\uae08 \ub354 \uba38\ub9bf\uc18d\uc5d0\uc11c \uc815\ub9ac\ud558\uace0, \ubb38\uc81c\uc5d0 \ub300\ud574 \uae4a\uac8c \uace0\ubbfc\ud558\ub294 \uc2dc\uac04\uc744 \ub298\ub824\uc57c\uaca0\ub2e4.\\n\\n\uac1c\ubc1c\uc5d0 \uc5f4\uc815\uc744 \uac00\uc9c4 \uac8c \ub290\uaef4\uc9c4\ub2e4. \\n\ub098\ub3c4 \uac1c\ubc1c\uc744 \uc88b\uc544\ud558\uc9c0\ub9cc, \ucd5c\uadfc\uc5d0\ub294 \uc758\uc9c0\uac00 \uc57d\ud574\uc84c\uc5c8\ub2e4. \\n\uc5f4\uc815\uc774 \uac00\ub4dd\ud55c \uc0ac\ub78c\uc744 \ub9cc\ub098\ub2c8 \ub098\ub3c4 \uc5f4\uc815\uc801\uc778 \uc0ac\ub78c\uc774 \ub418\ub294 \uac83 \uac19\ub2e4.\\n\\n\uce6d\ucc2c\uc744 \ub9ce\uc774 \ud574\uc900\ub2e4. \ub2e8\uc21c\ud788 \ub9ce\uc774 \ud574\uc8fc\ub294 \uac83\uc774 \uc544\ub2c8\ub77c, \uc9c4\uc2ec\uc744 \ub2f4\uae34 \uce6d\ucc2c\uc744 \ud574\uc92c\ub2e4. \\n\uce6d\ucc2c\uc740 \uace0\ub798\ub3c4 \ucda4\ucd94\uac8c \ud558\ub358\uac00? \\n\uadf8\ub798\uc11c \uc990\uac70\uc6b4 \ub9c8\uc74c\uc73c\ub85c \ud398\uc5b4 \ud504\ub85c\uadf8\ub798\ubc0d\uc744 \ud588\uc5c8\ub358 \uac83 \uac19\ub2e4.\\n\\n\uc5b4\ub5a4 \uc774\uc720 \ub54c\ubb38\uc778\uc9c0 \ubaa8\ub974\uaca0\uc9c0\ub9cc \uac19\uc774 \ud398\uc5b4\ud558\ub294\ub370 \ud3b8\ud55c \ub9c8\uc74c\uc774 \ub4e4\uc5c8\ub2e4. \\n\uc774\uac74 \ubc14\ub85c \ubc30\uc6b8 \uc218 \uc5c6\uc9c0\ub9cc. \\n\ub098\ub3c4 \uac19\uc774 \uc77c\ud560 \ub54c \ud3b8\ud55c \uc0ac\ub78c, \uac19\uc774 \uc77c\ud558\uace0 \uc2f6\uc740 \uc0ac\ub78c\uc774 \ub418\uae30 \uc704\ud574 \uae4a\uc774 \uace0\ubbfc\ud574\ubd10\uc57c\uaca0\ub2e4."},{"id":"parameterized-tests","metadata":{"permalink":"/parameterized-tests","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-02-12-Parameterized Tests.mdx","source":"@site/blog/2023-1/2023-02-12-Parameterized Tests.mdx","title":"Parameterized Tests","description":"\ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub2e4\ubcf4\uba74 \ub9e4\uac1c\ubcc0\uc218\uc5d0 \ub530\ub77c \ubc18\ubcf5\uc774 \ub418\ub294 \ud14c\uc2a4\ud2b8\ub4e4\uc774 \uc0dd\uae34\ub2e4.","date":"2023-02-12T00:00:00.000Z","formattedDate":"2023\ub144 2\uc6d4 12\uc77c","tags":[{"label":"Java","permalink":"/tags/java"}],"readingTime":3.17,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"Parameterized Tests","slug":"parameterized-tests","tags":["Java"]},"prevItem":{"title":"\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0","permalink":"/racing-car-retrospective"},"nextItem":{"title":"IntelliJ \uc124\uc815","permalink":"/intellij-settings"}},"content":"\ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub2e4\ubcf4\uba74 \ub9e4\uac1c\ubcc0\uc218\uc5d0 \ub530\ub77c \ubc18\ubcf5\uc774 \ub418\ub294 \ud14c\uc2a4\ud2b8\ub4e4\uc774 \uc0dd\uae34\ub2e4. \\n\uc774 \ub54c `@ParameterizedTest`\ub97c \uc0ac\uc6a9\ud558\uba74 \ub2e8\uc77c \ud14c\uc2a4\ud2b8\ub97c \ub9e4\uac1c\ubcc0\uc218\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc5ec\ub7ec \ubc88 \ubc18\ubcf5\ud560 \uc218 \uc788\ub2e4.\\n\\n## Argument Sources\\n\\n`@ParameterizedTest`\ub97c \uc0ac\uc6a9\ud558\ub824\uba74 \ucd5c\uc18c \ud558\ub098 \uc774\uc0c1\uc758 Source \uc560\ub178\ud14c\uc774\uc158\uc774 \ud544\uc694\ud558\ub2e4. \\nJUnit\uc774 \uc81c\uacf5\ud558\ub294 \ub2e4\uc591\ud55c Source\uac00 \uc788\uae30 \ub54c\ubb38\uc5d0, \ud14c\uc2a4\ud2b8\uc5d0 \ub9de\ucdb0 \ub2e4\uc591\ud558\uac8c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n### Value Source\\n\\n\uac12\uc744 \uc774\uc6a9\ud558\uc5ec \uc81c\uacf5\ud558\ub294 \ud615\ud0dc\ub85c, \ub2e4\uc74c\uacfc \uac19\uc740 \ud0c0\uc785\uc758 \uac12\uc744 \ub9e4\uac1c\ubcc0\uc218\ub85c \uc81c\uacf5\ud560 \uc218 \uc788\ub2e4. \\n- short, int, long, float, double\\n- byte, char, boolean, String, Class \\n\\n```java\\n@ParameterizedTest\\n@ValueSource(ints = {1, 100, Integer.MAX_VALUE})\\nvoid valueTest(final int value) {\\n Assertions.assertThat(value).isPositive();\\n}\\n```\\n\\n### Null & Empty Source\\n\\nnull \uac12, \ube48 \uac12\uc744 \uc81c\uacf5\ud55c\ub2e4. \\nEmpty Source\uc758 \uacbd\uc6b0 \ub2e4\uc74c\uacfc \uac19\uc740 \ud0c0\uc785\uc5d0 \ud55c\ud574 \ub9e4\uac1c\ubcc0\uc218\ub85c \uc81c\uacf5\ud560 \uc218 \uc788\ub2e4.\\n- String\\n- java.util.List, java.util.Set, java.util.Map\\n- primitive arrays \u2014 ex) int[]\\n- object arrays \u2014 ex) String[]\\n\\n```java\\n@ParameterizedTest\\n@NullAndEmptySource\\nvoid nullAndEmptyTest(final String value) {\\n Assertions.assertThat(value).isNullOrEmpty();\\n}\\n```\\n\\n### Enum Source\\n\\nEnumSource\ub97c \uc774\uc6a9\ud558\uc5ec Enum \ub610\ud55c \ub9e4\uac1c\ubcc0\uc218\ub85c \uc81c\uacf5\ud560 \uc218 \uc788\ub2e4.\\n\\n```java\\nenum Day {\\n MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;\\n}\\n\\n@ParameterizedTest\\n@EnumSource(Day.class)\\nvoid enumTest(final Day day) {\\n assertThat(day).isInstanceOf(Day.class);\\n}\\n```\\n\\n\ub2e4\uc74c\uacfc \uac19\uc774 mode \uac12\uc744 \uc774\uc6a9\ud558\uc5ec \ud2b9\uc9d5 Enum\uc744 \uc81c\uc678\ud558\uac70\ub098, \ud3ec\ud568\uc2dc\ud0ac \uc218 \uc788\ub2e4. (default: Mode.Include)\\n\\n```java\\n@ParameterizedTest\\n@EnumSource(value = Day.class, names = {\\"SATURDAY\\", \\"SUNDAY\\"}, mode = Mode.EXCLUDE)\\nvoid enumTest(final Day day) {\\n // MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY\\n assertThat(day).isInstanceOf(Day.class);\\n}\\n```\\n\\n### CSV Source\\n\\ncsv \ud615\uc2dd\uc758 \uac12\uc744 \uc774\uc6a9\ud558\uc5ec \ub9e4\uac1c\ubcc0\uc218\ub97c \uc81c\uacf5\ud55c\ub2e4. \\n\uad6c\ubd84\uc790\uc758 \uae30\ubcf8\uac12\uc740 \uc27c\ud45c(,)\ub85c \uad6c\ubd84\uc790\ub97c \ubcc0\uacbd\ud558\uace0 \uc2f6\uc744 \ub550 delimeter \uac12\uc744 \ub530\ub85c \uc804\ub2ec\ud558\uc5ec \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\uac1c\uc778\uc801\uc73c\ub85c 2\uac1c \uc815\ub3c4\uc758 \uac12\uc744 \ub9e4\uac1c\ubcc0\uc218\ub85c \uc804\ub2ec\ud558\ub294 \uacbd\uc6b0 CsvSource\ub97c \uc0ac\uc6a9\ud55c\ub2e4.\\n\\n```java\\n@ParameterizedTest\\n@CsvSource({\\"1,1\\", \\"2,4\\", \\"3,9\\", \\"4,16\\"})\\nvoid csvTest(final int number, final int result) {\\n assertThat(number * number).isEqualTo(result);\\n}\\n```\\n\\n### Method Source\\n\\n\ubcf5\uc7a1\ud55c \ud0c0\uc785\uc758 \uac12\uc744 \uc804\ub2ec\ud560 \ub54c \uc0ac\uc6a9\ud55c\ub2e4. \\n\uba54\uc11c\ub4dc\uba85\uc744 \uc785\ub825\ud558\uc5ec \ub9e4\uac1c\ubcc0\uc218\ub97c \uc81c\uacf5\ud558\ub294 \uba54\uc11c\ub4dc\ub97c \uc9c0\uc815\ud560 \uc218 \uc788\ub2e4. \\n\uba54\uc11c\ub4dc\uba85\uc744 \ub530\ub85c \uc785\ub825\ud558\uc9c0 \uc54a\uc73c\uba74 \ud14c\uc2a4\ud2b8\uba85\uacfc \ub3d9\uc77c\ud55c static \uba54\uc11c\ub4dc\uac00 \uc9c0\uc815\ub41c\ub2e4.\\n\\n```java\\n@ParameterizedTest\\n@MethodSource\\nvoid methodTest(final List numbers, final int count) {\\n assertThat(numbers).hasSize(count);\\n}\\n\\nprivate static Stream methodTest() {\\n return Stream.of(\\n Arguments.of(List.of(1), 1),\\n Arguments.of(List.of(1, 2), 2),\\n Arguments.of(List.of(1, 2, 3), 3)\\n );\\n}\\n```\\n\\n### ETC.\\n\\n\uc704\uc5d0\uc11c \uc5b8\uae09\ud55c \ubc29\ubc95 \uc774\uc678\uc5d0\ub3c4 \ub2e4\uc591\ud55c \ubc29\ubc95\uc73c\ub85c \ub9e4\uac1c\ubcc0\uc218\ub97c \uc81c\uacf5\ud560 \uc218 \uc788\ub2e4.\\n\\n- CSV \ud30c\uc77c\uc744 \uc774\uc6a9\ud55c CsvFileSource\\n- ArgumentsProvider \uad6c\ud604\ud55c \ud074\ub798\uc2a4\ub97c \uc774\uc6a9\ud558\ub294 ArgumentsSource\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n- [Guide to JUnit 5 Parameterized Tests](https://www.baeldung.com/parameterized-tests-junit-5)"},{"id":"intellij-settings","metadata":{"permalink":"/intellij-settings","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-30-IntelliJ \uc124\uc815.mdx/index.mdx","source":"@site/blog/2023-1/2023-01-30-IntelliJ \uc124\uc815.mdx/index.mdx","title":"IntelliJ \uc124\uc815","description":"Import \uc790\ub3d9 \uc801\uc6a9","date":"2023-01-30T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 30\uc77c","tags":[{"label":"IntelliJ","permalink":"/tags/intelli-j"}],"readingTime":0.465,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"IntelliJ \uc124\uc815","slug":"intellij-settings","tags":["IntelliJ"]},"prevItem":{"title":"Parameterized Tests","permalink":"/parameterized-tests"},"nextItem":{"title":"Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95","permalink":"/kotlin-null"}},"content":"### Import \uc790\ub3d9 \uc801\uc6a9\\n\\nPrefrences > Editor > General > Auto Import > Add unambiguous imports on the fly\\n\\n![auto-import](./auto-import.png)\\n\\n### \uc800\uc7a5\uc2dc \ub3d9\uc791\\n\\nPrefrences > Tools > Actions on Save\\n\\n![actions-on-save](./actions-on-save.png)\\n\\nReformat Code: Code Reformmating\\n\\nOptimize imports: \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 Import \uc81c\uac70\\n\\nRearrange: Code Style > Arrangement \uc124\uc815 \uae30\ubc18 \ucf54\ub4dc \uc7ac\uc815\ub82c\\n\\n### \uba54\uc18c\ub4dc \ucd94\ucd9c, \ubcc0\uc218 \ucd94\ucd9c\uc2dc final \uc801\uc6a9\\n\\nPrefrences > Editor > Code Style > Java > Code Generation > Final Modifier\\n\\n![final-modifier](./final-modifier.png)"},{"id":"kotlin-null","metadata":{"permalink":"/kotlin-null","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-16-Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95.mdx","source":"@site/blog/2023-1/2023-01-16-Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95.mdx","title":"Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95","description":"nullable \ud0c0\uc785","date":"2023-01-16T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 16\uc77c","tags":[{"label":"Kotlin","permalink":"/tags/kotlin"}],"readingTime":4.225,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95","slug":"kotlin-null","tags":["Kotlin"]},"prevItem":{"title":"IntelliJ \uc124\uc815","permalink":"/intellij-settings"},"nextItem":{"title":"JSR-310","permalink":"/jsr-310"}},"content":"import Tabs from \\"@theme/Tabs\\";\\nimport TabItem from \\"@theme/TabItem\\";\\n\\n### nullable \ud0c0\uc785\\n\\n\ucf54\ud2c0\ub9b0\uc740 `NullPointerException` \uc608\uc678\ub97c \ucd5c\ub300\ud55c \ubc1c\uc0dd\uc2dc\ud0a4\uc9c0 \uc54a\uae30 \uc704\ud574 \ud0c0\uc785 \uc2dc\uc2a4\ud15c\uc774 \uc124\uacc4\ub418\uc5b4 \uc788\ub2e4. \\n\uc774\ub294 \uc2e4\ud589 \uc2dc\uc810\uc774 \uc544\ub2cc \ucef4\ud30c\uc77c \uc2dc \ubbf8\ub9ac \uc624\ub958\uac00 \ubc1c\uc0dd\ud560 \uac00\ub2a5\uc131\uc774 \uc788\ub294 \ubd80\ubd84\uc744 \ubbf8\ub9ac \uac10\uc9c0\ud558\uc5ec NPE \ubc1c\uc0dd\uc758 \uac00\ub2a5\uc131\uc744 \uc904\uc5ec\uc900\ub2e4.\\n\\n\ucf54\ud2c0\ub9b0\uc758 \uacbd\uc6b0 nullable \ud0c0\uc785\uc744 \ub2e4\uc74c\uacfc \uac19\uc774 \ud45c\ud604\ud55c\ub2e4.\\n\\n```kotlin\\nval number: Int?\\n```\\n\\n\ud0c0\uc785 \ub4a4\uc5d0 `?`\ub97c \ubd99\uc5ec \ud574\ub2f9 \uac12\uc774 null\uc774 \ub420 \uc218 \uc788\ub2e4\ub294 \uac83\uc744 \uc758\ubbf8\ud55c\ub2e4. \\n\ub9cc\uc57d `?`\ub97c \ubd99\uc774\uc9c0 \uc54a\uc744 \ub54c null\uc744 \ubc1b\ub294 \uacbd\uc6b0 \ucef4\ud30c\uc77c \uc2dc \uc624\ub958\uac00 \ubc1c\uc0dd\ud55c\ub2e4.\\n\\n### `?.` Safe Calls \uc5f0\uc0b0\uc790\\n\\n\uc790\ubc14\uc5d0\uc11c NPE\ub97c \ubc1c\uc0dd\uc2dc\ud0a4\uc9c0 \uc54a\uae30 \uc704\ud574 null\uc744 \ucc98\ub9ac\ud558\ub294 \uac00\uc7a5 \uac04\ub2e8\ud55c \ubc29\ubc95\uc73c\ub85c\ub294 \ubd84\uae30\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc774 \uc788\ub2e4.\\n\\n\ucf54\ud2c0\ub9b0\uc740 \uc548\uc804\ud55c \ud638\ucd9c \uc5f0\uc0b0\uc790\uc778 `?.` \uc5f0\uc0b0\uc790\ub97c \uc9c0\uc6d0\ud55c\ub2e4. \\n\ub530\ub77c\uc11c \ucc38\uc870 \uac12\uc774 null\uc774 \uc544\ub2d0 \uacbd\uc6b0\uc5d0\ub9cc \uba54\uc11c\ub4dc \ud638\ucd9c\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\ucc38\uc870 \uac12\uc774 null\uc778 \uacbd\uc6b0 \uba54\uc11c\ub4dc \ud638\ucd9c\uc774 \ubb34\uc2dc\ub418\uace0, null\uc744 \ubc18\ud658\ud55c\ub2e4. \\n\\n\\n\\n\\n```java\\npublic String repeat(String word) {\\n if (word == null) {\\n return null;\\n }\\n return word.repeat(2);\\n}\\n```\\n\\n\\n\\n\\n```kotlin\\nfun repeat(word: String?): String? {\\n return word?.repeat(2)\\n}\\n```\\n\\n\\n\\n\\n### `?:` \uc5d8\ube44\uc2a4 \uc5f0\uc0b0\uc790\\n\\n\ucc38\uc870\ud558\ub824\ub294 \uac12\uc774 null\uc77c \uacbd\uc6b0 \uae30\ubcf8 \uac12\uc744 \ubc18\ud658\ud558\uace0 \uc2f6\uc744 \ub54c\ub294 \uc5b4\ub5bb\uac8c \ud574\uc57c \ud560\uae4c? \\n\ucf54\ud2c0\ub9b0\uc740 null\uc774 \uc544\ub2cc \uacbd\uc6b0 \uae30\ubcf8 \uac12\uc744 \uc9c0\uc815\ud560 \ub54c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub294 \uc5d8\ube44\uc2a4 \uc5f0\uc0b0\uc790\ub97c \uc9c0\uc6d0\ud55c\ub2e4.\\n\\n\\n\\n\\n```java\\npublic String stringSafe(String word) {\\n if (word == null) {\\n return \\"\\";\\n }\\n return word;\\n}\\n```\\n\\n\\n\\n\\n```kotlin\\nfun stringSafe(word: String?): String {\\n return word ?: \\"\\"\\n}\\n```\\n\\n\\n\\n\\n\ucf54\ud2c0\ub9b0\uc5d0\uc11c\ub294 throw\ub3c4 \uc2dd\uc774\uae30 \ub54c\ubb38\uc5d0 \uc5d8\ube44\uc2a4 \uc5f0\uc0b0\uc790\ub97c \uc774\uc6a9\ud558\uc5ec \uc608\uc678\ub97c \ub358\uc9c8 \uc218 \uc788\ub2e4. \\n\uc608\ub97c \ub4e4\uc5b4 \uc0ac\uc6a9\uc790 \uc815\ubcf4\uac00 \uc788\ub294 \uc800\uc7a5\uc18c\uc5d0 \ucc3e\ub294 \uc0ac\uc6a9\uc790\uac00 \uc5c6\ub294 \uacbd\uc6b0 \uc544\ub798\uc640 \uac19\uc774 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n```kotlin\\nuserRepository.findByName(name) ?: throw IllegalArgumentException()\\n```\\n\\n### `!!` \ub110 \uc544\ub2d8 \ub2e8\uc5b8 \uc5f0\uc0b0\uc790\\n\\n!! \uc5f0\uc0b0\uc790\ub97c \uc774\uc6a9\ud55c\ub2e4\uba74 \uac15\uc81c\ub85c \uc5b4\ub5a4 \uac12\uc774\ub4e0 non-nullable \ud0c0\uc785\uc73c\ub85c \ubcc0\uacbd\ud560 \uc218 \uc788\ub2e4. \\n\ud558\uc9c0\ub9cc null\uc778 \uac12\uc5d0 \uc0ac\uc6a9\ud55c\ub2e4\uba74 NPE\uac00 \ubc1c\uc0dd\ud558\uac8c \ub41c\ub2e4. \\n\uc77c\ubc18\uc801\uc778 \uacbd\uc6b0\uc5d0\ub294 !! \uc5f0\uc0b0\uc790\ub97c \uc0ac\uc6a9\ud558\ub294 \uac83\uc740 \uc704\ud5d8\ud558\ub2e4. \\n\uc0ac\uc6a9\ud558\uae30 \uc27d\uc9c0\ub9cc, \ub9ac\uc2a4\ud06c\uac00 \ud06c\uace0 \ud639\uc2dc\ub098 \ud574\ub2f9 \uac12\uc774 \ucd94\ud6c4\uc5d0\ub294 null\uc774 \ub420 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \uc9c0\uc591\ud574\uc57c \ub41c\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4.\\n\\n```kotlin\\nval length: Int = word!!.length\\n```\\n\\n### `as?` \uc548\uc804\ud55c \uce90\uc2a4\ud305\\n\\n\ud0c0\uc785 \ubcc0\ud658\uc744 \ud560 \ub54c \uc9c0\uc815\ud55c \ud0c0\uc785\uc73c\ub85c \ubcc0\uacbd\ud560 \uc218 \uc5c6\ub2e4\uba74 `ClassCastException`\uc774 \ubc1c\uc0dd\ud55c\ub2e4. \\n\ucf54\ud2c0\ub9b0\uc5d0\uc11c\ub294 as \ub4a4\uc5d0 ?\ub97c \ubd99\uc5ec \uc548\uc804\ud558\uac8c \ud0c0\uc785 \ubcc0\ud658\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\ub530\ub77c\uc11c \ubbf8\ub9ac \ubcc0\ud658 \uac00\ub2a5\ud55c \ud0c0\uc785\uc778\uc9c0 \ud655\uc778\ud558\uc9c0 \uc54a\uace0, \uc548\uc804\ud558\uac8c \ud0c0\uc785\uc744 \ubcc0\ud658 \ud560 \uc218 \uc788\ub2e4. \\n\\n\ud0c0\uc785 \ubcc0\ud658\uc774 \ubd88\uac00\ub2a5 \ud560 \uacbd\uc6b0 \uc608\uc678\ub97c \ubc1c\uc0dd\uc2dc\ud0a4\uc9c0 \uc54a\uace0 null\uc744 \ubc18\ud658\ud55c\ub2e4.\\n\\n```kotlin\\nval value: Int? = something as? Int\\n```\\n\\n### List\uc5d0\uc11c\uc758 null \ucc98\ub9ac\\n\\nList\uc5d0\ub294 null\uc774 \uc544\ub2cc \uac12\ub9cc \ubc18\ud658\ud558\ub294 `filterNotNull` \uc720\ud2f8\ub9ac\ud2f0 \uba54\uc11c\ub4dc\ub97c \uc81c\uacf5\ud55c\ub2e4.\\n\\n```kotlin\\nval foodsWithNull: List = listOf(\\"Pizza\\", \\"Cheese\\", null, \\"Potato\\")\\nval foods = foodsWithNull.filterNotNull()\\n```\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n- [Kotlin in Action](https://product.kyobobook.co.kr/detail/S000001804588)\\n- [Effective Kotlin Item 8](https://product.kyobobook.co.kr/detail/S000001033129)\\n- [Comprehensive Guide to Null Safety in Kotlin](https://www.baeldung.com/kotlin/null-safety)\\n- [Kotlin NullSafety](https://kotlinlang.org/docs/null-safety.html)"},{"id":"jsr-310","metadata":{"permalink":"/jsr-310","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-08-JSR-310.mdx","source":"@site/blog/2023-1/2023-01-08-JSR-310.mdx","title":"JSR-310","description":"\uc774\uc804\uc5d0 \ub9ce\uc740 \ubb38\uc81c\uac00 \uc788\ub358 \uc790\ubc14\uc758 \ud074\ub798\uc2a4(Calendar, Date)\ub97c \ub300\uccb4\ud558\ub294 \ub0a0\uc9dc\uc640 \uc2dc\uac04 API","date":"2023-01-08T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 8\uc77c","tags":[{"label":"Java","permalink":"/tags/java"},{"label":"Time","permalink":"/tags/time"}],"readingTime":1.685,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"JSR-310","slug":"jsr-310","tags":["Java","Time"]},"prevItem":{"title":"Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95","permalink":"/kotlin-null"},"nextItem":{"title":"[\ucc45] \uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574","permalink":"/the-essence-of-object-orientation"}},"content":"\uc774\uc804\uc5d0 \ub9ce\uc740 \ubb38\uc81c\uac00 \uc788\ub358 \uc790\ubc14\uc758 \ud074\ub798\uc2a4(Calendar, Date)\ub97c \ub300\uccb4\ud558\ub294 \ub0a0\uc9dc\uc640 \uc2dc\uac04 API \\nISO-8601\uc744 \uae30\ubc18\uc73c\ub85c \uc791\uc131 \\n\uc124\uacc4 \ubaa9\ud45c \u2192 \ubd88\ubcc0, Fluent API, \uba85\ud655\ud558\uace0 \uba85\uc2dc\uc801, \ud655\uc7a5 \uac00\ub2a5\uc131\\n\\n:::note ISO-8601\\n\\n\ub0a0\uc9dc\uc640 \uc2dc\uac04\uc5d0 \uad00\ub828\ub41c \ub370\uc774\ud130\ub97c \ub2e4\ub8e8\ub294 \uad6d\uc81c \ud45c\uc900\\n\\n:::\\n\\n### LocalDate, LocalTime, LocalDateTime\\n\\n\ub0a0\uc9dc\uc640 \uc2dc\uac04\uc744 \ud45c\ud604\ud558\ub294 \ud074\ub798\uc2a4\\n\\n### Instant\\n\\n\uc720\ub2c9\uc2a4 \uc2dc\uac04(1970-01-01, 00:00:00 UTC) \uae30\uc900\uc73c\ub85c \ud2b9\uc815 \uc9c0\uc810\uae4c\uc9c0\uc758 \uc2dc\uac04\uc744 \ucd08\ub85c \ud45c\ud604\ud558\ub294 \ud074\ub798\uc2a4 \\n\uae30\uacc4\uc758 \uad00\uc810\uc5d0\uc11c \uc2dc\uac04 \ud45c\ud604\\n\\n### Duration, Period\\n\\n\uac04\uaca9\uc744 \ud45c\ud604\ud558\ub294 \ud074\ub798\uc2a4\\n\\n### TemporalAdjusters\\n\\n\ubcf5\uc7a1\ud55c \ub0a0\uc9dc \uc870\uc815\uc774 \ud544\uc694\ud560 \ub54c \uc0ac\uc6a9 \\n\ud544\uc694\ud55c \uacbd\uc6b0 \ub2e4\uc74c \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud558\uc5ec \ucee4\uc2a4\ud140 TemporalAdjuster\ub97c \uad6c\ud604 \uac00\ub2a5\\n\\n```java\\n@FunctionalInterface\\npublic interface TemporalAdjuster {\\n Temporal adjustInto(Temporal temporal);\\n}\\n```\\n\\n### DateTimeFormatter\\n\\n\ub0a0\uc9dc\uc640 \uc2dc\uac04 \ud3ec\ub9f7 \ud074\ub798\uc2a4 \\n\ud2b9\uc815 \ub0a0\uc9dc \ud328\ud134\uc774\ub098, DateTimeFormatterBuilder\ub97c \uc774\uc6a9\ud574\uc11c \ucee4\uc2a4\ud140\ud55c \ud3ec\ub9f7\uc744 \uc0dd\uc131 \uac00\ub2a5\\n\\n### ZoneId, ZoneOffset\\n\\nZoneId\ub294 \uc9c0\uc5ed ID\ub294 `\u2018\uc9c0\uc5ed/\ub3c4\uc2dc\u2019` \ud615\uc2dd, ZoneOffset\uc740 \uc2dc\ucc28 UTC \uae30\uc900 \uace0\uc815\ub41c \uc2dc\uac04 \ucc28\uc774 \uc774\uc6a9 \\nZoneId\uc758 \uacbd\uc6b0 IANA Time Zone Database\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uc9c0\uc5ed \uc9d1\ud569 \uc815\ubcf4 \uc0ac\uc6a9\\n\\n```java\\nInstant instant = Instant.now();\\nLocalDateTime utc = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);\\n```\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n- [\ubaa8\ub358 \uc790\ubc14 \uc778 \uc561\uc158](https://product.kyobobook.co.kr/detail/S000001810171)\\n- [Java\uc758 \ub0a0\uc9dc\uc640 \uc2dc\uac04 API](https://d2.naver.com/helloworld/645609)\\n- [ISO-8601](https://www.w3.org/TR/NOTE-datetime)\\n- [JSR-310 Spec](https://download.oracle.com/otn-pub/jcp/date_time-0.2-edr-oth-JSpec/date_time-0_2-edr-spec.pdf?AuthParam=1673171124_74a718be92efe4911c6977c02965aff4)\\n- [Temporal Adjuster](https://www.baeldung.com/java-temporal-adjuster)\\n- [DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)"},{"id":"the-essence-of-object-orientation","metadata":{"permalink":"/the-essence-of-object-orientation","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-07-\uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574.mdx","source":"@site/blog/2023-1/2023-01-07-\uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574.mdx","title":"[\ucc45] \uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574","description":"\ucc45 \uc815\ubcf4","date":"2023-01-07T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 7\uc77c","tags":[{"label":"Book","permalink":"/tags/book"}],"readingTime":5.415,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ucc45] \uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574","slug":"the-essence-of-object-orientation","tags":["Book"]},"prevItem":{"title":"JSR-310","permalink":"/jsr-310"},"nextItem":{"title":"2022\ub144 \ud68c\uace0","permalink":"/2022-retrospective"}},"content":"### \ucc45 \uc815\ubcf4\\n\\n> \uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574 \\n> \uc870\uc601\ud638\\n> \\n\\n### \uc77d\uace0 \ub098\uc11c\\n\\n\uc870\uc601\ud638\ub2d8\uc758 \uc624\ube0c\uc81d\ud2b8\ub97c \uc77d\uace0 \ub098\uc11c \ub2e4\uc2dc \ud55c \ubc88 \uc77d\uc5b4\ubcf4\uc558\ub2e4. \\n\uc544\uc9c1 \uc774\ud574\uac00 \uc548\ub418\ub294 \ubd80\ubd84\uc774 \ub9ce\uc9c0\ub9cc, \uadf8\ub798\ub3c4 \ud56d\uc0c1 \uc0c8\ub85c\uc6c0\uc744 \ub290\ub080\ub2e4. \\n\ub354\ud560 \ub098\uc704 \uc5c6\uc774 \ud73c\ub96d\ud55c \uac1d\uccb4\uc9c0\ud5a5 \ucc45\uc774\uace0, \uc870\uae08 \ub354 \uacf5\ubd80\ud558\uace0 \ub2e4\uc2dc \uc77d\uc5b4\ubd10\uc57c\ub420 \uac83 \uac19\ub2e4. \\n\\n\ucee4\ud53c \uc804\ubb38\uc810, \uc9c0\ud558\ucca0 \ub178\uc120\ub3c4, \uc774\uc0c1\ud55c \ub098\ub77c\uc758 \uc5d8\ub9ac\uc2a4\ub97c \uc608\uc2dc\ub85c \ub4e0 \uc124\uba85\uc774 \ub108\ubb34 \uc88b\uc558\uace0 \\n\uc88b\uc740 \ub0b4\uc6a9\uc744 \ub2f4\uace0 \uc788\uc9c0\ub9cc \uadf8\ub807\ub2e4\uace0 \ub108\ubb34 \ubb34\uac81\uc9c0 \uc54a\uc544 \uac00\ubccd\uac8c \uc77d\uae30\ub3c4 \uc88b\uc740 \uac83 \uac19\ub2e4.\\n\\n### \ucc45\uc784\uc758 \uc790\uc728\uc131\uc744 \uac15\uc870\ud558\ub294 \uc774\uc720 p.173\\n\\n\ud611\ub825\uc744 \ub2e8\uc21c\ud558\uac8c \ub9cc\ub4e0\ub2e4.\\n\\n- \uc758\ub3c4\ub97c \uba85\ud655\ud558\uac8c \ud45c\ud604 \u2192 \ud611\ub825\uc758 \ubcf5\uc7a1\ud568 \uc800\ud558\\n- \ucc45\uc784\uc758 \ucd94\uc0c1\ud654\\n\\n\uc678\ubd80\uc640 \ub0b4\ubd80\ub97c \uba85\ud655\ud558\uac8c \ubd84\ub9ac\ud55c\ub2e4.\\n\\n- \uc694\uccad\ud558\ub294 \uac1d\uccb4\uac00 \ubab0\ub77c\ub3c4 \ub418\ub294 \ubd80\ubd84\uc774 \ucea1\uc290\ud654\ub428\uc73c\ub85c \uc778\ud130\ud398\uc774\uc2a4\uc640 \uad6c\ud604\uc758 \ubd84\ub9ac\\n\\n\ucc45\uc784\uc744 \uc218\ud589\ud558\ub294 \ub0b4\ubd80\uc801\uc778 \ubc29\ubc95\uc744 \ubcc0\uacbd\ud558\ub354\ub77c\ub3c4 \uc678\ubd80\uc5d0 \uc601\ud5a5\uc744 \ubbf8\uce58\uc9c0 \uc54a\ub294\ub2e4.\\n\\n- \ubcc0\uacbd\uc758 \ud30c\uae09\ud6a8\uacfc\ub97c \uac1d\uccb4 \ub0b4\ubd80\ub85c \ucea1\uc290\ud654 \u2192 \uba54\uc2dc\uc9c0\ub97c \ubcf4\ub0b4\ub294 \uac1d\uccb4\uc640\uc758 \uacb0\ud569\ub3c4 \uc800\ud558\\n\\n\ud611\ub825\uc758 \ub300\uc0c1\uc744 \ub2e4\uc591\ud558\uac8c \uc120\ud0dd\ud560 \uc218 \uc788\ub294 \uc720\uc5f0\uc131\uc744 \uc81c\uacf5\ud55c\ub2e4.\\n\\n- \uc720\uc5f0\ud55c \uc124\uacc4 \u2192 \uc7ac\uc0ac\uc6a9\uc131 \uc99d\uac00\\n\\n\uac1d\uccb4\uc758 \uc5ed\ud560\uc744 \uc774\ud574\ud558\uae30 \uc26c\uc6cc\uc9c4\ub2e4.\\n\\n- \uc751\uc9d1\ub3c4\ub97c \ub192\uc740 \uc0c1\ud0dc\ub85c \uc720\uc9c0\\n\\n### \ubc11\uc904 \uce5c \ubb38\uc7a5\ub4e4\\n\\n> \uac1d\uccb4\uc9c0\ud5a5\uc758 \ubaa9\ud45c\ub294 \uc2e4\uc138\uacc4\ub97c \ubaa8\ubc29\ud558\ub294 \uac83\uc774 \uc544\ub2c8\ub2e4.\\n\uc624\ud788\ub824 \uc0c8\ub85c\uc6b4 \uc138\uacc4\ub97c \ucc3d\uc870\ud558\ub294 \uac83\uc774\ub2e4.\\n\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uac1c\ubc1c\uc790\uc758 \uc5ed\ud560\uc740 \ub2e8\uc21c\ud788 \uc2e4\uc138\uacc4\ub97c \uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc548\uc73c\ub85c \uc62e\uaca8 \ub2f4\ub294 \uac83\uc774 \uc544\ub2c8\ub77c \uace0\uac1d\uacfc \uc0ac\uc6a9\uc790\ub97c \ub9cc\uc871\uc2dc\ud0ac \uc218 \uc788\ub294 \uc2e0\uc138\uacc4\ub97c \ucc3d\uc870\ud558\ub294 \uac83\uc774\ub2e4.\\np.21\\n> \\n\\n> \uacfc\uac70\uc758 \uc804\ud1b5\uc801\uc778 \uac1c\ubc1c \ubc29\ubc95\uc740 \ub370\uc774\ud130\uc640 \ud504\ub85c\uc138\uc2a4\ub97c \uc5c4\uaca9\ud558\uac8c \uad6c\ubd84\ud55c\ub2e4.\\n\uc774\uc5d0 \ubc18\ud574 \uac1d\uccb4\uc9c0\ud5a5\uc5d0\uc11c\ub294 \ub370\uc774\ud130\uc640 \ud504\ub85c\uc138\uc2a4\ub97c \uac1d\uccb4\ub77c\ub294 \ud558\ub098\uc758 \ud2c0 \uc548\uc5d0 \ud568\uaed8 \ubb36\uc5b4 \ub193\uc74c\uc73c\ub85c\uc368 \uac1d\uccb4\uc758 \uc790\uc728\uc131\uc744 \ubcf4\uc7a5\ud55c\ub2e4.\\n\uc790\uc728\uc801\uc778 \uac1d\uccb4\ub85c \uad6c\uc131\ub41c \uacf5\ub3d9\uccb4\ub294 \uc720\uc9c0 \ubcf4\uc218\uac00 \uc27d\uace0 \uc7ac\uc0ac\uc6a9\uc774 \uc6a9\uc774\ud55c \uc2dc\uc2a4\ud15c\uc744 \uad6c\ucd95\ud560 \uc218 \uc788\ub294 \uac00\ub2a5\uc131\uc744 \uc81c\uc2dc\ud55c\ub2e4.\\np.33\\n> \\n\\n> **\uac1d\uccb4\uc9c0\ud5a5\uc758 \ubcf8\uc9c8**\\n> \\n> \\n> \uc2dc\uc2a4\ud15c\uc744 \uc0c1\ud638\uc791\uc6a9\ud558\ub294 \uc790\uc728\uc801\uc778 \uac1d\uccb4\ub4e4\uc758 \uacf5\ub3d9\uccb4\ub85c \ubc14\ub77c\ubcf4\uace0 \uac1d\uccb4\ub97c \uc774\uc6a9\ud574 \uc2dc\uc2a4\ud15c\uc744 \ubd84\ud560\ud558\ub294 \ubc29\ubc95\\n> \\n> \uc790\uc728\uc801\uc778 \uac1d\uccb4\ub780 \uc0c1\ud0dc\uc640 \ud589\uc704\ub97c \ud568\uaed8 \uc9c0\ub2c8\uba70 \uc2a4\uc2a4\ub85c \uc790\uae30 \uc790\uc2e0\uc744 \ucc45\uc784\uc9c0\ub294 \uac1d\uccb4\ub97c \uc758\ubbf8\ud55c\ub2e4.\\n> \\n> \uac1d\uccb4\ub294 \uc2dc\uc2a4\ud15c\uc758 \ud589\uc704\ub97c \uad6c\ud604\ud558\uae30 \uc704\ud574 \ub2e4\ub978 \uac1d\uccb4\uc640 \ud611\ub825\ud55c\ub2e4. \uac01 \uac1d\uccb4\ub294 \ud611\ub825 \ub0b4\uc5d0\uc11c \uc815\ud574\uc9c4 \uc5ed\ud560\uc744 \uc218\ud589\ud558\uba70 \uc5ed\ud560\uc740 \uad00\ub828\ub41c \ucc45\uc784\uc758 \uc9d1\ud569\uc774\ub2e4.\\n> \\n> \uac1d\uccb4\ub294 \ub2e4\ub978 \uac1d\uccb4\uc640 \ud611\ub825\ud558\uae30 \uc704\ud574 \uba54\uc2dc\uc9c0\ub97c \uc804\uc1a1\ud558\uace0, \uba54\uc2dc\uc9c0\ub97c \uc218\uc2e0\ud55c \uac1d\uccb4\ub294 \uba54\uc2dc\uc9c0\ub97c \ucc98\ub9ac\ud558\ub294 \ub370 \uc801\ud569\ud55c \uba54\uc11c\ub4dc\ub97c \uc790\uc728\uc801\uc73c\ub85c \uc120\ud0dd\ud55c\ub2e4.\\n> p.35\\n> \\n\\n> \ud074\ub798\uc2a4\uc758 \uad6c\uc870\uc640 \uba54\uc11c\ub4dc\uac00 \uc544\ub2c8\ub77c \uac1d\uccb4\uc758 \uc5ed\ud560, \ucc45\uc784, \ud611\ub825\uc5d0 \uc9d1\uc911\ud558\ub77c.\\n\uac1d\uccb4\uc9c0\ud5a5\uc740 \uac1d\uccb4\ub97c \uc9c0\ud5a5\ud558\ub294 \uac83\uc774\uc9c0 \ud074\ub798\uc2a4\ub97c \uc9c0\ud5a5\ud558\ub294 \uac83\uc774 \uc544\ub2c8\ub2e4.\\np.38\\n> \\n\\n> \uac1d\uccb4\uc9c0\ud5a5\uc5d0\uc11c \uc911\uc694\ud55c \uac83\uc740 \ub3d9\uc801\uc73c\ub85c \ubcc0\ud558\ub294 \uac1d\uccb4\uc758 \u2018\uc0c1\ud0dc\u2019\uc640 \uc0c1\ud0dc\ub97c \ubcc0\uacbd\ud558\ub294 \u2018\ud589\uc704\u2019\ub2e4.\\n\ud074\ub798\uc2a4\ub294 \ud0c0\uc785\uc744 \uad6c\ud604\ud558\uae30 \uc704\ud574 \ud504\ub85c\uadf8\ub798\ubc0d \uc5b8\uc5b4\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uad6c\ud604 \uba54\ucee4\ub2c8\uc998\uc774\ub77c\ub294 \uc0ac\uc2e4\uc744 \uae30\uc5b5\ud558\ub77c.\\np.105\\n> \\n\\n> \ucc45\uc784 \uc8fc\ub3c4 \uc124\uacc4\uc758 \ud575\uc2ec\uc740 \uc5b4\ub5a4 \ud589\uc704\uac00 \ud544\uc694\ud55c\uc9c0\ub97c \uba3c\uc800 \uacb0\uc815\ud55c \ud6c4\uc5d0 \uc774 \ud589\uc704\ub97c \uc218\ud589\ud560 \uac1d\uccb4\ub97c \uacb0\uc815\ud558\ub294 \uac83\uc774\ub2e4.\\n\uc774 \uacfc\uc815\uc744 \ud754\ud788 What/Who \uc0ac\uc774\ud074\uc774\ub77c\uace0 \ud55c\ub2e4.\\n\u2019\uc5b4\ub5a4 \ud589\uc704(What)\u2019\ub97c \uc218\ud589\ud560 \uac83\uc778\uc9c0 \uacb0\uc815\ud55c \ud6c4 \u2018\ub204\uac00(who)\u2019 \uadf8 \ud589\uc704\ub97c \uc218\ud589\ud560 \uac83\uc778\uc9c0 \uacb0\uc815\ud574\uc57c \ud55c\ub2e4.\\n\uc5ec\uae30\uc11c \u2018\uc5b4\ub5a4 \ud589\uc704\u2019\uac00 \ubc14\ub85c \uba54\uc2dc\uc9c0\ub2e4.\\np.158\\n>"},{"id":"2022-retrospective","metadata":{"permalink":"/2022-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-02-2022\ub144 \ud68c\uace0.mdx","source":"@site/blog/2023-1/2023-01-02-2022\ub144 \ud68c\uace0.mdx","title":"2022\ub144 \ud68c\uace0","description":"\uc801\ub2f9\ud55c \uc804\ud658\uc810, 2022\ub144\uc744 \ub3cc\uc544\ubcf4\uba70","date":"2023-01-02T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 2\uc77c","tags":[{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":3.705,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"2022\ub144 \ud68c\uace0","slug":"2022-retrospective","tags":["Retrospective"]},"prevItem":{"title":"[\ucc45] \uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574","permalink":"/the-essence-of-object-orientation"},"nextItem":{"title":"[\ucc45] \uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.","permalink":"/book-writer"}},"content":"\uc801\ub2f9\ud55c \uc804\ud658\uc810, 2022\ub144\uc744 \ub3cc\uc544\ubcf4\uba70 \\n\\n### \uc804\uc5ed\\n\\n\uc57d 1\ub144 6\uac1c\uc6d4\uac04\uc758 \uacf5\uad70 \uc815\ubcf4\ubcf4\ud638\ubcd1 \uc0dd\ud65c\uc744 \ub9c8\uce58\uace0 \uc804\uc5ed\uc744 \ud588\ub2e4. \\n\uc870\uae30 \uc804\uc5ed \ub54c\ubb38\uc5d0 2021\ub144 12\uc6d4\uc5d0 \ub098\uc654\uc9c0\ub9cc, \uc2e4\uc81c \uc804\uc5ed \ub0a0\uc9dc\ub294 2022\ub144\uc774\ub2c8 \ud68c\uace0\uc5d0 \uc801\uc5b4\ub3c4 \uc0c1\uad00\uc5c6\uaca0\uc9c0. \\n\\n\uc870\uae08 \ub354 \ubbf8\ub798\uc5d0 \ub300\ud55c \uc0dd\uac01\uc744 \ud574\ubcfc\uac78 \uadf8\ub7ac\ub2e4. \\n\uc804\uc5ed\uc744 \ud588\uc9c0\ub9cc \ubb50 \ud558\ub098 \uc81c\ub300\ub85c \ud560 \uc904 \uc544\ub294 \uac83\ub3c4 \uc5c6\uc73c\ub2c8 \ub113\uc740 \ubc14\ub2f7\uc18d\uc5d0 \ub369\uadf8\ub7ec\ub2c8 \ub193\uc544\uc9c4 \uae30\ubd84\uc774 \uad1c\ud788 \ub4e4\uc5c8\uc5c8\ub2e4. \\n\uc77c\ucc0d \uc0dd\uac01\uc744 \uc815\ub9ac\ud558\uc5ec \ubc29\ud5a5\uc744 \uc7a1\uc9c0 \ubabb\ud588\uae30\uc5d0 \uc544\uc26c\uc6c0\uc774 \ub9ce\uc774 \ub0a8\uc558\ub2e4. \\n\\n### \uc790\ubc14\\n\\n\uc804\uc5ed\uc744 \ud558\uace0 \uc9c4\ub85c\ub97c \uace0\ubbfc\ud558\ub2e4 \ud5a5\ub85c\ub2d8\uc758 [\uc790\ubc14 \uacf5\ud654\uad6d](https://jojoldu.tistory.com/609) \ud3ec\uc2a4\ud305\uc744 \uc77d\uace0 \ub098\uc11c \uc790\ubc14 \uacf5\ubd80\ub97c \uc2dc\uc791\ud588\ub2e4. \\n\uc720\uba85\ud55c \uc778\ud504\ub7f0\uc758 \uae40\uc601\ud55c\ub2d8\uc758 \uc2a4\ud504\ub9c1 \uac15\uc758\ub3c4 \uc788\uace0, \uc88b\uc740 \uc790\ubc14 \uac1c\ubc1c \uc11c\uc801\uc774 \ub9ce\uc544\uc11c \ub3c5\ud559\ud558\uae30\ub85c \uacb0\uc815\ud588\ub2e4. \\n\ud558\ub2e4 \ubcf4\ub2c8 \uc790\ubc14\uc640 \uc2a4\ud504\ub9c1\uc744 \uacf5\ubd80\ud558\uba74\uc11c \u201c\uc65c \uc9c4\uc791\ud558\uc9c0 \uc54a\uc558\uc9c0\u201d\ub77c\ub294 \uc0dd\uac01\ub3c4 \ub9ce\uc774 \ub4e4\uc5c8\ub2e4. \\n\uc591\uc9c8\uc758 \uc790\ub8cc\ub3c4 \ub9ce\uc558\uae30 \ub54c\ubb38\uc5d0, \uc608\uc804\uc5d0 \ub178\ub4dc\ub85c \uac1c\ubc1c\ud588\uc744 \ub54c \ud480\uc9c0 \ubabb\ud588\ub358 \ub2f5\ub2f5\ud568\uc744 \ub9ce\uc774 \ud574\uc18c\ud588\ub358 \uac83 \uac19\ub2e4.\\n\\n23\ub144\uc5d0\ub294 \uc870\uae08 \ub354 \uae4a\uac8c \uc790\ubc14\ub97c \uacf5\ubd80\ud574\ubcfc \uc0dd\uac01\uc774\ub2e4. \\n\uc5b8\uc5b4\ub97c \ud558\ub098 \uae4a\uac8c \uacf5\ubd80\ud558\ub294 \uac74 \ub9ce\uc740 \ub3c4\uc6c0\uc774 \ub418\ub294 \uac83 \uac19\ub2e4.\\n\\n### \uc2a4\ud130\ub514\\n\\n\uae40\uc601\ud55c\ub2d8\uc758 \uac15\uc758\ub97c \uac70\uc758 \ub2e4 \ub4e4\uc5c8\uc744 \ub54c\ucbe4, \ud56d\uc0c1 \uac15\uc758\uc5d0\uc11c \uc5b8\uae09\ub418\ub294 \ud1a0\ube44\uc758 \uc2a4\ud504\ub9c1\uc744 \uc77d\uc5b4\ubcf4\uace0 \uc2f6\uc5b4\uc84c\uace0 \\n\ud63c\uc790 \uacf5\ubd80\ud558\uae30\uc5d0\ub294 \ub3d9\uae30\ubd80\uc5ec\ub3c4 \ubd80\uc871\ud588\uae30 \ub54c\ubb38\uc5d0 \uc2a4\ud130\ub514\ub97c \uc2dc\uc791\ud588\ub2e4. \\n\ub2e4\ub978 \uc0ac\ub78c\uc5d0\uac8c \uc124\uba85\uc744 \ud574\uc57c \ud588\uae30 \ub54c\ubb38\uc5d0 \ub354\uc6b1 \uaf3c\uaf3c\ud558\uac8c \uacf5\ubd80\ub97c \ud560 \uc218 \uc788\uc5b4\uc11c \uc88b\uc558\uc9c0\ub9cc \ub098\uc5d0\uac8c\ub294 \ub0b4\uc6a9\uc774 \uaf64\ub098 \uc5b4\ub824\uc6cc\uc11c \uc2dc\uac04\uc744 \ub9ce\uc774 \uc18c\ube44\ud588\ub2e4. \\n\uac19\uc774 \uc2a4\ud130\ub514\ud558\uc2dc\ub294 \ubd84\uacfc 7\uac1c\uc6d4 \ub3d9\uc548 \uc2a4\ud130\ub514\ub97c \uafb8\uc900\ud788 \uc774\uc5b4\ub098\uac00 \ucd1d 3\uad8c\uc758 \ucc45\uc744 \uc77d\uc744 \uc218 \uc788\uc5c8\ub2e4.\\n\\n### \uc6b0\uc544\ud55c \ud14c\ud06c\ucf54\uc2a4\\n\\n\uad70 \ubcf5\ubb34 \uc911\uc77c \ub54c \uc9c0\uc6d0\ud588\ub2e4 \ub5a8\uc5b4\uc9c4 \uc6b0\uc544\ud55c \ud14c\ud06c\ucf54\uc2a4\ub97c \ub2e4\uc2dc \uc9c0\uc6d0\ud588\ub2e4. \\n\uc774\ubc88 \uc5f0\ub3c4\uc5d0 \ucde8\uc5c5\uc744 \ud558\ub294 \uac8c \ubaa9\ud45c\uc600\uc9c0\ub9cc \ub0b4\uac00 \uac00\uc9c0\uace0 \uc788\ub294 \ud2b9\ubcc4\ud55c \ubb34\uae30\uac00 \uc5c6\ub2e4\ub294 \uac78 \uae68\ub2ec\uc558\ub2e4. \\n\uc801\uc9c0 \uc54a\uc740 \uc2dc\uac04\uc744 \ud22c\uc790\ud574 \uc900\ube44\ub97c \ud588\uace0, \uac10\uc0ac\ud558\uac8c\ub3c4 \uc774\ubc88\uc5d0\ub294 \ucd5c\uc885 \ud569\uaca9\uc744 \ud588\ub2e4. \\n\\n\ub09c \uc0ac\ub78c\ub4e4\uacfc \uc18c\ud1b5\ud558\uace0, \ud611\uc5c5\ud558\ub294 \ub2a5\ub825\uc774 \ubd80\uc871\ud558\ub2e4\uace0 \uc0dd\uac01\uc744 \ub9ce\uc774 \ud588\ub2e4. \\n\uc6b0\uc544\ud55c \ud14c\ud06c\ucf54\uc2a4\ub97c \ud1b5\ud574 \uadf8 \ube48 \ubd80\ubd84\uc744 \ucc44\uc6b0\ub3c4\ub85d \ub178\ub825\ud574\uc57c\uaca0\ub2e4. \\n\\n### 2023\ub144\uc5d0\ub294\\n\\n\ub9c8\uc74c\uc758 \uc5ec\uc720\uac00 \uc5c6\uc5c8\ub358 2022\ub144\uc774\uc5c8\ub358 \uac83 \uac19\ub2e4. \\n\ud558\uace0 \uc2f6\uc740 \uac74 \ub9ce\uc9c0\ub9cc, \uc774\ubc88\uc5d0\ub294 \uc5ec\uc720\ub97c \uac00\uc9c0\uace0 \ud560 \uc218 \uc788\ub294 \uac83\uc5d0 \ucd5c\uc120\uc744 \ub2e4\ud574\uc57c\uaca0\ub2e4."},{"id":"book-writer","metadata":{"permalink":"/book-writer","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-01-\uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.mdx","source":"@site/blog/2023-1/2023-01-01-\uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.mdx","title":"[\ucc45] \uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.","description":"\ucc45 \uc815\ubcf4","date":"2023-01-01T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 1\uc77c","tags":[{"label":"Book","permalink":"/tags/book"}],"readingTime":4.425,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ucc45] \uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.","slug":"book-writer","tags":["Book"]},"prevItem":{"title":"2022\ub144 \ud68c\uace0","permalink":"/2022-retrospective"}},"content":"### \ucc45 \uc815\ubcf4\\n\\n> \uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \\n> \ubc15\uc194\ubbf8 \\n>\\n\\n### \uc77d\uace0 \ub098\uc11c\\n\\n\uc800\uc790\uc758 \uacbd\ud5d8\uacfc \ud568\uaed8 \uae00\uc4f0\uae30\uc5d0 \ub300\ud55c \uac00\ubcbc\uc6b4 \uc870\uc5b8\uc774 \ub2f4\uaca8\uc788\uc5b4 \uac00\ubccd\uac8c \uc77d\uae30 \uc88b\uc558\ub2e4. \\n\uae00\uc744 \uc798 \uc791\uc131\ud574 \ubcf4\uace0 \uc2f6\uc744 \ub54c \uc801\uc6a9\ud574 \ubcfc \uc218 \uc788\ub294 \uc815\ubcf4\uac00 \ub9ce\uc544\uc11c \ub3c4\uc6c0\uc774 \ub418\uc5c8\ub2e4. \\n\\n\uc6b0\uc544\ud55c \ud14c\ud06c\ucf54\uc2a4\uc758 \ud504\ub9ac\ucf54\uc2a4\ub97c \uc9c4\ud589\ud560 \ub54c \ud6c4\uae30\ub97c \uc791\uc131\ud558\uace0 \ub098\uba74 \ud56d\uc0c1 \uae00\uc774 \ub531\ub531\ud558\ub2e4\ub294 \ub290\ub08c\uc744 \ubc1b\uc558\ub2e4. \\n\ub2e4\ub978 \uc9c0\uc6d0\uc790\ub4e4\uc758 \uc77d\uae30 \ud3b8\ud558\uace0, \ubc1d\uc740 \ub290\ub08c\uc744 \uc8fc\ub294 \uae00\uc744 \ubcf4\uba74 \ubd80\ub7ec\uc6b4 \ub9c8\uc74c\uc744 \uac00\uc9c0\uae30\ub3c4 \ud588\ub2e4. \\n\uc774 \ucc45\uc744 \uc77d\uc5c8\uc73c\ub2c8 2023\ub144\uc5d0\ub294 \uc870\uae08 \ub354 \uae00\uc744 \uc798 \uc801\uc5b4\ubcf4\ub824\uace0 \ud55c\ub2e4.\\n\\n### \ubc11\uc904 \uce5c \ubb38\uc7a5\ub4e4\\n\\n> \ubb38\uc7a5\uc774 \uc2ec\uc2ec\ud558\uace0 \uc9c0\ub8e8\ud558\ub2e4\uba74\\n\ub0b4\uc6a9\uc744 \uc77c\ubaa9\uc694\uc5f0\ud558\uac8c \uc815\ub9ac\ud588\uace0, \uae00\uc758 \uc758\ub3c4\ub3c4 \uc090\ub6a4\uc9c0 \uc54a\uace0, \ub2e8\uc5b4\ub3c4 \uc801\uc808\ud55c \uac83\uc73c\ub85c \uace8\ub790\ub294\ub370\u2026 \uadf8\ub7f0\ub370\ub3c4 \uc5b4\ub518\uac00\uac00 \uc2ec\uc2ec\ud558\uace0 \uc9c0\ub8e8\ud558\ub2e4\uba74? \ucd95\ucd95 \ucc98\uc9c0\uace0 \ub530\ubd84\ud558\ub2e4\uba74? \ub9d0\uaf2c\ub9ac\ub97c \ubaa8\uc870\ub9ac \u2018~\ub2e4\u2019\ub85c \ud1b5\uc77c\ud55c \uac74 \uc544\ub2cc\uc9c0 \uc810\uac80\ud574 \ubcf4\uc138\uc694.\\n> \\n\\n> \ub9d0\uaf2c\ub9ac\ub97c \uc798 \uac16\uace0 \ub180\uc544\uc57c \ud569\ub2c8\ub2e4. \ubb38\uc7a5\uc758 \ub9c8\uc9c0\ub9c9 \uae00\uc790\ub97c \ub9e4\ubc88 \ub2e4\ub974\uac8c \uace0\uccd0\uc4f0\ub294 \uac83\ub9cc\uc73c\ub85c\ub3c4 \uae00\uc5d0 \ud65c\uae30\ub97c \ub354\ud560 \uc218 \uc788\uc8e0. \ub54c\ub860 \ubb38\uc7a5\uc744 \ub2e4 \ub9c8\uce58\uc9c0 \uc54a\uace0, \ub2e8\uc5b4\ub85c\ub9cc \ub05d\ub9fa\ub294 \uac83\ub3c4 \ubc29\ubc95. \ubb38\uc7a5\uacfc \ubb38\uc7a5 \uc0ac\uc774\uc5d0 \uc27c\ud45c\uac00 \ub4e4\uc5b4\uc11c\uba70 \uae00 \uc804\uccb4\uc5d0 \ud65c\uae30\uac00 \ub3cc\uac8c \ub3fc\uc694. \ubb38\uc7a5\uc758 \uae38\uc774\ub3c4 \ub2e4\ucc44\ub85c\uc6cc\uc9c0\ub294 \ub355\ubd84\uc5d0 \ub364\uc73c\ub85c \uc5bb\uac8c \ub418\ub294 \uac83\ub3c4 \uc788\uc2b5\ub2c8\ub2e4. \ubc14\ub85c, \uae00\uc758 \ub9ac\ub4ec.\\n> \\n\\n> \uc774\uc804 \ubb38\uc7a5\uc5d0\uc11c \ub05d\ub09c \uae00\uc790\ub85c, \ub2e4\uc74c \ubb38\uc7a5\uc744 \ub05d\ub9fa\uc9c0 \uc54a\uae30. \ud55c\ub450 \ubb38\ub2e8\ub9c8\ub2e4 \ub2e8\uc5b4 \uc218\uc900\uc758 \uc544\uc8fc \uc9e7\uc740 \ubb38\uc7a5 \ubc30\uce58\ud558\uae30.\\n> \\n\\n> \uae00\uc758 \uc9c4\uc9dc \uc774\uc720, \uae00\uc758 \uc9c4\uc9dc \ubaa9\uc801, \uae00\uc758 \uc9c4\uc9dc \ub300\uc0c1\uc744 \ucc3e\uc73c\ub824\uace0 \uc560\uc37c\uc2b5\ub2c8\ub2e4. \uc9c0\uae08\ucc98\ub7fc \ud2c0\uc744 \ub5a0\uc62c\ub9b0\ub2e4\uac70\ub098, \ub208\uce58\ub97c \ubcf8\ub2e4\uac70\ub098, \uc815\uce58\uc801\uc778 \uc148\ub3c4 \ud558\uc9c0 \uc54a\uc558\uc5b4\uc694.\\n> \\n\\n> \uc81c\ubaa9\uc740 \uc9e7\uac8c, \ubcf4\uae30 \uc27d\uac8c, \uc77d\uae30 \uc27d\uac8c, \ubc1c\uc74c\uc774 \ube44\uc2b7\ud558\uac8c, \uc21c\uc11c\ub97c \ubc14\uafd4\uc11c\\n> \\n\\n> \uae00\uc744 \ub9c8\uc9c0\ub9c9\uc73c\ub85c \ub2e4\ub4ec\uc744 \ub54c, \ub178\ub798\uc5d0 \uac00\uae4c\uc6cc\uc9c8 \ubc29\ubc95\uc740 \uc5c6\uc744\uc9c0 \uace0\ubbfc\ud574\ubd05\ub2c8\ub2e4. \uac10\ud788 \uac00 \ub2ff\uc744 \uc218 \uc5c6\ub294 \ubaa9\ud45c\uc774\uaca0\uc9c0\ub9cc, \ud560 \uc218 \uc788\ub294 \ucd5c\uc18c\ud55c\uc758 \ub9ac\ub4ec\uc774\ub77c\ub3c4 \ubd99\uc5ec\uc8fc\uace0 \uc2f6\uc5b4\uc694.\\n> \\n\\n> \uc5ec\ub294 \ub9d0\uacfc \ub9c8\uc9c0\ub9c9 \ub9d0\uc5d0 \uc791\uc815\ud558\uace0 \ub9c8\uc74c\uc744 \ub2f4\ub294 \uc5f0\uc2b5\uc744 \ud574\ubd05\uc2dc\ub2e4. \uae00\uc758 \uc5b4\ub290 \uad6c\uc11d\uc774\ub77c\ub3c4 \ubed4\ud55c \uae00\uc790\ub294 \ub0a8\uae30\uc9c0 \uc54a\uaca0\ub178\ub77c \ub2e4\uc9d0\ud558\uba70 \uc368\ubcf4\ub294 \uac81\ub2c8\ub2e4. \ub098\ub9cc\uc774 \uac00\uc9c4 \uc720\uc77c\ud55c \uba54\uc2dc\uc9c0\uc5d0 \uc9d1\uc911\ud558\uba74\uc11c\uc694. \uadf8\ub7fc \uc0dd\uac01\uc774 \ub2ec\ub77c\uc9c0\uace0, \uace0\ub974\ub294 \ub2e8\uc5b4\ub3c4 \ub2ec\ub77c\uc9c0\uace0, \ub0a8\uae34 \ubb38\uc7a5\ub3c4 \ub2ec\ub77c\uc838\uc694. \uacb0\uad6d\uc5d0\ub294 \uae00\uc744 \uc4f4 \uc0ac\ub78c\uc778 \ub098 \uc790\uc2e0\ub3c4 \ub0a8\ub2ec\ub77c\uc9c8 \uac81\ub2c8\ub2e4.\\n> \\n\\n> \ub9de\ucda4\ubc95\uc740 \uc911\uc694\ud569\ub2c8\ub2e4. \ud558\uc9c0\ub9cc \ub9de\ucda4\ubc95\ubcf4\ub2e4 \ub354 \uc911\uc694\ud55c \uac74 \uac70\uae30\uc5d0 \ub2f4\uae34 \ub9c8\uc74c\uc785\ub2c8\ub2e4. \ub0b4 \ub9c8\uc74c\uc744 \uae00\uc5d0 \ub2f4\uc544 \uc2e4\uc5b4 \ubcf4\ub0b4\uae30 \uc804, \ub9de\ucda4\ubc95\uc744 \uc810\uac80\ud558\ub294 \uc774\uc720 \uc5ed\uc2dc \uadf8\uac81\ub2c8\ub2e4. \uc624\uc9c1 \ub0b4 \ub9c8\uc74c\uc774 \ub0a8\uc5d0\uac8c \uc77d\ud788\ub294 \ub3d9\uc548 \ubc29\ud574\uac00 \ub418\uc9c0 \uc54a\uae30\ub97c \ubc14\ub77c\uae30 \ub54c\ubb38\uc774\uc8e0. \ub0b4\uac00 \uc4f4 \uae00\ub3c4, \ub0a8\uc774 \uc4f4 \uae00\ub3c4. \uc5b8\uc81c\ub098 \uadf8 \uc548\uc5d0 \ub2f4\uae34 \ub9c8\uc74c\uc774 \uba3c\uc800\uc785\ub2c8\ub2e4.\\n> \\n\\n> \uae00\uc744 \uc4f4\ub2e4\uace0 \uae00\uc774 \uc644\uc131\ub418\ub294 \uac8c \uc544\ub2c8\uc5d0\uc694. \uae00\uacfc \ub2ee\uc740 \ubaa8\uc2b5\uc73c\ub85c \uc0b4 \ub54c, \uae00\uc740 \ube44\ub85c\uc18c \uc644\uc131\ub429\ub2c8\ub2e4.\\n>"}]}')}}]); \ No newline at end of file diff --git a/assets/js/b2b675dd.f512bfe6.js b/assets/js/b2b675dd.f512bfe6.js new file mode 100644 index 000000000..f16465215 --- /dev/null +++ b/assets/js/b2b675dd.f512bfe6.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[533],{28017:n=>{n.exports=JSON.parse('{"blogPosts":[{"id":"async-exception","metadata":{"permalink":"/async-exception","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac.mdx","source":"@site/blog/2023-3/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac.mdx","title":"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac","description":"\uac1c\uc694","date":"2023-09-18T00:00:00.000Z","formattedDate":"2023\ub144 9\uc6d4 18\uc77c","tags":[{"label":"async","permalink":"/tags/async"},{"label":"exception","permalink":"/tags/exception"}],"readingTime":3.15,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac","slug":"async-exception","tags":["async","exception"]},"nextItem":{"title":"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0","permalink":"/tomcat-retrospective"}},"content":"### \uac1c\uc694\\n\\n\ud604\uc7ac \ud2b8\ub9bd\ub4dc\ub85c\uc6b0\uc758 \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uae30\ub2a5\uc740 \ube44\ub3d9\uae30\ub85c \ucc98\ub9ac\ub418\uace0 \uc788\ub2e4. \ub85c\uadf8\ub97c \ud655\uc778\ud558\ub294 \ub3c4\uc911 `@Async`\uac00 \uc801\uc6a9\ub41c \uba54\uc11c\ub4dc\uc5d0\uc11c \uc608\uc678\uac00 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 \ub85c\uadf8\uac00 \uc815\uc0c1\uc801\uc73c\ub85c \ucd9c\ub825\ub418\uc9c0 \uc54a\ub294 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud588\ub2e4. \\n\\n\ud655\uc778\ud574 \ubcf4\ub2c8 Spring\uc758 `@ControllerAdvice` + `@ExceptionHandler`\uc758 \uacbd\uc6b0 \ub3d9\uae30 \uc608\uc678\ub9cc \ucc98\ub9ac\ud558\uace0, \ube44\ub3d9\uae30 \uc608\uc678\ub97c \ucc98\ub9ac\ud558\uc9c0 \uc54a\uc558\ub2e4. \ub530\ub77c\uc11c Spring\uc5d0\uc11c \uc9c0\uc6d0\ud574 \uc8fc\ub294 `AsyncUncaughtExceptionHandler` \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud574\uc11c \uc608\uc678\ub97c \ucc98\ub9ac\ud558\ub294 \ud074\ub798\uc2a4\ub97c \uc0dd\uc131\ud588\ub2e4. \\n\\n### \ube44\ub3d9\uae30 \uc608\uc678\ucc98\ub9ac\\n\\n```java title=AsyncExceptionHandler\\n@Slf4j\\npublic class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {\\n\\n private static final String LOG_FORMAT = \\"[%s] %s\\";\\n\\n @Override\\n public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {\\n log.info(String.format(LOG_FORMAT, MDC.get(REQUEST_ID.key()), throwable.getMessage()), throwable);\\n }\\n}\\n```\\n\\n\ud574\ub2f9 `AsyncExceptionHandler`\uc758 \uacbd\uc6b0 `AsyncConfigurer`\ub97c \uad6c\ud604\ud55c Configuration \ud074\ub798\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub4f1\ub85d\ud560 \uc218 \uc788\ub2e4. `getAsyncUncaughtExceptionHandler` \uba54\uc11c\ub4dc\ub97c \uc624\ubc84\ub77c\uc774\ub529\ud558\uc5ec \uc774\uc804\uc5d0 \uc0dd\uc131\ud574 \uc900 `AsyncExceptionHandler`\ub97c \ubc18\ud658\ud558\ub3c4\ub85d \uc124\uc815\ud588\ub2e4. \\n\uc774\ub807\uac8c \uc124\uc815\ud55c\ub2e4\uba74 \uc608\uc678\uac00 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 `AsyncUncaughtExceptionHandler`\uc758 \uad6c\ud604\uccb4\uc778 `AsyncExceptionHandler`\uac00 \uc608\uc678\ub97c \uc7a1\uc544\uc11c \ucc98\ub9ac\ub97c \ud574\uc900\ub2e4. \\n\\n```java title=AsyncConfig\\n@EnableAsync\\n@Configuration\\npublic class AsyncConfig implements AsyncConfigurer {\\n\\n @Override\\n public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {\\n return new AsyncExceptionHandler();\\n }\\n}\\n```\\n\\n### MDC \uc815\ubcf4 \uc5f0\ub3d9 \ubb38\uc81c\\n\\n![./mdc-null.png](./mdc-null.png)\\n\\n\uae30\uc874 \uc608\uc678\uac00 \ubc1c\uc0dd\ud560 \ub54c \uc2e4\ud589 \ud750\ub984\uc744 \ucd94\uc801\ud558\uae30 \uc704\ud574 MDC(Mapped Diagnostic Context)\ub97c \uc0ac\uc6a9\ud55c\ub2e4. \\n\ube44\ub3d9\uae30 \ucc98\ub9ac\uc758 \uacbd\uc6b0 \ubcc4\ub3c4\uc758 \uc2a4\ub808\ub4dc\uc5d0\uc11c \ub3d9\uc791\ud558\uae30 \ub54c\ubb38\uc5d0 ThreadLocal \uae30\ubc18\uc73c\ub85c \ub3d9\uc791\ud558\ub294 MDC\uc758 \uc815\ubcf4\ub97c \uc5bb\uc5b4\uc62c \uc218 \uc5c6\uc5c8\ub2e4. \\n\\n\uc774\ub97c \uc801\uc808\ud558\uac8c Decorator \ud074\ub798\uc2a4\ub97c \uc124\uc815\ud558\uc5ec MDC\uc758 \uc815\ubcf4\ub97c \ubcf5\uc0ac\ud574\uc11c \ub118\uaca8\uc904 \uc218 \uc788\ub2e4. \\n\\n\ub2e4\uc74c\uacfc \uac19\uc774 TaskDecorator\ub97c \uad6c\ud604\ud55c \ud074\ub798\uc2a4\ub97c \ud558\ub098 \uc0dd\uc131\ud558\uace0, Task\uac00 \uc2e4\ud589\ub418\uae30 \uc804 MDC\uc758 \uc815\ubcf4\ub97c \ubcf5\uc0ac\ud558\ub3c4\ub85d \uc124\uc815\ud588\ub2e4. \\n\\n```java title=MdcTaskDecorator\\npublic class MdcTaskDecorator implements TaskDecorator {\\n\\n @Override\\n public Runnable decorate(final Runnable runnable) {\\n Map threadContext = MDC.getCopyOfContextMap();\\n return () -> {\\n MDC.setContextMap(threadContext);\\n runnable.run();\\n };\\n }\\n}\\n```\\n\\n\ud574\ub2f9 Decorator \ud074\ub798\uc2a4\ub97c \uc124\uc815 \ud30c\uc77c\uc5d0 \ub4f1\ub85d\ud574 \uc900\ub2e4.\\n\\n```java title=AsyncConfig\\n@RequiredArgsConstructor\\n@EnableAsync\\n@Configuration\\npublic class AsyncConfig implements AsyncConfigurer {\\n\\n private final AsyncConfigurationProperties properties;\\n\\n @Bean\\n public ThreadPoolTaskExecutor taskExecutor() {\\n ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\\n executor.setCorePoolSize(properties.coreSize());\\n executor.setMaxPoolSize(properties.maxSize());\\n executor.setQueueCapacity(properties.queueCapacity());\\n \\n // highlight-next-line\\n executor.setTaskDecorator(new MdcTaskDecorator());\\n executor.setWaitForTasksToCompleteOnShutdown(true);\\n executor.initialize();\\n return executor;\\n }\\n\\n @Override\\n public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {\\n return new AsyncExceptionHandler();\\n }\\n}\\n```\\n\\n\uc124\uc815 \ud6c4\uc5d0\ub294 \uc815\uc0c1\uc801\uc73c\ub85c MDC\uc5d0 \ub4e4\uc5b4\uac00 \uc788\ub294 UUID\uac00 \ucd9c\ub825\ub418\ub294 \uac83\uc744 \ubcfc \uc218 \uc788\ub2e4.\\n\\n![./mdc-not-null.png](./mdc-not-null.png)\\n\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n[spring async, baeldung](https://www.baeldung.com/spring-async) \\n[@Async will not call by @ControllerAdvice for global exception](https://stackoverflow.com/questions/61885358/async-will-not-call-by-controlleradvice-for-global-exception) \\n[Spring \uc758 \ub3d9\uae30, \ube44\ub3d9\uae30, \ubc30\uce58 \ucc98\ub9ac\uc2dc \ud56d\uc0c1 context \ub97c \uc720\uc9c0\ud558\uace0 \ub85c\uae45\ud558\uae30, \uac15\ub0a8\uc5b8\ub2c8](https://blog.gangnamunni.com/post/mdc-context-task-decorator/)"},{"id":"tomcat-retrospective","metadata":{"permalink":"/tomcat-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0","description":"\ud1b0\ucea3 \uad6c\ud604","date":"2023-09-11T00:00:00.000Z","formattedDate":"2023\ub144 9\uc6d4 11\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":12.27,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0","slug":"tomcat-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac","permalink":"/async-exception"},"nextItem":{"title":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958","permalink":"/performance-test-type"}},"content":"### \ud1b0\ucea3 \uad6c\ud604\\n\\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\ub97c \uc9c0\uc6d0\ud560 \ub54c \uac1d\uccb4\uc9c0\ud5a5\uacfc \uad00\ub828\ub41c \ubbf8\uc158\ub3c4 \uae30\ub300\ub97c \ub9ce\uc774 \ud588\uc9c0\ub9cc \ub808\ubca8 4\uc5d0 \uc9c4\ud589\ud558\ub294 \ubbf8\uc158\uc774 \uc815\ub9d0 \ud558\uace0 \uc2f6\uc5c8\ub2e4. \\n\uadf8\ub798\uc11c \ubbf8\uc158\uc744 \ud560 \uc218 \uc788\uc744\uae4c\ub77c\ub294 \uac71\uc815 \ubc18, \ubbf8\uc158\uc5d0 \ub300\ud55c \uae30\ub300 \ubc18\uc73c\ub85c \ubd80\ud47c \ub9c8\uc74c\uc744 \uac00\uc9c0\uace0 \ubbf8\uc158\uc744 \uc2dc\uc791\ud588\ub358 \uac83 \uac19\ub2e4. \\n\\n\uc774\ubc88 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc801\uc808\ud558\uac8c \ucd94\uc0c1\ud654\ud558\uace0, \ubbf8\uc158\uc758 \ubcf8\uc9c8\uc744 \uc774\ud574\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. \\n\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158\uc740 [RFC 2616](https://datatracker.ietf.org/doc/html/rfc2616/)\uc5d0 \uba85\uc2dc\ub41c \uc2a4\ud399(\uc644\ubcbd\ud558\uc9c0 \uc54a\uc9c0\ub9cc \ubbf8\uc158\uc5d0\uc11c \uc8fc\uc5b4\uc9c4 \uc694\uad6c\uc0ac\ud56d\ub9cc \ub9cc\uc871\ud558\ub3c4\ub85d)\uc73c\ub85c \uc694\uccad\uc744 \ubc1b\uc544 \ucc98\ub9ac \ud6c4 \ubc18\ud658\ud558\ub294\ub370 \uc9d1\uc911\ud588\ub2e4. \\n\\n### \ub2e4\uc774\uc5b4\uadf8\ub7a8\\n\\nCatalina\ub294 Tomcat\uc758 \uc11c\ube14\ub9bf \ucee8\ud14c\uc774\ub108, Coyote\ub294 HTTP 1.1 \uc6f9 \uc11c\ubc84\ub97c \uc9c0\uc6d0\ud558\ub294 \uad6c\uc131 \uc694\uc18c\ub77c\uace0 \uc0dd\uac01\ud558\uace0 \uc544\ub798\uc640 \uac19\uc774 \uad6c\uc131\ud588\ub2e4. \\n\uc0ac\uc2e4 \ub0b4\ubd80 \uad6c\uc870\ub97c \uae4a\uac8c \uacf5\ubd80\ud560 \uc2dc\uac04\uc744 \uac00\uc9c0\uc9c0 \ubabb\ud574\uc11c \uac01 \uad6c\uc131 \uc694\uc18c\uac00 \uc65c \ud574\ub2f9 \uc704\uce58\uc5d0 \uc788\ub294\uc9c0 \uc644\ubcbd\ud558\uac8c \uc124\uba85\ud558\uc9c0\ub294 \ubabb\ud558\uc9c0\ub9cc \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc774\uac74 \uc5ec\uae30\uc5d0 \uc788\uc73c\uba74 \uc88b\uc744 \uac83 \uac19\uc740\ub370? \ub77c\ub294 \uc0dd\uac01\uc774 \ub4e4\uba74 \uc801\uc808\ud55c \ud328\ud0a4\uc9c0\uc5d0 \uc704\uce58\uc2dc\ud0a4\ub294 \ubc29\ud5a5\uc73c\ub85c \uc9c4\ud589\uc744 \ud588\ub2e4. \\n\ub610\ud55c \uc801\uc808\ud558\uac8c \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc758\uc874\uc131 \ubc29\ud5a5\uc744 \ub2e8\ubc29\ud5a5\uc73c\ub85c \ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. \\n\\n```mermaid\\ngraph LR\\n\\tsubgraph coyote\\n\\t\\tHP[Http11Processor] --\x3e A\\n\\t\\tHP --\x3e HttpRequestParser\\n\\t\\tHP --\x3e HttpResponseGenerator\\n\\t\\tA[Adapter]\\n end\\n\\n subgraph catalina\\n\\t\\tRA[RequestAdapter] -.-> A\\n\\t\\tAC[AbstractController] -.-> C[Controller]\\n\\t\\tStaticController -.-> AC\\n\\t\\tSM[SessionManger] -.-> Manager\\n\\t\\tTC[Tomcat] --\x3e RA\\n\\t\\tRA --\x3e C\\n\\t\\tRA --\x3e Manager\\n\\t\\tRA --\x3e RM\\n\\t\\tRM[RequestMapper] --\x3e C\\n end\\n\\n subgraph jwp\\n\\t\\tLC[LoginController] -.-> AC\\n\\t\\tApplication --\x3e TC\\n end\\n\\n```\\n\\n### \ucf54\ub4dc \ub9ac\ubdf0 \\n\\n\ud06c\ub8e8 \uc911 \ud55c \uba85\uc774 \ub098\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\uace0, \ub0b4\uac00 \ub2e4\ub978 \ud06c\ub8e8\uc758 \ub9ac\ubdf0\uc5b4\uac00 \ub418\ub294 \ud615\ud0dc\ub85c \uc9c4\ud589\uc774 \ub418\uc5c8\ub2e4. \\n\ub098\uc758 \ub9ac\ubdf0\uc5b4\ub294 \ub514\ub178, \ub9ac\ubdf0\uc774\ub294 \ud544\ub9bd\uc774\uc5c8\ub2e4. \\n\\n\ub514\ub178(\ub9e4\uc758 \ub208\uc774 \uc544\ub2cc \uacf5\ub8e1\uc758 \ub208?)\uac00 \ub9e4\uc6b0 \uaf3c\uaf3c\ud558\uac8c \ucf54\ub4dc \ub9ac\ubdf0\ub97c \ud574\uc8fc\uc5b4\uc11c \uc870\uae08 \ub354 \ub098\uc740 \ucf54\ub4dc\ub97c \uc791\uc131\ud560 \uc218 \uc788\uc5c8\uace0, \ud544\ub9bd\uc758 \ucf54\ub4dc\uc5d0\uc11c\ub294 \uaf3c\uaf3c\ud558\uac8c \uc608\uc678\ucc98\ub9ac \ud558\ub294 \ubd80\ubd84\uc744 \ubc30\uc6b8 \uc218 \uc788\uc5c8\ub2e4. \\n\ud55c \uac00\uc9c0 \uc544\uc26c\uc6b4 \uc810\uc740 \ud544\ub9bd\uc5d0\uac8c \uc791\uc131\ud55c \ub098\uc758 \ucf54\uba58\ud2b8\ub4e4\uc774 \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uacbd\ud5d8 \uae30\ubc18\uc73c\ub85c \uc791\uc131\ud55c \ub0b4\uc6a9\uc774 \ub9ce\uc544 \uadfc\uac70\uac00 \uc870\uae08 \ubd80\uc871\ud588\uace0, \uc815\ub9ac\ub418\uc9c0 \uc54a\uc740 \ubd80\ubd84\uc774 \ub9ce\uc558\ub358 \uac83 \uac19\ub2e4. \\n\ub2e4\uc74c \ubbf8\uc158\ubd80\ud130 \ub9ac\ubdf0\ud560 \ub54c \uc870\uae08 \ub354 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ub354 \uc88b\uc740 \ub0b4\uc6a9\uc744 \ud06c\ub8e8\ub4e4\uacfc \uacf5\uc720\ud560 \uc218 \uc788\ub3c4\ub85d \ub178\ub825\ud574\uc57c\uaca0\ub2e4. \\n\\n### SessionConfig\\n\\n\ubbf8\uc158\uc744 \uc9c4\ud589 \uc911 catalina \ud328\ud0a4\uc9c0\uc758 Session \uad00\ub828 \ubd80\ubd84\uc744 \ubcf4\uba74\uc11c \uc911\ubcf5 \ub85c\uc9c1\uc744 \uac1c\uc120\ud574 \ubcfc \uc218 \uc788\uc744 \uac83 \uac19\uc544 [\ucee8\ud2b8\ub9ac\ubdf0\ud2b8](https://github.com/apache/tomcat/pull/660)\ub97c \uc2dc\ub3c4\ud588\ub2e4. \\n\uc138\uc158 \ucfe0\ud0a4\uc758 \uc774\ub984\uc744 \uac00\uc838\uc624\ub294 Util \ud074\ub798\uc2a4\uc758 \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub294\ub370 \uae30\ubcf8 \uac12\uc740 JSESSIONID \uc9c0\ub9cc \uc124\uc815\uc5d0 \ub530\ub77c\uc11c \uc138\uc158 \ucfe0\ud0a4\uba85\uc744 \ub2e4\ub974\uac8c \uc0ac\uc6a9\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \ud574\ub2f9 \ub85c\uc9c1\uc774 \uc788\ub294 \uac83\uc73c\ub85c \uc0dd\uac01\ud588\ub2e4. \\n\uae30\uc874\uc758 \ucf54\ub4dc\ub294 \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \ucf54\ub4dc\uc758 \ud750\ub984\uc774 \uc77c\uce58\ud558\uc9c0 \uc54a\uc544\uc11c \uc57d\uac04 \uc774\ud574\ud558\uae30 \uc5b4\ub824\uc6e0\ub2e4. \\n\\n\ucd08\uae30\uc5d0 \uc694\uccad\ud588\ub358 PR\uc740 \uae30\uc874\uc758 \ucf54\ub4dc\ubcf4\ub2e4 \uc804\uccb4\uc801\uc73c\ub85c \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, context\uac00 null\uc778 \uacbd\uc6b0 \ubc14\ub85c \uae30\ubcf8 \uac12\uc744 \ubc18\ud658\ud568\uc73c\ub85c\uc368 \uc131\ub2a5 \uac1c\uc120\uc758 \ud6a8\uacfc\uac00 \uc788\uc744 \uac70\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uba54\uc778\ud14c\uc774\ub108\uc778 Mark Thomas \ud615\uc774 \ud574\ub2f9 \ub85c\uc9c1\uc758 \uacbd\uc6b0 \ucef4\ud30c\uc77c\ub7ec\uac00 \ud574\ub2f9 \ubd80\ubd84\uc744 \ucd5c\uc801\ud654 \ud560 \uc218 \uc788\uc744 \uac70\ub77c\uace0 \uae30\ub300\ud55c\ub2e4\uace0 \ud588\uace0, \uac00\ub3c5\uc131\uc744 \uac1c\uc120\uc2dc\ucf1c\ubcf4\ub77c\uace0 \uc870\uc5b8\ud574\uc8fc\uc168\ub2e4. \\n\ucef4\ud30c\uc77c\ub7ec \ucd5c\uc801\ud654\ub294 \uace0\ub824\ud574\ubcf4\uc9c0 \ubabb\ud55c \ubd80\ubd84\uc778\ub370, \uc55e\uc73c\ub85c \ud559\uc2b5\ud574\uc57c \ud560 \ubd80\ubd84\uc774 \uc0b0\ub354\ubbf8\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\\n\ub0a8\uaca8\uc900 \ucf54\uba58\ud2b8\uc5d0 \ub530\ub77c \ucd5c\uc885\uc801\uc73c\ub85c\ub294 \uc911\ubcf5\ub41c \ucf54\ub4dc\ub97c \uc904\uc774\ub294 \ubc29\ud5a5\uc73c\ub85c \ucf54\ub4dc\ub97c \uc218\uc815\ud588\ub2e4. \\n\uacb0\uacfc\uc801\uc73c\ub85c \uae30\uc874 \ub85c\uc9c1 \ub300\ube44 \ube44\uad50 \uc5f0\uc0b0\uc744 \ud55c \ubc88 \uc904\uc77c \uc218 \uc788\uc5c8\uace0, \uba85\uc2dc\ub41c \uc8fc\uc11d\uc758 \ub0b4\uc6a9\uacfc \uc720\uc0ac\ud55c \ud750\ub984\uc758 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\uc5ec \uc88b\uc740 \ubc29\ud5a5\uc73c\ub85c \ub9ac\ud329\ud130\ub9c1\uc744 \ud588\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\\nimport Tabs from \\"@theme/Tabs\\";\\nimport TabItem from \\"@theme/TabItem\\";\\n\\n\\n\\n\\n```java\\npublic static String getSessionCookieName(Context context) {\\n\\n String result = getConfiguredSessionCookieName(context);\\n\\n if (result == null) {\\n result = DEFAULT_SESSION_COOKIE_NAME;\\n }\\n\\n return result;\\n}\\n\\npublic static String getSessionUriParamName(Context context) {\\n\\n String result = getConfiguredSessionCookieName(context);\\n\\n if (result == null) {\\n result = DEFAULT_SESSION_PARAMETER_NAME;\\n }\\n\\n return result;\\n}\\n\\nprivate static String getConfiguredSessionCookieName(Context context) {\\n\\n // Priority is:\\n // 1. Cookie name defined in context\\n // 2. Cookie name configured for app\\n // 3. Default defined by spec\\n if (context != null) {\\n String cookieName = context.getSessionCookieName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n\\n SessionCookieConfig scc =\\n context.getServletContext().getSessionCookieConfig();\\n cookieName = scc.getName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n }\\n\\n return null;\\n}\\n```\\n\\n\\n\\n\\n\\n```java\\npublic static String getSessionCookieName(Context context) {\\n if (context == null) {\\n return DEFAULT_SESSION_COOKIE_NAME;\\n }\\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\\n}\\n\\npublic static String getSessionUriParamName(Context context) {\\n if (context == null) {\\n return DEFAULT_SESSION_PARAMETER_NAME;\\n }\\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\\n}\\n\\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\\n // Priority is:\\n // 1. Cookie name defined in context\\n // 2. Cookie name configured for app\\n // 3. Default defined by spec\\n String cookieName = context.getSessionCookieName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n\\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\\n cookieName = scc.getName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n\\n return defaultName;\\n}\\n```\\n\\n\\n\\n\\n```java\\npublic static String getSessionCookieName(Context context) {\\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_COOKIE_NAME);\\n}\\n\\npublic static String getSessionUriParamName(Context context) {\\n return getConfiguredSessionCookieName(context, DEFAULT_SESSION_PARAMETER_NAME);\\n}\\n\\nprivate static String getConfiguredSessionCookieName(Context context, String defaultName) {\\n // Priority is:\\n // 1. Cookie name defined in context\\n // 2. Cookie name configured for app\\n // 3. Default defined by spec\\n if (context != null) {\\n String cookieName = context.getSessionCookieName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n\\n SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig();\\n cookieName = scc.getName();\\n if (cookieName != null && cookieName.length() > 0) {\\n return cookieName;\\n }\\n }\\n return defaultName;\\n}\\n```\\n\\n\\n\\n\\n### HTTP \uc218\uc5c5\\n\\n\ubbf8\uc158 \uc911\uac04\uc5d0 \uc9c4\ud589\ub418\uc5c8\ub358 HTTP \uc218\uc5c5\uc5d0\ub294 HTTP\ub97c \uc801\uc808\ud558\uac8c \ud65c\uc6a9\ud558\ub294 \ubd80\ubd84\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4. \\n\ud56d\uc0c1 \uc131\ub2a5 \uac1c\uc120\uc744 \uc704\ud574 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ub2e8\uc5d0\uc11c \ucd5c\uc801\ud654\ud574\ubcf4\ub824\uace0 \ub178\ub825\uc744 \ud588\uc9c0\ub9cc, \ub354 \uc801\uc740 \uc2dc\uac04\uc744 \ud22c\uc790\ud574\uc11c \ud6a8\uc728\uc801\uc73c\ub85c \uc131\ub2a5\uc744 \uac1c\uc120\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc5d0 \ub300\ud574 \uc54c \uc218 \uc788\uc5c8\ub358 \uc218\uc5c5\uc774\uc5c8\ub2e4. \\nHTTP \uc555\ucd95, HTTP \uce90\uc2f1, \ub9ac\uc18c\uc2a4 \ucd5c\uc801\ud654 \uae30\ubc95\uc5d0 \ub300\ud574 \ud559\uc2b5\ud588\ub2e4. \\n\\n\uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \ub2e4\uc74c \uc635\uc158\uc744 \uc124\uc815\ud558\uc5ec http\uc758 \uc1a1\uc218\uc2e0\uc758 \uc555\ucd95\uc744 \uc9c4\ud589\ud560 \uc218 \uc788\ub2e4. \\n\\n```yml\\nserver:\\n compression:\\n enabled: true\\n```\\n\\n\uc218\uc5c5 \uc911 \ud574\ub2f9 \uc555\ucd95 \uc131\ub2a5\uc774 \uc88b\ub2e4\uba74 \uc65c \uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\uc11c\ub294 \uae30\ubcf8 \uac12\uc73c\ub85c \uc124\uc815\uc744 \ud558\uc9c0 \uc54a\uc558\ub294\uc9c0 \uad81\uae08\ud574\uc84c\ub2e4. \\n\uad81\uae08\uc99d\uc744 \ud574\uc18c\ud558\uc9c0 \ubabb\ud588\ub294\ub370 \ub9d0\ub791\uc774 \uc7a1\ub2f4 \ucc44\ub110\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc740 [issue](https://github.com/spring-projects/spring-boot/issues/21369)\ub97c \ucc3e\uc544\uc8fc\uc5c8\ub2e4. \\n\ub0b4\uc6a9\uc744 \uc694\uc57d\ud574 \ubcf4\uc790\uba74 WAS \ubcc4\ub85c \uc555\ucd95\uc744 \ud558\uae30 \uc704\ud574 \uc124\uc815\ud574\uc57c \ud558\ub294 \uac83\uc774 \ub2e4\ub974\uace0, \ubb34\uc870\uac74 \uc555\ucd95\uc744 \ud558\ub294 \uac83\uc774 \ucd5c\uc801\uc758 \uacbd\uc6b0\uac00 \uc544\ub2d0 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \uae30\ubcf8\uac12\uc73c\ub85c \uc124\uc815\ud558\uc9c0 \uc54a\ub294 \uac83 \uac19\ub2e4. \\n\\n> If you\'re developing a public-facing application then it\'s probably likely gzip compression would be worthwhile. If, however, you\'re a microservice application and you\'re in a dataceter, you may well prefer to reduce CPU load because you know you\'ll only be talking to other microservices and you have a reliable gigabit network.\\n\\nPhil Webb \ud615\ub2d8\uc758 \ub9d0\uc5d0 \ub530\ub974\uba74 \uc77c\ubc18\uc801\uc778 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uac1c\ubc1c\ud558\ub294 \uacbd\uc6b0 gzip \uc555\ucd95\uc744 \uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\uc9c0\ub9cc, MSA \ud658\uacbd + \ub370\uc774\ud130 \uc13c\ud130\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \uc624\uc9c1 \ub2e4\ub978 MSA \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uacfc \ud1b5\uc2e0\ud558\uace0, \uace0\uc131\ub2a5 \ub124\ud2b8\uc6cc\ud06c\uac00 \uc788\uae30 \ub54c\ubb38\uc5d0 CPU \ubd80\ud558\ub97c \uc904\uc774\ub294 \uac83\uc774 \uc6b0\uc120\uc2dc \ub420 \uc218\ub3c4 \uc788\ub2e4\ub294 \uac83\uc774\uc5c8\ub2e4. \\n\\n\uc774\uc678\uc5d0\ub3c4 \uc758\ub3c4\ud558\uc9c0 \uc54a\uc740 \uce90\uc2f1\uc744 \ub9c9\uae30 \uc704\ud574 \ud734\ub9ac\uc2a4\ud2f1 \uce90\uc2f1\uc744 \uc81c\uac70\ud558\uac70\ub098, \uac1c\uc778 \uc815\ubcf4 \uc720\ucd9c\uc744 \ub9c9\uae30 \uc704\ud574 \uc751\ub2f5 \ud5e4\ub354\uc5d0 private\uc744 \uc124\uc815, ETag\ub3c4 \ud559\uc2b5\ud588\ub2e4. \\n\\n:::note ETag\\nETag HTTP \uc751\ub2f5 \ud5e4\ub354\ub294 \ud2b9\uc815 \ubc84\uc804\uc758 \ub9ac\uc18c\uc2a4\ub97c \uc2dd\ubcc4\ud558\ub294 \uc2dd\ubcc4\uc790\ub2e4. \\n\uc6f9 \uc11c\ubc84\uac00 \ub0b4\uc6a9\uc744 \ud655\uc778\ud558\uace0 \ubcc0\ud558\uc9c0 \uc54a\uc558\uc73c\uba74, \uc6f9 \uc11c\ubc84\ub85c full \uc694\uccad\uc744 \ubcf4\ub0b4\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0, \uce90\uc2dc\uac00 \ub354 \ud6a8\uc728\uc801\uc774\uac8c \ub41c\ub2e4. \\nMDN\\n:::\\n\\n### Thread \uc218\uc5c5\\n\\n\uc2a4\ub808\ub4dc\uc5d0 \ub300\ud55c \uc218\uc5c5\uc744 \ub4e4\uc5c8\uc9c0\ub9cc, \ubcf5\uc7a1\ud55c \ub0b4\uc6a9\ub3c4 \uc6cc\ub099 \ub9ce\uc558\uae30 \ub54c\ubb38\uc5d0 \uc124\uba85\ud558\ub77c\uace0 \ud558\uba74 \uc798 \ubabb\ud560 \uac83 \uac19\uc740 \ub290\ub08c\uc774 \ub4e0\ub2e4. \\n\ud604\uc7ac \ud504\ub85c\uc81d\ud2b8, \ubbf8\uc158, \ud14c\ucf54\ud1a1 \uc900\ube44\ub97c \ubcd1\ud589\ud574\uc57c \ud574\uc11c \uc138\ubd80\uc801\uc778 \ub0b4\uc6a9\uc740 \uc2dc\uac04 \ub0a0 \ub54c \ubcf5\uc2b5\ud558\ub824\uace0 \ud55c\ub2e4. \\n\\n\uc2a4\ub808\ub4dc\ub97c \uc774\ud574\ud558\uace0, WAS\uc5d0 \uc2a4\ub808\ub4dc \uc124\uc815 \uad00\ub828\ud55c \uc2e4\uc2b5\uc774 \uc788\uc5c8\ub294\ub370 \ud14c\uc624\uc640 \uac19\uc774 1\uc2dc\uac04 \uc815\ub3c4 \ud398\uc5b4\ub85c Thread \uc2e4\uc2b5\uc744 \uc9c4\ud589\ud574 \ubcf4\uc558\ub2e4. \\n\ud559\uc2b5\ud55c \ub0b4\uc6a9\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\\n`threads.max`: Tomcat\uc758 \ucd5c\ub300 \uc2a4\ub808\ub4dc \uac1c\uc218 \\n`max-connections`: Tomcat\uc774 \uc720\uc9c0\ud560 \uc218 \uc788\ub294 \ucd5c\ub300 \ucee4\ub125\uc158 \uac1c\uc218 \\n`accept-count`: \ucd5c\ub300 \uc5f0\uacb0 \uc218\uc5d0 \ub3c4\ub2ec\ud588\uc744 \ub54c \uc5f0\uacb0 \uc694\uccad\uc5d0 \ub300\ud574 \uc6b4\uc601 \uccb4\uc81c\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \ub300\uae30\uc5f4\uc758 \ucd5c\ub300 \uae38\uc774. \ud574\ub2f9 Queue\uc5d0 \uc694\uccad\uc774 \uc313\uc774\ub294 \uac83\uc740 Tomcat\uc774 \ub354 \uc774\uc0c1 \uc694\uccad\uc744 \ubc1b\uc744 \uc218 \uc5c6\ub2e4\ub294 \ub73b\uc774\ub2e4. accpet-count queue\uc5d0\ub3c4 \uc694\uccad\uc774 \uac00\ub4dd\ucc28\uba74 \uadf8 \uc774\ud6c4\uc5d0 \uc624\ub294 \uc694\uccad\uc740 \uac70\ubd80\ub41c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n C(\\"Client\\") -- request --\x3e ACQ(\\"\uc6b4\uc601\uccb4\uc81c\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue \\n size = accept-count\\") --\x3e TCQ(\\"Tomcat Connector\uc5d0 \uc758\ud574 \uad00\ub9ac\ub418\ub294 Queue\\n size = max-connections\\") --\x3e TP(\\"Thread Pool\\n size = threads.max\\")\\n```\\n\\n### \ub9c8\uce58\uba70\\n\\n\uc2dc\uac04\uc740 \ub108\ubb34 \ube60\ub974\uac8c \uac00\uace0 \ud560 \uc77c\uc740 \ub9ce\uc740 \uac83 \uac19\ub2e4. \\n\uc6b0\uc120\uc21c\uc704\ub97c \uc798 \uc815\ud558\uace0 \ud559\uc2b5\uc744 \uc9c4\ud589\ud574\uc57c\uaca0\ub2e4. \\n\ud604\uc7ac \ub370\uc774\ud130 \ub2e4\ub8e8\ub294 \ubd80\ubd84(DB)\uc5d0 \ub300\ud55c \ud559\uc2b5\uc774 \ub9ce\uc774 \ubd80\uc871\ud55c \uac83 \uac19\ub2e4. \ud574\ub2f9 \ubd80\ubd84\uc740 \ud14c\ucf54\ud1a1\uc774 \ub05d\ub098\ub294\ub300\ub85c \ucc44\uc6cc\uc57c\uaca0\ub2e4. \\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n[RFC 2616](https://datatracker.ietf.org/doc/html/rfc2616/) \\n[ETag, mdn](https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/ETag) \\n[Apache Tomcat 8 Configuration Reference](https://tomcat.apache.org/tomcat-8.5-doc/config/http.html) \\n[Apache Tomcat Tuning, Terry Cho](https://bcho.tistory.com/788) \\n[maxThreads, maxConnections, acceptCount\ub85c Tomcat \ud29c\ub2dd\ud558\uae30](https://dev-ws.tistory.com/96)"},{"id":"performance-test-type","metadata":{"permalink":"/performance-test-type","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8.mdx","source":"@site/blog/2023-3/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8.mdx","title":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958","description":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8","date":"2023-09-10T00:00:00.000Z","formattedDate":"2023\ub144 9\uc6d4 10\uc77c","tags":[{"label":"performance test","permalink":"/tags/performance-test"}],"readingTime":5.8,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958","slug":"performance-test-type","tags":["performance test"]},"prevItem":{"title":"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0","permalink":"/tomcat-retrospective"},"nextItem":{"title":"DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30","permalink":"/db-replication"}},"content":"## \uc131\ub2a5 \ud14c\uc2a4\ud2b8\\n\\nAPI\uc758 \uc694\uccad\uc774 \ub9ce\uc740 \uc0c1\ud669\uc5d0\uc11c \uc11c\ubc84\uac00 \uc5b4\ub5bb\uac8c \ub3d9\uc791\ud558\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8\\n\\n\uc2dc\uc2a4\ud15c\uc5d0 \ubd80\ud558\uac00 \uac78\ub9ac\uba74 \ubb38\uc81c \uc0c1\ud669\uc774 \ubc1c\uc0dd\ud560 \uc218 \uc788\ub2e4. \\n\ub2e4\uc591\ud55c \uc0c1\ud669\uc5d0 \ub300\ube44\ud574\uc11c \uc131\ub2a5 \ud14c\uc2a4\ud2b8\ub97c \ud574\uc57c\ud55c\ub2e4. \\n\\n![./test.png](./test.png)\\n\\n### \uc2a4\ubaa8\ud06c \ud14c\uc2a4\ud2b8(Smoke Test)\\n\\n\ucd5c\uc18c\ud55c\uc758 \ubd80\ud558\ub97c \uc8fc\uc5b4 \uc2dc\uc2a4\ud15c\uc774 \uc815\uc0c1\uc801\uc73c\ub85c \ub3d9\uc791\ud558\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8 \\n\\nVU\ub97c \ucd5c\uc18c\ud55c\uc73c\ub85c \ub450\uace0, \uc9e7\uc740 \uc2dc\uac04\uc744 \uac00\uc9c0\uace0 \ud14c\uc2a4\ud2b8\ud55c\ub2e4. \\n\ub2e4\ub978 \ud14c\uc2a4\ud2b8\ub97c \uc2dc\uc791\ud558\uae30 \uc804\uc5d0 \uc2a4\ubaa8\ud06c \ud14c\uc2a4\ud2b8\ub97c \ud568\uc73c\ub85c\uc368 \ud14c\uc2a4\ud2b8 \uc2a4\ud06c\ub9bd\ud2b8\uc5d0 \uc624\ub958\uac00 \uc5c6\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\uace0, \uc131\ub2a5 \uc9c0\ud45c\uac00 \uc815\uc0c1\uc801\uc73c\ub85c \uc218\uc9d1, \ubaa8\ub2c8\ud130\ub9c1 \ub418\uace0 \uc788\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n:::note \uac00\uc0c1 \uc0ac\uc6a9\uc790(VU)\\n\uac00\uc0c1 \uc0ac\uc6a9\uc790\ub294 \uc11c\ubc84 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0 \ub300\ud574 \ud2b9\uc815 \ud14c\uc2a4\ud2b8\ub97c \uc2e4\ud589\ud55c\ub2e4. \\n\uc774\ub294 \ub2e4\ub978 \uac00\uc0c1 \uc0ac\uc6a9\uc790\uc640 \ub3c5\ub9bd\uc801\uc73c\ub85c \uc2e4\ud589\ub418\uba70, \uc5ec\ub7ec \uac00\uc0c1 \uc0ac\uc6a9\uc790\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub3d9\uc2dc \uc5f0\uacb0\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\uc2a4\ub808\ub4dc\ub77c\uace0 \uc0dd\uac01\ud558\uba74 \ub41c\ub2e4. \\n:::\\n\\n### \uc2a4\ud30c\uc774\ud06c \ud14c\uc2a4\ud2b8(Spike Test)\\n\\n\uc0ac\uc6a9\ub7c9\uc774 \uae09\uc99d\ud558\ub294 \uc0c1\ud669\uc5d0\uc11c \uc2dc\uc2a4\ud15c\uc774 \uacac\ub514\uace0 \uc131\ub2a5\uc5d0 \ubb38\uc81c\uac00 \uc5c6\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8 \\n\\n\ud2f0\ucf13 \ubc1c\uae09, \ud560\uc778 \ucfe0\ud3f0 \ubc1c\uae09\uacfc \uac19\uc740 \uc774\ubca4\ud2b8\ub97c \ud558\ub294 \uacbd\uc6b0 \ub300\uaddc\ubaa8 \ud2b8\ub798\ud53d\uc774 \ub4e4\uc5b4\uc628\ub2e4. \\n\uc2a4\ud30c\uc774\ud06c \ud14c\uc2a4\ud2b8\ub97c \ud1b5\ud574 \uae09\uc99d\ud558\ub294 \ubd80\ud558 \uc0c1\ud669\uc5d0\uc11c \uc2dc\uc2a4\ud15c\uc774 \uc5b4\ub5bb\uac8c \ub3d9\uc791\ud558\uace0, \ubd80\ud558\ub97c \uc798 \ubc84\ud2f0\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n### \ubd80\ud558 \ud14c\uc2a4\ud2b8(Load Test)\\n\\n\ubaa9\ud46f\uac12\uc5d0 \ud574\ub2f9\ub418\ub294 \ubd80\ud558\ub97c \uacac\ub51c \uc218 \uc788\uc744\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8 \\n\\n\uc77c\ubc18\uc801\uc778 \ubd80\ud558 \uc0c1\ud669\uc5d0\uc11c \uc2dc\uc2a4\ud15c\uc774 \uc5b4\ub5bb\uac8c \ub3d9\uc791\ud558\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8\ub2e4. \\n\ub7a8\ud504\uc5c5 \ub610\ub294 \ubb19\ud46f\uac12\uc5d0 \ud574\ub2f9\ud558\ub294 \ubd80\ud558 \uae30\uac04\ub3d9\uc548 \uc131\ub2a5\uc774 \ubb38\uc81c\uac00 \uc788\ub294\uc9c0 \ud655\uc778\ud558\uace0, \uc2dc\uc2a4\ud15c \ubcc0\uacbd \ud6c4\uc5d0\ub3c4 \ubd80\ud558 \ud14c\uc2a4\ud2b8\ub97c \ub3cc\ub824 \ub3d9\uc77c\ud558\uac8c \ubaa9\ud46f\uac12\uc744 \ucc98\ub9ac\ud558\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n:::note \ub7a8\ud504 \uc5c5(Ramp-up)\\n\ubd80\ud558 \ud14c\uc2a4\ud2b8\ub97c \uc704\ud574 \uc124\uc815\ud55c \uac00\uc0c1 \uc0ac\uc6a9\uc790 \uc218\uc5d0 \ub3c4\ub2ec\ud558\ub294 \ub370 \uac78\ub9ac\ub294 \uc2dc\uac04\\n:::\\n\\n### \uc2a4\ud2b8\ub808\uc2a4 \ud14c\uc2a4\ud2b8(Stress Test) \\n\\n\uc2dc\uc2a4\ud15c\uc758 \ucd5c\ub300\uce58\uc5d0 \ud574\ub2f9\ub418\ub294 \ubd80\ud558\ub97c \ubc1b\uc558\uc744 \ub54c \uc2dc\uc2a4\ud15c\uc774 \uc5b4\ub5bb\uac8c \ub3d9\uc791\ud558\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8 \\n\\n\uadf8\ub798\ud504\ub97c \ubd24\uc744 \ub54c \ubd80\ud558 \ud14c\uc2a4\ud2b8\uc640 \uc720\uc0ac\ud55c \ud615\ud0dc\ub85c \ubcf4\uc774\uc9c0\ub9cc, \ubd80\ud558\ub7c9\uc774 \ub2e4\ub974\ub2e4. \\n\uc77c\ubc18\uc801\uc73c\ub85c \ud3c9\uade0\uc801\uc778 \ubaa9\ud46f\uac12 \ub300\ube44 \uc791\uac8c\ub294 50% \uc774\uc0c1, \ud544\uc694\uc758 \uacbd\uc6b0 \uadf8 \uc774\uc0c1\uc73c\ub85c \ubd80\ud558\ub97c \uc900\ub2e4. \\n\uc2a4\ud2b8\ub808\uc2a4 \ud14c\uc2a4\ud2b8\ub294 \ubd80\ud558 \ud14c\uc2a4\ud2b8\ub97c \uc2e4\ud589\ud55c \ud6c4\uc5d0\ub9cc \uc2e4\ud589\ud574\uc57c \ud55c\ub2e4. \ubd80\ud558 \ud14c\uc2a4\ud2b8\uac00 \uc774\ub8e8\uc5b4\uc9c0\uc9c0 \uc54a\uc740 \uc0c1\ud669\uc5d0\uc11c \uc2a4\ud2b8\ub808\uc2a4 \ud14c\uc2a4\ud2b8\ub97c \uc2e4\ud589\ud558\ub294 \uacbd\uc6b0\uc5d0\ub294 \ubcd1\ubaa9 \uc9c0\uc810\uc774\ub098 \ubb38\uc81c \uc0c1\ud669\uc744 \ucc3e\uae30 \uc5b4\ub824\uc6cc\uc9c4\ub2e4. \\n\ub610\ud55c \ubd80\ud558 \ud14c\uc2a4\ud2b8\uc5d0\uc11c \uc0ac\uc6a9\ud55c \uc2a4\ud06c\ub9bd\ud2b8\ub97c VU\uac12(\uc2a4\ub808\ub4dc \uc218)\ub9cc \uc218\uc815\ud558\uc5ec \uc7ac\uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\ub2e4. \\n\\n### \ub0b4\uad6c \ud14c\uc2a4\ud2b8(Endurance Test) \\n\\n\ud3c9\uade0 \uc0ac\uc6a9\ub960\ub85c \uc77c\uc815 \ubd80\ud558\ub97c \uc9c0\uc18d\uc801\uc73c\ub85c \uc8fc\uba70 \uc2dc\uc2a4\ud15c\uc774 \ubb38\uc81c\ub418\ub294 \uc9c0\uc810\uc744 \ud655\uc778\ud558\ub294 \ud14c\uc2a4\ud2b8 \\n\\n\ud761\uc218 \ud14c\uc2a4\ud2b8(Soak Test)\ub77c\uace0\ub3c4 \ud558\uba70, \uae30\ubcf8\uc801\uc778 \ubd80\ud558 \ud14c\uc2a4\ud2b8\uc758 \ubcc0\ud615\uc774\ub77c\uace0 \ubcfc \uc218 \uc788\ub2e4. \\n\ub2e4\ub978 \ud14c\uc2a4\ud2b8\uc640 \ub2ec\ub9ac \uae34 \uc2dc\uac04\ub3d9\uc548 \ud14c\uc2a4\ud2b8\ub97c \ud558\ub294 \uac83\uc774 \ud2b9\uc9d5\uc774\uba70, \uba54\ubaa8\ub9ac \ub204\uc218 \ubb38\uc81c\uc640 \uac19\uc774 \uc7a5\uc2dc\uac04 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uc2e4\ud589\ud560 \ub54c \uc2dc\uc2a4\ud15c\uc758 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud558\ub294 \ubd80\ubd84\uc744 \ud655\uc778\ud558\ub294 \uac83\uc774 \ubaa9\uc801\uc774\ub2e4. \\n\\n### \uc911\ub2e8\uc810 \ud14c\uc2a4\ud2b8(Breakpoint Test)\\n\\n\uc784\uacc4 \uc9c0\uc810\uc744 \ucc3e\uae30 \uc704\ud574 \ubd80\ud558\ub97c \uc810\uc9c4\uc801\uc73c\ub85c \uc99d\uac00\uc2dc\ud0a4\uba70 \uc9c4\ud589\ud558\ub294 \ud14c\uc2a4\ud2b8\\n\\n\ubb38\uc81c\ub418\ub294 \ubd80\ubd84\uc744 \ub354 \ube68\ub9ac \ucc3e\uae30 \uc704\ud574 \ub2e4\ub978 \ud14c\uc2a4\ud2b8\ub97c \ud1b5\uacfc\ud55c \ub2e4\uc74c\uc5d0 \uc911\ub2e8\uc810 \ud14c\uc2a4\ud2b8\ub97c \uc9c4\ud589\ud558\uace0, \uc774 \ub54c \uc810\uc9c4\uc801\uc73c\ub85c \ubd80\ud558\ub97c \ub298\ub824\ub098\uac00\ub294 \uac83\uc774 \uc88b\ub2e4. \\n\uc2a4\ud2b8\ub808\uc2a4 \ud14c\uc2a4\ud2b8\ub97c \uc131\ub2a5 \ud29c\ub2dd\uacfc \ubc18\ubcf5\ud574\uc11c \uc9c4\ud589\ud55c\ub2e4\uba74, \uc2dc\uc2a4\ud15c\uc744 \ub354\uc6b1 \ubc1c\uc804\uc2dc\ud0ac \uc218 \uc788\ub2e4. \\n\ub2e4\ub9cc Auto Scaling\uc774 \uc801\uc6a9\ub41c \ud074\ub77c\uc6b0\ub4dc \ud658\uacbd\uc5d0\uc11c\ub294 \uc9c4\ud589\ud558\uc9c0 \uc54a\uc544\uc57c \ud55c\ub2e4. \\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n[Load test types, k6](https://k6.io/docs/test-types/load-test-types/) \\n\uc790\ubc14 \ucd5c\uc801\ud654 - \ubca4\uc800\ubbfc J. \uc5d0\ubc88\uc2a4, \uc81c\uc784\uc2a4 \uace0\ud504, \ud06c\ub9ac\uc2a4 \ub274\ub79c\ub4dc \\n\uc544\ub9c8\uc874 \uc6f9 \uc11c\ube44\uc2a4 \ubd80\ud558 \ud14c\uc2a4\ud2b8 \uc785\ubb38 - \ub098\uce74\uac00\uc640 \ud0c0\ub8e8\ud558\uce58, \ubaa8\ub9ac\uc2dc\ud0c0 \ucf04"},{"id":"db-replication","metadata":{"permalink":"/db-replication","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-08-22-DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30.mdx","source":"@site/blog/2023-3/2023-08-22-DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30.mdx","title":"DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30","description":"\ubcf5\uc81c(Replication)","date":"2023-08-22T00:00:00.000Z","formattedDate":"2023\ub144 8\uc6d4 22\uc77c","tags":[{"label":"mysql","permalink":"/tags/mysql"},{"label":"replication","permalink":"/tags/replication"}],"readingTime":20.58,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30","slug":"db-replication","tags":["mysql","replication"]},"prevItem":{"title":"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc885\ub958","permalink":"/performance-test-type"},"nextItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 3 \ud68c\uace0","permalink":"/woowacourse-level3-retrospective"}},"content":"## \ubcf5\uc81c(Replication)\\n\\n\ud55c \uc11c\ubc84\uc5d0\uc11c \ub2e4\ub978 \uc11c\ubc84\ub85c \ub370\uc774\ud130\ub97c \ub3d9\uae30\ud654\ud558\ub294 \uac83\uc744 \uc758\ubbf8\ud55c\ub2e4. \\n\uc6d0\ubcf8 \ub370\uc774\ud130\ub97c \uac00\uc9c0\ub294 \uc11c\ubc84\ub97c Primary \ub610\ub294 Source \ub77c\uace0 \ubd80\ub974\uace0, \ubcf5\uc81c\ub41c \ub370\uc774\ud130\ub97c \uac00\uc9c0\ub294 \uc11c\ubc84\ub97c Secondary \ub610\ub294 Replica \ub77c\uace0 \ubd80\ub978\ub2e4. \\n\\n### \ubcf5\uc81c\ub97c \ud558\ub294 \uc774\uc720\\n\\n**1. \uc2a4\ucf00\uc77c \uc544\uc6c3**\\n\\n\uc0ac\uc6a9\uc790\uc758 \ud2b8\ub798\ud53d\uc774 \uc99d\uac00\ud558\ub294 \uacbd\uc6b0, \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uac00\ud574\uc9c0\ub294 \ubd80\ud558\ub3c4 \uc790\uc5f0\uc2a4\ub7fd\uac8c \uc99d\uac00\ud55c\ub2e4. \\n\uc774\ub97c \ucc98\ub9ac\ud558\uae30 \uc704\ud574 \ubcf5\uc81c\ub97c \ud1b5\ud55c \uc2a4\ucf00\uc77c \uc544\uc6c3\uc744 \uc801\uc6a9\ud558\uc5ec \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \ucffc\ub9ac\ub4e4\uc744 \uac01\uac01\uc758 \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub85c \ubd84\uc0b0 \uc2dc\ud0ac \uc218 \uc788\ub2e4. \\n\\n**2. \ub370\uc774\ud130 \ubc31\uc5c5**\\n\\n\uc2e4\uc81c \uc6b4\uc601\ub418\ub294 \uc11c\ube44\uc2a4\uac00 \uc0ac\uc6a9\ud558\uace0 \uc788\ub294 DB\uc5d0\uc11c \ubc31\uc5c5\uc744 \uc9c4\ud589\ud558\ub294 \uacbd\uc6b0, \uc11c\ube44\uc2a4\uc5d0 \uc601\ud5a5\uc744 \ubbf8\uce60 \uc218 \uc788\ub2e4. \\n\ub530\ub77c\uc11c \uc2e4\uc81c \uc11c\ube44\uc2a4\uc5d0 \uc601\ud5a5\uc774 \uac00\uc9c0 \uc54a\ub3c4\ub85d \ubcf5\uc81c\ub97c \ud1b5\ud574 Replica \uc11c\ubc84\ub97c \uad6c\ucd95\ud558\uc5ec, Replica \uc11c\ubc84\uc5d0\uc11c \ubcf5\uc81c\ub97c \uc9c4\ud589\ud558\ub294 \ubc29\ubc95\uc73c\ub85c \uc601\ud5a5\uc744 \ucd5c\uc18c\ud654 \ud560 \uc218 \uc788\ub2e4. \\n\\n**3. \ub370\uc774\ud130 \ubd84\uc11d**\\n\\n\ubc31\uc5c5\uacfc \ub9c8\ucc2c\uac00\uc9c0\ub85c \ubcf5\uc7a1\ud558\uace0 \ubb34\uac70\uc6b4 \ubd84\uc11d\uc6a9 \ucffc\ub9ac\uc758 \uc11c\ube44\uc2a4\uc5d0 \uc601\ud5a5\uc744 \ubbf8\uce60 \uc218 \uc788\ub2e4. \\n\ub9c8\ucc2c\uac00\uc9c0\ub85c \ubcf5\uc81c\ub97c \uc0ac\uc6a9\ud574 \ubd84\uc11d\uc6a9 \ucffc\ub9ac\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub294 \ud658\uacbd\uc744 \ub9cc\ub4e4 \uc218 \uc788\ub2e4. \\n\\n**4. \ub370\uc774\ud130\uc758 \uc9c0\ub9ac\uc801 \ubd84\uc0b0**\\n\\n\ube60\ub978 \uc751\ub2f5\uc744 \uc704\ud574 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc11c\ubc84\uc5d0 \uac00\uae5d\uac8c \uc11c\ubc84\ub97c \uad6c\uc131\ud558\uac70\ub098, \uace0\uac00\uc6a9\uc131(High Availability)\uc744 \uc704\ud574\uc11c\ub3c4 \uc0ac\uc6a9\ub41c\ub2e4. \\n\\n### \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ud30c\uc77c \uc704\uce58 \uae30\ubc18 \ubcf5\uc81c\\n\\nMySQL \uc11c\ubc84\uc5d0\uc11c \ubc1c\uc0dd\ud558\ub294 \ubcc0\uacbd\uc0ac\ud56d\uc5d0 \ub300\ud55c \ub85c\uadf8 \ud30c\uc77c\uc744 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8\ub77c\uace0 \ud55c\ub2e4. \\n\ubc14\uc774\ub108\ub9ac \ub85c\uadf8\ub97c \ud1b5\ud574 \ub370\uc774\ud130 \ubcc0\uacbd, \ud14c\uc774\ube14 \uad6c\uc870 \ubcc0\uacbd, \uacc4\uc815\uc774\ub098 \uad8c\ud55c \ubcc0\uacbd\uc5d0 \ub300\ud55c \uc815\ubcf4\uac00 \uc800\uc7a5\ub41c\ub2e4. \\nMySQL\uc758 \ubcf5\uc81c\ub294 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \uae30\ubc18\uc73c\ub85c \uad6c\ud604\ub418\uc5b4 \uc788\ub2e4. \uc774\ub97c Replica \uc11c\ubc84\ub85c \uc804\ub2ec\ud558\uace0 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \uae30\ubc18\uc73c\ub85c \ub370\uc774\ud130\ub97c \ubcc0\uacbd \uc0ac\ud56d\uc744 \ubc18\uc601\ud55c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n\\tsubgraph Replica\\n\\t\\tdirection TB\\n\\t\\tIO[Replication I/O thread] -- save --\x3e RL[Relay Log]\\n\\t\\tSQL[Replication SQL Thread] -- read --\x3e RL\\n\\tend\\n\\n\\tsubgraph Source\\n\\t\\tdirection TB\\n\\t\\tBLD[Binary Log Dump Thread] -- Send Binary Log Dump--\x3e IO\\n\\tend\\n\\n```\\n\\n:::note \uc2a4\ub808\ub4dc\ubcc4 \uc5ed\ud560\\n\\nBinary Log Dump Thread: \ubc14\uc774\ub108\ub9ac \ub85c\uadf8\uc758 \ub0b4\uc6a9\uc744 Replica \uc11c\ubc84\ub85c \uc804\ub2ec \\nReplication I/O Thread: Binary \ub85c\uadf8 \uc774\ubca4\ud2b8\ub97c \uac00\uc838\uc640 \ub85c\uceec \uc11c\ubc84\uc758 \ud30c\uc77c(Relay Log)\ub85c \uc800\uc7a5 \\nReplication SQL Thread: \ub9b4\ub808\uc774 \ub85c\uadf8 \ud30c\uc77c\uc758 \uc774\ubca4\ud2b8\ub97c \uc77d\uace0 \uc2e4\ud589\\n\\n:::\\n\\n### \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ubc29\uc2dd\uc758 \ubb38\uc81c\uc810\\n\\n\ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ubc29\uc2dd\uc740 \uc11c\ubc84\uc5d0 \uc7a5\uc560\uac00 \ubc1c\uc0dd\ud588\uc744 \ub54c \ubcf5\uc81c \ud1a0\ud3f4\ub85c\uc9c0 \ubcc0\uacbd\uc774 \uae4c\ub2e4\ub86d\ub2e4. \\n\ud1a0\ud3f4\ub85c\uc9c0\ub780 \ub124\ud2b8\uc6cc\ud06c\uc758 \uc694\uc18c\ub4e4\uc744 \ubb3c\ub9ac\uc801\uc73c\ub85c \uc5f0\uacb0\ud574 \ub193\uc740 \uac83, \ub610\ub294 \uadf8 \uc5f0\uacb0 \ubc29\uc2dd\uc744 \ub9d0\ud55c\ub2e4.\\n\\n```mermaid\\ngraph TD\\n\\tA[A binary-log:300] --\x3e B[B Binary-log:300]\\n\\tA[A binary-log:300] --\x3e C[C Binary-log:200]\\n```\\n\\n\uc704\uc640 \uac19\uc774 Source \uc11c\ubc84, Replica 2\ub300\uac00 \uc874\uc7ac\ud558\uace0, C \uc11c\ubc84\uc5d0 \ubcf5\uc81c \uc9c0\uc5f0\uc774 \ub418\uc5c8\uc744 \ub54c \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud55c\ub2e4. \\n\\n```mermaid\\ngraph TD\\n\\tA[A binary-log:300]\\n\\tB[B Binary-log:300] --\x3e C[C Binary-log:200 \ubb38\uc81c \ubc1c\uc0dd]\\n```\\n\\nA \uc11c\ubc84\uc5d0\uc11c \uc7a5\uc560\uac00 \ubc1c\uc0dd\ud55c\ub2e4\uba74 B \uc11c\ubc84\ub97c Source \uc11c\ubc84\ub85c \uc2b9\uaca9\ud558\uace0, C\uc5d0\uac8c \uc870\ud68c \ucffc\ub9ac\ub97c \ubd84\uc0b0\uc2dc\ud0a8\ub2e4. \\n\ud558\uc9c0\ub9cc \uc5ec\uae30\uc11c C \uc11c\ubc84\uc5d0\ub294 A \uc11c\ubc84\uc640 \ub3d9\uae30\ud654\uac00 \uc548\ub418\uc5c8\uc73c\ub2c8 \uc870\ud68c \uc2dc \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud55c\ub2e4. \\n\ub4a4\ub2a6\uac8c B \uc11c\ubc84\uc640 \ub3d9\uae30\ud654\ub97c \ud558\ub824\uace0 \ud574\ub3c4, \uc5b4\ub5a4 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8, \uc5b4\ub5a4 \uc704\uce58\uc640 \ub3d9\uae30\ud654\ud574\uc57c\ud558\ub294\uc9c0 \uc54c\uae30 \uc5b4\ub835\ub2e4. \\n\\n### \uae00\ub85c\ubc8c \ud2b8\ub79c\uc7ad\uc158 \uc544\uc774\ub514(GTID) \uae30\ubc18 \ubcf5\uc81c\\n\\nGTID \ubc29\uc2dd\uc744 \uc0ac\uc6a9\ud558\uc5ec \ucc38\uc5ec\ud55c \ubaa8\ub4e0 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uac00 \ubc1c\uc0dd\ud55c \uc774\ubca4\ud2b8\uc5d0 \uace0\uc720\ud55c \uc2dd\ubcc4\uac12\uc744 \ubd80\uc5ec\ud55c\ub2e4\uba74, \ub3d9\uae30\ud654\uc5d0 \ub300\ud55c \ubb38\uc81c\ub97c \uac04\ub2e8\ud558\uac8c \ud574\uacb0\ud560 \uc218 \uc788\ub2e4. \\n\uc704\uc758 \uc608\uc2dc\uc640 \uac19\uc774 \ubcf5\uc81c \uc9c0\uc5f0\uacfc \ud568\uaed8 \uc7a5\uc560\uac00 \ubc1c\uc0dd\ud55c\ub2e4\ud574\ub3c4 \ud2b9\uc815 GTID \ubd80\ud130 \ubcf5\uc81c\ub97c \uc7ac\uac1c\ud558\uba74 \ub41c\ub2e4. \\n\\n:::note GTID\\n\ubcf5\uc81c \ud1a0\ud3f4\ub85c\uc9c0\uc5d0 \ucc38\uc5ec\ud55c \ubaa8\ub4e0 \uc11c\ubc84\uc5d0\uc11c \uace0\uc720\ud558\ub3c4\ub85d \uac01 \uc774\ubca4\ud2b8\uc5d0 \ubd80\uc5ec\ub41c \uc2dd\ubcc4\uac12 \\n[source_id]:[transaction_id]\ub85c \uad6c\uc131\ub418\uba70, source_id\ub294 \uc11c\ubc84\ub97c \uc2dd\ubcc4\ud558\uae30 \uc704\ud55c \uac12\uc774\uace0 transaction_id\ub294 \ucee4\ubc0b\ub41c \ud2b8\ub79c\uc7ad\uc158\uc744 \uc2dd\ubcc4\ud558\uae30 \uc704\ud55c \uac12\uc73c\ub85c 1\uc529 \uc99d\uac00\ud558\ub294 \ud615\ud0dc\ub85c \ubc1c\uae09\ub41c\ub2e4. \\n:::\\n\\n### \ubcf5\uc81c \ud1a0\ud3f4\ub85c\uc9c0\\n\\n**\uc2f1\uae00 \ub808\ud50c\ub9ac\uce74 \ubcf5\uc81c \uad6c\uc131**\\n\\n\uac00\uc7a5 \uac04\ub2e8\ud55c \uad6c\uc131\uc73c\ub85c \uc81c\uc77c \ub9ce\uc774 \uc0ac\uc6a9\ud558\ub294 \ud615\ud0dc\ub2e4. \\nreplica \uc11c\ubc84\ub97c \uc77d\uae30 \uc804\uc6a9, \uc608\ube44 \uc11c\ubc84, \ubc31\uc5c5 \uc6a9\ub3c4\ub85c \ub9ce\uc774 \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n W[Web Server] -- \uc77d\uae30 + \uc4f0\uae30 --\x3e S\\n W -- \uc77d\uae30 --\x3e R\\n S[Source] --\x3e R[Replica]\\n```\\n\\n**\uba40\ud2f0 \ub808\ud50c\ub9ac\uce74 \ubcf5\uc81c \uad6c\uc131**\\n\\n2\uac1c\uc758 replica \uc11c\ubc84\ub97c \uc0ac\uc6a9\ud558\ub294 \ud615\ud0dc\ub2e4. \\n\ud558\ub098\uc758 replica\ub294 \uc608\ube44 \uc6a9\ub3c4\ub85c \ub0a8\uaca8\ub450\ub294 \ud615\ud0dc\ub2e4. \\n\ucd94\ud6c4\uc5d0 \ud2b8\ub798\ud53d\uc774 \uc99d\uac00\ud558\ub294 \uacbd\uc6b0 \uc608\ube44 \uc6a9\ub3c4\uc758 replica\ub97c \uc0ac\uc6a9\ud568\uc73c\ub85c \uc77d\uae30 \uc694\uccad\uc758 \ubd80\ud558 \ubd84\uc0b0\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\\n```mermaid\\ngraph LR\\n W[Web Server] -- \uc77d\uae30 + \uc4f0\uae30 --\x3e S\\n W -- \uc77d\uae30 --\x3e R1\\n S[Source] --\x3e R1[Replica1]\\n S --\x3e R2[Replica2]\\n```\\n\\n**\uccb4\uc778 \ubcf5\uc81c \uad6c\uc131**\\n\\nreplica \uc11c\ubc84\uac00 \ub9ce\uc740 \uacbd\uc6b0 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8\ub97c \uc804\ub2ec\ud558\ub294 \uc791\uc5c5 \uc790\uccb4\uac00 \ubd80\ud558\uac00 \ub420 \uc218 \uc788\ub2e4. \\n\ub530\ub77c\uc11c 1:M:M \uad6c\uc870\ub85c \uccb4\uc778 \ubcf5\uc81c \uad6c\uc131\uc744 \uace0\ub824\ud560 \uc218 \uc788\ub2e4. \\n\\n```mermaid\\ngraph LR\\n W[Web Server] -- \uc77d\uae30 + \uc4f0\uae30 --\x3e S\\n W -- \uc77d\uae30 --\x3e R1\\n S[Source] --\x3e R1[Replica1]\\n S --\x3e R2[Replica2]\\n S --\x3e R3[Replica3]\\n\\n R3 --\x3e R3-1[Replica 3-1]\\n R3 --\x3e R3-2[Replica 3-2]\\n\\n B[Batch Server] --\x3e R3-2\\n```\\n\\n**\ub4c0\uc5bc \uc18c\uc2a4 \ubcf5\uc81c \uad6c\uc131**\\n\\n2\uac1c\uc758 MySQL \uc11c\ubc84 \ubaa8\ub450 \uc77d\uae30\uc640 \uc4f0\uae30\uac00 \uac00\ub2a5\ud558\ub3c4\ub85d \ud558\ub294 \uad6c\uc131\uc774\ub2e4. \\n\uac01 \uc11c\ubc84\uc5d0\uc11c \ubcc0\uacbd\ub41c \ub370\uc774\ud130\ub294 \ub2e4\ub978 \uc11c\ubc84\uc5d0 \ubc18\uc601\ub41c\ub2e4. \\n\ubaa9\uc801\uc5d0 \ub530\ub77c ACTIVE-ACTIVE \ud615\ud0dc \ub610\ub294 ACTIVE-PASSIVE \ud615\ud0dc\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. \\nACTIVE-PASSIVE \ud615\ud0dc\uc778 \uacbd\uc6b0 \uc2f1\uae00 \ub808\ud50c\ub9ac\uce74 \ubcf5\uc81c \uad6c\uc131\uacfc \ub3d9\uc77c\ud574\ubcf4\uc774\uc9c0\ub9cc, ACTIVE \uc11c\ubc84\uc5d0\uc11c \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud558\uba74 \uc124\uc815\uc758 \ubcc0\uacbd\uc5c6\uc774 PASSIVE \uc11c\ubc84\ub85c \uc4f0\uae30 \uc791\uc5c5\uc744 \uc804\ud658\ud560 \uc218 \uc788\ub2e4\ub294 \uac83\uc774 \uc7a5\uc810\uc774\ub2e4. \\n\\n```mermaid\\ngraph LR\\n W[Web Server] -- \uc77d\uae30 + \uc4f0\uae30 --\x3e SR1\\n W -- \uc77d\uae30 + \uc4f0\uae30 --\x3e SR2\\n SR1[Source/Replica 1] --\x3e SR2[Source/Replica 2]\\n```\\n\\n:::note ACTIVE-ACTIVE, ACTIVE-PASSIVE\\nACTIVE-ACTIVE: 2\uac1c\uc758 \uc11c\ubc84 \ubaa8\ub450 \uc4f0\uae30 \uc791\uc5c5\uc744 \uc218\ud589\ud558\ub294 \ud615\ud0dc \\nACTIVE-PASSIVE: \ud558\ub098\uc758 \uc11c\ubc84\uc5d0\uc11c\ub9cc \uc4f0\uae30 \uc791\uc5c5\uc744 \uc218\ud589\ud558\ub294 \ud615\ud0dc\\n:::\\n\\n**\uba40\ud2f0 \uc18c\uc2a4 \ubcf5\uc81c \uad6c\uc131**\\n\\n\uc5ec\ub7ec\uac1c\uc758 source \uc11c\ubc84\uc640 \ud558\ub098\uc758 replica \uc11c\ubc84\ub97c \uc0ac\uc6a9\ud558\ub294 \uad6c\uc131\uc774\ub2e4. \\n\uc774\ub294 source \uc11c\ubc84\uc758 \ub370\uc774\ud130\ub97c \ud55c \uacf3\uc5d0 \ubc31\uc5c5\ud558\ub294 \uc6a9\ub3c4\ub85c \uc0ac\uc6a9, \uc5ec\ub7ec \uc11c\ubc84\uc5d0 \uc874\uc7ac\ud558\ub294 \ub370\uc774\ud130\ub97c \ud1b5\ud569, \uc0e4\ub529\ub418\uc5b4\uc788\ub294 \ud14c\uc774\ube14 \ub370\uc774\ud130\ub97c \ud1b5\ud569\ud560 \ub54c \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n S1[Source 1] --\x3e R[Replica]\\n S2[Source 2] --\x3e R\\n```\\n\\n## \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ubc29\uc2dd Replication \uad6c\uc131\ud558\uae30\\n\\nmysql 2\ub300\ub97c \uc774\uc6a9\ud558\uc5ec replication\uc744 \uad6c\uc131\ud558\uace0, spring boot application\uc73c\ub85c source, replica \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc811\uadfc\ud574\ubcf4\ub294 \uc608\uc81c\uc774\ub2e4. \\n[https://github.com/bbiac/db-replication](https://github.com/bbiac/db-replication) \\n\\n### MySQL \ud658\uacbd \uad6c\uc131\\n\\nMySQL \ubc84\uc804\uc740 8.1\uc744 \uc0ac\uc6a9\ud588\ub2e4. \\n13306, 13307 \ud3ec\ud2b8\ub97c \uc0ac\uc6a9\ud574\uc11c MySQL \uc11c\ubc84 2\ub300\ub97c \ub744\uc6e0\ub2e4. \\n\ub610\ud55c \uc0ac\uc2e4 IP \ub300\uc5ed\uc73c\ub85c \ud1b5\uc2e0\ud560 \uc218 \uc788\ub3c4\ub85d \ucee4\uc2a4\ud140 \ub124\ud2b8\uc6cc\ud06c\ub97c \ucd94\uac00\ud588\ub2e4. \\n\\n```yml\\nversion: \'3.8\'\\n\\nservices:\\n source:\\n platform: linux/x86_64\\n image: mysql:latest\\n restart: always\\n container_name: mysql-source\\n environment:\\n TZ: \'Asia/Seoul\'\\n MYSQL_DATABASE: \'db\'\\n MYSQL_USER: \'user\'\\n MYSQL_PASSWORD: \'password\'\\n MYSQL_ROOT_PASSWORD: \'password\'\\n ports:\\n - \\"13306:3306\\"\\n volumes:\\n - db-source:/var/lib/mysql\\n - db-source:/var/lib/mysql-files\\n - ./docker/source.cnf:/etc/mysql/my.cnf\\n networks:\\n - mysql_network\\n\\n replica:\\n platform: linux/x86_64\\n image: mysql:latest\\n restart: always\\n container_name: mysql-replica\\n environment:\\n TZ: \'Asia/Seoul\'\\n MYSQL_DATABASE: \'db\'\\n MYSQL_USER: \'user\'\\n MYSQL_PASSWORD: \'password\'\\n MYSQL_ROOT_PASSWORD: \'password\'\\n ports:\\n - \\"13307:3306\\"\\n volumes:\\n - db-replica:/var/lib/mysql\\n - db-replica:/var/lib/mysql-files\\n - ./docker/replica.cnf:/etc/mysql/my.cnf\\n networks:\\n - mysql_network\\n\\nvolumes:\\n db-source:\\n db-replica:\\n\\nnetworks:\\n mysql_network:\\n driver: bridge\\n```\\n\\n\ub610\ud55c source, replica \uac01\uac01 \ub2e4\uc74c\uacfc \uac19\uc774 db \uc124\uc815\uc744 \ud588\ub2e4. \\n\\n| \uc124\uc815 | \uc124\uba85 |\\n| --- | --- |\\n| server_id | \uac01\uac01\uc758 mysql \ub9c8\ub2e4 \uace0\uc720\ud55c \uac12\uc744 \uac00\uc838\uc57c \ud55c\ub2e4. |\\n| log_bin | \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ud30c\uc77c \uacbd\ub85c \uc124\uc815\uc73c\ub85c \uc808\ub300\uacbd\ub85c\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294\ub2e4\uba74 /var/lib/mysql \uc544\ub798 \ud574\ub2f9 log_bin\uc5d0 \uc124\uc815\ub41c \uac12\uc73c\ub85c \ub85c\uadf8\uac00 \uc0dd\uc131\ub41c\ub2e4. |\\n| sync_binlog | N\uac1c\uc758 \ud2b8\ub79c\uc7ad\uc158 \ub2f9 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8\ub97c \ub514\uc2a4\ud06c\uc640 \ub3d9\uae30\ud654 \uc791\uc5c5\uc744 \ud558\ub3c4\ub85d \ud55c\ub2e4.\xa01\uc740 \uae30\ubcf8\uac12\uc73c\ub85c \uc548\uc815\uc801\uc774\uc9c0\ub9cc, \uac00\uc7a5 \ub290\ub9ac\ub2e4. |\\n| relay_log | \ub9b4\ub808\uc774 \ub85c\uadf8 \ud30c\uc77c \uacbd\ub85c \uc124\uc815 |\\n| relay_log_purge | \ud544\uc694 \uc5c6\ub294 \ub9b4\ub808\uc774 \ub85c\uadf8 \ud30c\uc77c\uc744 \uc790\ub3d9\uc73c\ub85c \uc0ad\uc81c\ud558\ub294 \uc635\uc158 |\\n| read_only | \uc77d\uae30 \uc804\uc6a9 \uc124\uc815 |\\n| log_replica_updates | Replication SQL Thread\ub85c \uc778\ud574 \uc2e4\ud589\ub418\ub294 \uc815\ubcf4\ub97c \ubc14\uc774\ub108\ub9ac \ub85c\uadf8\uc5d0 \uae30\ub85d \ucd94\ud6c4\uc5d0 \uc18c\uc2a4 \uc11c\ubc84\ub85c \uc2b9\uaca9\ub418\ub294 \uacbd\uc6b0\ub97c \uace0\ub824\ud558\uba74 \uc124\uc815\ud558\ub294 \uac83\uc774 \uc88b\ub2e4. |\\n\\nimport Tabs from \\"@theme/Tabs\\";\\nimport TabItem from \\"@theme/TabItem\\";\\n\\n\\n\\n\\n```cnf title=\\"/docker/source.cnf\\"\\n[mysqld]\\nserver_id=1\\nlog_bin=mysql-bin\\nsync_binlog=1\\n```\\n\\n\\n\\n\\n\\n```cnf title=\\"/docker/replica.cnf\\"\\n[mysqld]\\nserver_id=2\\nrelay_log=mysql-relay-bin\\nrelay_log_purge=ON\\nread_only\\nlog_replica_updates\\n```\\n\\n\\n\\n\\n### \ub3c4\ucee4 \uc2e4\ud589\\n\\ndocker-compose up \uba85\ub839\uc5b4\ub85c docker-compose \uc124\uc815\uc73c\ub85c docker\ub97c \ub744\uc6b4\ub2e4. \\n-d \uc635\uc158\uc744 \ubd99\uc774\uba74 \ubc31\uadf8\ub77c\uc6b4\ub4dc \ubaa8\ub4dc\ub85c \uc2e4\ud589\ub41c\ub2e4. \\n\\n```\\ndocker-compose up -d\\n```\\n\\n### replication slave \uad8c\ud55c \uc124\uc815\\n\\nREPLICATION SLAVE \uad8c\ud55c\uc774 \uc124\uc815\ub418\uc5b4 \uc788\uc5b4\uc57c replica \uc11c\ubc84\uc5d0\uc11c source \uc11c\ubc84\uc5d0 \uc811\uadfc\ud558\uc5ec \ub85c\uadf8\ub97c \uc77d\uc5b4\uc62c \uc218 \uc788\ub2e4. \\nsource \uc11c\ubc84\uc5d0 \uc811\uadfc\ud558\uc5ec user \uacc4\uc815\uc5d0 \ud574\ub2f9 \uad8c\ud55c\uc744 \uc124\uc815\ud574\uc900\ub2e4. \\n\\nSOURCE \uc811\uc18d\\n\\n```bash\\ndocker exec -it mysql-source mysql -u root -p\\n```\\n\\nuser \uacc4\uc815\uc5d0 REPLICATION SLAVE \uad8c\ud55c \ucd94\uac00\\n\\n```mysql\\nGRANT REPLICATION SLAVE ON *.* TO \'user\'@\'%\';\\nFLUSH PRIVILEGES;\\n```\\n\\n### SOURCE DB \uc815\ubcf4 \ud655\uc778\\n\\nreplica \uc124\uc815\uc5d0 \ud544\uc694\ud55c source db\uc758 \ubc14\uc774\ub108\ub9ac \ub85c\uadf8 \ud30c\uc77c\uba85\uacfc Position\uc744 \ud655\uc778\ud55c\ub2e4. \\nPosition \uac12\uc740 \uc2e4\uc81c \ud30c\uc77c\uc758 \ubc14\uc774\ud2b8 \uc218\ub97c \uc758\ubbf8\ud55c\ub2e4. \\n\ud655\uc778\ud55c File(SOURCE_LOG_FILE)\uacfc Position(SOURCE_LOG_POS) \uac12\uc740 replica \uc124\uc815\uc5d0\uc11c \uc0ac\uc6a9\ud55c\ub2e4.\\n\\n```mysql\\nSHOW MASTER STATUS;\\n\\n+------------------+----------+--------------+------------------+-------------------+\\n| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |\\n+------------------+----------+--------------+------------------+-------------------+\\n| mysql-bin.000003 | 1082 | | | |\\n+------------------+----------+--------------+------------------+-------------------+\\n```\\n\\n### SOURCE ip \uc8fc\uc18c \ud655\uc778\\n\\ndocker inspect -f \uc635\uc158\uc744 \uc0ac\uc6a9\ud558\uba74 \ud574\ub2f9 \ucee8\ud14c\uc774\ub108\uc758 \uc138\ubd80 \uc815\ubcf4\ub97c \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\ub2e4\uc74c \uba85\ub839\uc5b4\ub97c \uc774\uc6a9\ud574 docker-compose \ud30c\uc77c\uc5d0 \uc124\uc815\ud574\ub454 mysql_network\uc5d0\uc11c \uc0ac\uc6a9\ub418\ub294 \uc0ac\uc124 \uc544\uc774\ud53c \uc8fc\uc18c\ub97c \ud655\uc778\ud55c\ub2e4. \\n\\n```bash\\ndocker inspect -f \\"{{with index .NetworkSettings.Networks \\\\\\"db-replication_mysql_network\\\\\\"}}{{.IPAddress}}{{end}}\\" mysql-source\\n```\\n\\nip \uc8fc\uc18c\uac00 \ub098\uc624\uc9c0 \uc54a\ub294 \uacbd\uc6b0 docker inspect mysql-source\ub85c \ud655\uc778\ud55c\ub2e4. \\n\ud655\uc778\ud55c IP\uc8fc\uc18c(SOURCE_HOST) \uac12\uc740 replica \uc124\uc815\uc5d0\uc11c \uc0ac\uc6a9\ud55c\ub2e4.\\n\\n### replica mysql \uc811\uc18d\\n\\nsource db\uc5d0 \uc811\uc18d\ud588\ub358 \ubc29\ubc95\uacfc \ub3d9\uc77c\ud558\uac8c replica db\uc5d0 \uc811\uc18d\ud55c\ub2e4. \\n\\n```bash\\ndocker exec -it mysql-replica mysql -u root -p\\n```\\n\\n### replica \uc124\uc815\\n\\n\uc774\uc804\uc5d0 source db\uc5d0\uc11c \uc5bb\uc5c8\ub358 \uc815\ubcf4\ub4e4\uc744 \uc0ac\uc6a9\ud558\uc5ec replica \uc124\uc815\uc744 \uc9c4\ud589\ud55c\ub2e4. \\n\uc2e4\uc81c DB \uc11c\ubc84\uc5d0\uc11c \ubcf5\uc81c\ud558\ub294 \uacbd\uc6b0 \ucd94\uac00\uc801\uc73c\ub85c source DB\uc758 \ud30c\uc77c\uc744 \ubcf5\uc81c\ud574\uc57c\ud558\uc9c0\ub9cc \ud604\uc7ac \ubcf5\uc81c\ud560 \ub370\uc774\ud130\uac00 \uc5c6\uae30 \ub54c\ubb38\uc5d0 \ud574\ub2f9 \ubd80\ubd84\uc740 \uc0dd\ub7b5\ud588\ub2e4. \\nSOURCE_HOST, SOURCE_LOG_FILE, SOURCE_LOG_POS \ub97c \uc801\uc808\ud788 \ubcc0\uacbd\ud55c\ub2e4.\\n\\n```mysql\\nSTOP REPLICA;\\n\\nCHANGE REPLICATION SOURCE TO \\nSOURCE_HOST=\'172.29.0.2\', \\nSOURCE_USER=\'user\', \\nSOURCE_PASSWORD=\'password\', \\nSOURCE_LOG_FILE=\'mysql-bin.000001\', \\nSOURCE_LOG_POS=0, \\nGET_SOURCE_PUBLIC_KEY=1;\\n\\nSTART REPLICA;\\n```\\n\\n### \uc124\uc815 \ud655\uc778\\n\\n```mysql\\nSHOW REPLICA STATUS;\\n\\n+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+\\n| Replica_IO_State | Source_Host | Source_User | Source_Port | Connect_Retry | Source_Log_File | Read_Source_Log_Pos | Relay_Log_File | Relay_Log_Pos | Relay_Source_Log_File | Replica_IO_Running | Replica_SQL_Running | Replicate_Do_DB | Replicate_Ignore_DB | Replicate_Do_Table | Replicate_Ignore_Table | Replicate_Wild_Do_Table | Replicate_Wild_Ignore_Table | Last_Errno | Last_Error | Skip_Counter | Exec_Source_Log_Pos | Relay_Log_Space | Until_Condition | Until_Log_File | Until_Log_Pos | Source_SSL_Allowed | Source_SSL_CA_File | Source_SSL_CA_Path | Source_SSL_Cert | Source_SSL_Cipher | Source_SSL_Key | Seconds_Behind_Source | Source_SSL_Verify_Server_Cert | Last_IO_Errno | Last_IO_Error | Last_SQL_Errno | Last_SQL_Error | Replicate_Ignore_Server_Ids | Source_Server_Id | Source_UUID | Source_Info_File | SQL_Delay | SQL_Remaining_Delay | Replica_SQL_Running_State | Source_Retry_Count | Source_Bind | Last_IO_Error_Timestamp | Last_SQL_Error_Timestamp | Source_SSL_Crl | Source_SSL_Crlpath | Retrieved_Gtid_Set | Executed_Gtid_Set | Auto_Position | Replicate_Rewrite_DB | Channel_Name | Source_TLS_Version | Source_public_key_path | Get_Source_public_key | Network_Namespace |\\n+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+\\n| Waiting for source to send event | 172.25.0.3 | user | 3306 | 60 | mysql-bin.000003 | 1082 | mysql-relay-bin.000002 | 868 | mysql-bin.000003 | Yes | Yes | | | | | | | 0 | | 0 | 1082 | 1078 | None | | 0 | No | | | | | | 0 | No | 0 | | 0 | | | 1 | 5a396b02-41c6-11ee-a56d-0242ac190003 | mysql.slave_master_info | 0 | NULL | Replica has read all relay log; waiting for more updates | 86400 | | | | | | | | 0 | | | | | 1 | |\\n+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+\\n```\\n\\nReplica_IO_Running, Replica_SQL_Running \uac12\uc774 YES\ub77c\uba74 \uc815\uc0c1\uc801\uc73c\ub85c replication \uad6c\uc131\uc774 \uc644\ub8cc\ub41c \uac83\uc774\ub2e4. \\n\\n\uc124\uc815\uc744 \ub9c8\uce5c \ud6c4 source db\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc774 create table \uba85\ub839\uc5b4\ub97c \uc785\ub825\ud55c\ub2e4. \\nreplica db\uc5d0 \ub3d9\uc77c\ud55c member table\uc774 \uc0dd\uc131\ub41c \uac83\uc744 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n```sql\\nCREATE TABLE member\\n(\\n id BIGINT PRIMARY KEY AUTO_INCREMENT,\\n name VARCHAR(255)\\n);\\n```\\n\\n## \uc2a4\ud504\ub9c1 \ubd80\ud2b8\ub85c DB \uc811\uadfc\ud558\uae30\\n\\n\uc77c\ubc18\uc801\uc778 \ud2b8\ub79c\uc7ad\uc158\uc758 \uacbd\uc6b0 source, \uc77d\uae30 \uc804\uc6a9 \ud2b8\ub79c\uc7ad\uc158\uc778 \uacbd\uc6b0 replica\ub85c \uc694\uccad\uc774 \uac00\ub3c4\ub85d \uad6c\uc131\ud574\ubcf4\uc790. \\n\\n### Environment \uc124\uc815\\n\\n\ub2e4\uc74c\uacfc \uac19\uc774 source, replica\ub85c \uad6c\ubd84\ud558\uc5ec \uc124\uc815\ud55c\ub2e4. \\n\\n```yml title=\\"application.yml\\"\\nspring:\\n datasource:\\n source:\\n username: user\\n password: password\\n driver-class-name: com.mysql.cj.jdbc.Driver\\n jdbc-url: jdbc:mysql://localhost:13306/db\\n replica:\\n username: user\\n password: password\\n driver-class-name: com.mysql.cj.jdbc.Driver\\n jdbc-url: jdbc:mysql://localhost:13307/db\\n```\\n\\n### DataSourceType \uc124\uc815\\n\\n\ub2e8\uc21c \ubb38\uc790\uc5f4\ub85c\ub3c4 \uad6c\ubd84\ud560 \uc218 \uc788\uc9c0\ub9cc, enum\uc744 \uc774\uc6a9\ud574\uc11c \ud2b8\ub79c\uc7ad\uc158\uc744 \uad6c\ubd84\ud558\ub3c4\ub85d \uc0dd\uc131\ud55c\ub2e4. \\nKey\ub294 \ucd94\ud6c4\uc5d0 \ube48 \uc124\uc815\uc5d0 \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n```java title=\\"DataSourceType\\"\\npublic enum DataSourceType {\\n SOURCE(SOURCE_NAME),\\n REPLICA(REPLICA_NAME),\\n ;\\n\\n private final String key;\\n\\n DataSourceType(String key) {\\n this.key = key;\\n }\\n\\n public static class Key {\\n public static final String ROUTING_NAME = \\"ROUTING\\";\\n public static final String SOURCE_NAME = \\"SOURCE\\";\\n public static final String REPLICA_NAME = \\"REPLICA\\";\\n }\\n}\\n```\\n\\n### AbstractRoutingDataSource \uc124\uc815\\n\\n\uc2a4\ud504\ub9c1\uc774 \uc9c0\uc6d0\ud574\uc8fc\ub294 AbstractRoutingDataSource\ub97c \uc0c1\uc18d\ubc1b\uc544 \ud2b8\ub79c\uc7ad\uc158\uc758 \uc77d\uae30 \uc5ec\ubd80\uc5d0 \ub530\ub77c \ub2e4\ub978 DataSource\ub97c \ud5a5\ud558\ub3c4\ub85d \uc124\uc815\ud55c\ub2e4. \\n\\n\uc815\uc801 \ud329\ud130\ub9ac \uba54\uc11c\ub4dc\ub294 Map\uc5d0 \ud574\ub2f9\ud558\ub294 \uac12\uc744 \ubc1b\uc544 \ub370\uc774\ud130 \uc18c\uc2a4\ub97c \uc124\uc815\ud55c\ub2e4. \\n- setDefaultTargetDataSource: \uae30\ubcf8 \ub370\uc774\ud130 \uc18c\uc2a4\ub97c \uc124\uc815\ud55c\ub2e4. \\n- setTargetDataSources: \ub9f5 \ud615\ud0dc\ub85c \ubc1b\uc740 \ub370\uc774\ud130 \uc18c\uc2a4 \uac12\ub4e4\uc744 \uc124\uc815\ud55c\ub2e4. \\n\\ndetermineCurrentLookupKey\ub97c \uc624\ubc84\ub77c\uc774\ub529\ud558\uc5ec \ud2b8\ub79c\uc7ad\uc158\uc758 \uc77d\uae30 \uc5ec\ubd80\uc5d0 \ub530\ub77c \ub2e4\ub978 DataSourceType\uc744 \ubc18\ud658\ud558\ub3c4\ub85d \uc124\uc815\ud55c\ub2e4. \\n- isCurrentTransactionReadOnly() \uba54\uc11c\ub4dc\ub97c \ud1b5\ud574 \ud2b8\ub79c\uc7ad\uc158\uc774 \uc77d\uae30 \uc804\uc6a9\uc778\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n- DataSourceType\uc744 \ubc18\ud658\ud558\ub3c4\ub85d \uc124\uc815\ud558\uace0, \ubc18\ud658\ud55c \uac12\uc5d0 \ud574\ub2f9\ud558\ub294 \ub370\uc774\ud130 \uc18c\uc2a4\uac00 \uc0ac\uc6a9\ub41c\ub2e4. \\n\\n```java title=\\"RoutingDataSource\\"\\npublic class RoutingDataSource extends AbstractRoutingDataSource {\\n\\n private final Logger log = LoggerFactory.getLogger(getClass());\\n\\n public static RoutingDataSource from(Map dataSources) {\\n RoutingDataSource routingDataSource = new RoutingDataSource();\\n routingDataSource.setDefaultTargetDataSource(dataSources.get(DataSourceType.SOURCE));\\n routingDataSource.setTargetDataSources(dataSources);\\n return routingDataSource;\\n }\\n\\n @Override\\n protected Object determineCurrentLookupKey() {\\n boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();\\n\\n if (readOnly) {\\n log.info(\\"readOnly = true, request to replica\\");\\n return DataSourceType.REPLICA;\\n }\\n log.info(\\"readOnly = false, request to source\\");\\n return DataSourceType.SOURCE;\\n }\\n}\\n```\\n\\n### DataSource \uc124\uc815\\n\\n\uc704\uc5d0\uc11c\ubd80\ud130 \uc21c\uc11c\ub300\ub85c Source, Replica, RoutingDataSource, LazyConnectionDataSourceProxy \uc124\uc815\uc774\ub2e4. \\n\uc2a4\ud504\ub9c1\uc740 \ud2b8\ub79c\uc7ad\uc158 \uc2dc\uc791\uc2dc\uc5d0 \ucee4\ub125\uc158\uc758 \uc0ac\uc6a9\uc5ec\ubd80\uc640 \uc0c1\uad00\uc5c6\uc774 \ucee4\ub125\uc158\uc744 \ud655\ubcf4\ud55c\ub2e4. \\n\ub530\ub77c\uc11c readOnly \ud2b8\ub79c\uc7ad\uc158\uc774 \uc124\uc815\ub41c \uba54\uc11c\ub4dc\ub97c \uc0ac\uc6a9\ud558\ub354\ub77c\ub3c4 \ubbf8\ub9ac \ud655\ubcf4\ub41c \ucee4\ub125\uc158\uc744 \uc0ac\uc6a9\ud558\uae30 \ub54c\ubb38\uc5d0 replica db\ub85c \uc694\uccad\uc744 \ud558\uc9c0 \uc54a\uace0 setDefaultTargetDataSource\ub85c \uc124\uc815\ud55c source db\ub85c \uc694\uccad\uc744 \ud55c\ub2e4. \\nLazyConnectionDataSourceProxy\ub97c \uc124\uc815\ud558\ub294 \uacbd\uc6b0 \uc2e4\uc81c DataSource\ub97c \uc0ac\uc6a9\ud558\ub294 \uc2dc\uc810\uc5d0 \ucee4\ub125\uc158\uc744 \ud68d\ub4dd\ud574\uc11c \uc0ac\uc6a9\ud558\uae30 \ub54c\ubb38\uc5d0 \uc124\uc815\ud55c\ub300\ub85c replica db\ub85c \uc870\ud68c \uc694\uccad\uc744 \ud55c\ub2e4. \\n\\n```java title=\\"DataSourceConfiguration\\"\\n@Configuration\\npublic class DataSourceConfiguration {\\n\\n @Bean\\n @Qualifier(SOURCE_NAME)\\n @ConfigurationProperties(prefix = \\"spring.datasource.source\\")\\n public DataSource sourceDataSource() {\\n return DataSourceBuilder.create().build();\\n }\\n\\n @Bean\\n @Qualifier(REPLICA_NAME)\\n @ConfigurationProperties(prefix = \\"spring.datasource.replica\\")\\n public DataSource replicaDataSource() {\\n return DataSourceBuilder.create().build();\\n }\\n\\n @Bean\\n @Qualifier(ROUTING_NAME)\\n public DataSource routingDataSource(\\n @Qualifier(SOURCE_NAME) DataSource sourceDataSource,\\n @Qualifier(REPLICA_NAME) DataSource replicaDataSource\\n ) {\\n return RoutingDataSource.from(Map.of(\\n DataSourceType.SOURCE, sourceDataSource,\\n DataSourceType.REPLICA, replicaDataSource\\n ));\\n }\\n\\n @Bean\\n @Primary\\n public DataSource dataSource(\\n @Qualifier(ROUTING_NAME) DataSource routingDataSource\\n ) {\\n return new LazyConnectionDataSourceProxy(routingDataSource);\\n }\\n}\\n```\\n\\n\ucd5c\uc885\uc801\uc73c\ub85c DataSource \ube48\uc740 \ub2e4\uc74c\uacfc \uac19\uc740 \ud615\ud0dc\uac00 \ub41c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n DSP[LazyConnectionDataSourceProxy] --\x3e RDS[RoutingDataSource]\\n\\tRDS --\x3e S[SourceDataSource]\\n\\tRDS --\x3e R[ReplicaDataSource]\\n```\\n\\n### \ub3d9\uc791 \ud655\uc778\\n\\n\uac04\ub2e8\ud558\uac8c \ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud574\uc11c \uc124\uc815\ud55c\ub300\ub85c \ub3d9\uc791\uc774 \ub418\ub294\uc9c0 \ud655\uc778\ud574\ubcf4\uc558\ub2e4. \\nsave \uba54\uc11c\ub4dc\uc758 \uacbd\uc6b0 `@Transactional`, findById \uba54\uc11c\ub4dc\uc758 \uacbd\uc6b0 `@Transactional(readOnly = true)`\uac00 \uc124\uc815\ub418\uc5b4\uc788\ub2e4. \\n\ub85c\uadf8\ub97c \ud1b5\ud574 save\uc758 \uacbd\uc6b0 source db\ub85c findById\uc758 \uacbd\uc6b0 replica db\ub85c \uc694\uccad\uc744 \ud558\ub294 \uac83\uc744 \uc54c \uc218 \uc788\ub2e4. \\n\\n```java title=\\"MemberServiceTest\\"\\n@SpringBootTest\\nclass MemberServiceTest {\\n\\n @Autowired\\n private MemberService memberService;\\n\\n @Test\\n void \uc0ac\uc6a9\uc790\ub97c_\uc800\uc7a5\ud55c\ub2e4() {\\n // RoutingDataSource log: readOnly = false\\n memberService.save(\\"bbiac\\");\\n }\\n\\n @Test\\n void \uc0ac\uc6a9\uc790\ub97c_\uc870\ud68c\ud55c\ub2e4() {\\n // RoutingDataSource log: readOnly = true\\n assertThatThrownBy(() -> memberService.findById(MAX_VALUE))\\n .isInstanceOf(NoSuchElementException.class);\\n }\\n}\\n```\\n\\nDB\uc5d0\uc11c\ub294 \ud655\uc778\ud558\ub824\uba74 root \uacc4\uc815\uc73c\ub85c \uc811\uc18d\ud55c \ud6c4 general log\ub97c \ud65c\uc131\ud654 \uc2dc\ud0a8\ub2e4. \\n\\n```sql\\nSET GLOBAL log_output = \'table\';\\nSET GLOBAL general_log = 1;\\n```\\n\\ngeneral log\ub97c \ud65c\uc131\ud654 \ud55c \ud6c4 \uc77d\uae30 \uc804\uc6a9 \uba54\uc11c\ub4dc\ub97c \uc2e4\ud589\ud55c\ub2e4. \\nserver_id, \uc2e4\ud589\ud55c \ucffc\ub9ac\ubb38\uc744 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n```sql\\nSELECT user_host, thread_id, server_id, convert(argument using utf8) FROM mysql.general_log where argument like \'%select%\';\\n\\n+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+\\n| user_host | thread_id | server_id | convert(argument using utf8) |\\n+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+\\n| user[user] @ [172.25.0.1] | 277 | 2 | select m1_0.id,m1_0.name from member m1_0 where m1_0.id=9223372036854775807 |\\n+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+\\n```\\n\\n\ud655\uc778 \ud6c4 general log\ub97c \ube44\ud65c\uc131\ud654 \ud55c \ud6c4 \ube44\ud65c\uc131\ud654 \ub418\uc5c8\ub294\uc9c0 \ud655\uc778\ud55c\ub2e4. \\n```sql\\nSET GLOBAL general_log = 0;\\nSHOW VARIABLES LIKE \'%general%\';\\n\\n+------------------+---------------------------------+\\n| Variable_name | Value |\\n+------------------+---------------------------------+\\n| general_log | OFF |\\n| general_log_file | /var/lib/mysql/4b6b9db98290.log |\\n+------------------+---------------------------------+\\n```\\n\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n16\uc7a5 \ubcf5\uc81c, Real MySQL 8.0 - \ubc31\uc740\ube48, \uc774\uc131\uc6b1 \\n[Replication, MySQL Docs](https://dev.mysql.com/doc/refman/8.1/en/replication.html) \\n[MySql - Master Slave Replication \uad6c\uc870 \ub9cc\ub4e4\uc5b4\ubcf4\uae30](https://huisam.tistory.com/entry/mysql-replication) \\n[Spring \ub808\ud50c\ub9ac\ucf00\uc774\uc158 \ud2b8\ub79c\uc7ad\uc158 \ucc98\ub9ac \ubc29\uc2dd](https://cheese10yun.github.io/spring-transaction/) \\n[replication-datasource](https://github.com/kwon37xi/replication-datasource) \\n[Simplified Guide to MySQL Replication with Docker Compose](https://www.linkedin.com/pulse/simplified-guide-mysql-replication-docker-compose-rakesh-shekhawat/) \\n[Dockerfile\uc5d0\uc11c \uc790\uc8fc \uc4f0\uc774\ub294 \uba85\ub839\uc5b4](https://www.daleseo.com/dockerfile/) \\n[CHANGE REPLICATION SOURCE TO Statement](https://dev.mysql.com/doc/refman/8.1/en/change-replication-source-to.html) \\n[LazyConnectionDataSourceProxy](https://kwonnam.pe.kr/wiki/springframework/lazyconnectiondatasourceproxy) \\n[\ub370\uc774\ud130\ubca0\uc774\uc2a4 \ub808\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \ud1b5\ud55c \ucffc\ub9ac \uc131\ub2a5 \uac1c\uc120 (feat. Mysql, SpringBoot)](https://hudi.blog/database-replication-with-springboot-and-mysql/) \\n[\ubd80\ud558 \ubd84\uc0b0\uc744 \uc704\ud55c MySQL Replication \uad6c\uc131 \ubc0f \ucffc\ub9ac \uc694\uccad \ubd84\uae30](https://chagokx2.tistory.com/100) \\n[Use Docker Compose, Docker](https://docs.docker.com/get-started/08_using_compose/)"},{"id":"woowacourse-level3-retrospective","metadata":{"permalink":"/woowacourse-level3-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-08-19-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca83 \ud68c\uace0/2023-08-19-\ub808\ubca8 3 \ud68c\uace0.mdx","source":"@site/blog/2023-3/2023-08-19-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca83 \ud68c\uace0/2023-08-19-\ub808\ubca8 3 \ud68c\uace0.mdx","title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 3 \ud68c\uace0","description":"\ud68c\uace0","date":"2023-08-19T00:00:00.000Z","formattedDate":"2023\ub144 8\uc6d4 19\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":3.945,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 3 \ud68c\uace0","slug":"woowacourse-level3-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30","permalink":"/db-replication"},"nextItem":{"title":"CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131","permalink":"/cloudwatch"}},"content":"import logo from \'./logo.png\';\\n\\n### \ud68c\uace0\\n\\n\uc9c0\ub09c 8\uc8fc\ub294 \ub808\ubca8 1, 2 \ub54c\ubcf4\ub2e4 5\ubc30 \uc815\ub3c4 \ube60\ub974\uac8c \uc9c0\ub098\uac04 \uac83 \uac19\uc740 \ub290\ub08c\uc774 \ub4e4\uc5c8\ub2e4. \\n\ub808\ubca8 3\uc5d0\ub294 \uae30\uc220\uc801\uc778 \ubd80\ubd84\uc5d0\uc11c\ub3c4, \uae30\uc220 \uc678\uc801\uc778 \ubd80\ubd84\uc5d0\uc11c\ub3c4 \ubd80\uc871\ud568\uc774 \ub9ce\uc774 \ubcf4\uc600\ub358 \uac83 \uac19\ub2e4. \\n\ubd80\uc871\ud55c \ubd80\ubd84\uc744 \uc54c\uc558\uae30\uc5d0, \uc55e\uc73c\ub85c \ub354\uc6b1 \uc131\uc7a5\ud560 \uc218 \uc788\uc744 \uac83 \uac19\ub2e4. \\n\ub0b4\uac00 \ubd80\uc871\ud588\ub358 \ubd80\ubd84\uc744 \ud300\uc6d0\ub4e4\uc774 \uc798 \ubcf4\ucda9\ud574 \uc918\uc11c \ub4e0\ub4e0\ud588\ub2e4. \\n\\n### \uc544\uc26c\uc6b4 \uc810\\n\\n**\ubb38\uc11c\ud654**\\n\\n\uac1c\uc778\uc801\uc73c\ub85c\ub294 \uae30\uc220 \uc678\uc801\uc73c\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\uc744 \uc798 \uc815\ub9ac\ud558\uc9c0 \ubabb\ud588\ub2e4. \\n\ud504\ub85c\uc81d\ud2b8\ub97c \uc9c4\ud589\ud558\uba74\uc11c \ub0b4\uac00 \ud55c \ubd80\ubd84\uc744 \uc870\uae08 \ub354 \uaf3c\uaf3c\ud558\uac8c, \uc774\ud574\ud558\uae30 \uc27d\uac8c \ubb38\uc11c\ud654\ub97c \ud588\ub354\ub77c\uba74 \ud300\uc6d0\ub4e4\uc5d0\uac8c \ub354\uc6b1 \ub3c4\uc6c0\uc774 \ub418\uc5c8\uc744 \ud150\ub370 \uc774 \ubd80\ubd84\uc5d0 \uc2dc\uac04\uc744 \uc870\uae08 \ub354 \ud22c\uc790\ud558\uc9c0 \ubabb\ud588\ub358 \ubd80\ubd84\uc5d0\uc11c \uc544\uc26c\uc6c0\uc774 \ub9ce\uc774 \ub4e4\uc5c8\ub2e4. \\n\ubc29\ud559 \uae30\uac04 \ub3d9\uc548 \ubb38\uc11c\ud654\ub97c \ud558\uc9c0 \ubabb\ud588\ub358 \ubd80\ubd84\uc744 \uac1c\uc778 \ube14\ub85c\uadf8 \uc62c\ub9ac\uba74\uc11c \uc870\uae08 \ub354 \ucc44\uc6cc\ubcf4\ub824\uace0 \ud55c\ub2e4. \\n\\n**\ub0b4\uac00 \ubabb\ud558\ub294 \ubd80\ubd84\uc774\ub77c\uba74 \uc2dc\uac04\uc744 \ub4e4\uc774\uc790**\\n\\n\uc798 \ubabb\ud558\ub294 \ubd80\ubd84\uc774\ub77c\uba74 \uc2dc\uac04\uc744 \ub4e4\uc5ec\uc11c\ub77c\ub3c4 \uc911\uac04\uc740 \uac00\ub3c4\ub85d \ud574\uc57c\uaca0\ub2e4\ub294 \uc0dd\uac01\uc774 \ub9ce\uc774 \ub4e4\uc5c8\ub2e4. \\n\ub9d0\uc744 \ud558\uae30 \uc804\uc5d0 \uc815\ub9ac\ud574\uc11c \uc758\uacac\uc744 \ub0b4\ub294 \uac83, \ubc1c\ud45c \uc900\ube44, \uac10\uc815 \uc870\uc808 \ub4f1\ub4f1\\n\ubabb\ud558\ub294 \ubd80\ubd84\uc744 \uc778\uc9c0\ud558\uace0, \uac1c\uc120\ud558\uc790. \\n\\n**\ucef4\ud3ec\ud2b8 \uc874 \ubc97\uc5b4\ub098\uae30**\\n\\n\uc870\uae08 \ub354 \ub3c4\uc804\uc801\uc73c\ub85c \ubaa9\ud45c\ub97c \uc7a1\uc558\uc73c\uba74 \uc88b\uc558\uc744 \uac83 \uac19\ub2e4. \\n\ub9e4\ubc88 \uadfc\uac70\ub97c \uac00\uc9c0\uace0 \uae30\uc220\uc744 \ub3c4\uc785\ud558\uace0, \ucf54\ub4dc\ub97c \uc791\uc131\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. \\n\ud558\uc9c0\ub9cc \uc9c0\uc18d\uc801\uc73c\ub85c \uac1c\uc120\ud558\ub824\uace0 \ud558\ub294 \ubd80\ubd84\uc774 \ub2e4\uc18c \ubd80\uc871\ud588\ub2e4. \\n\\n### \uc88b\uc558\ub358 \uc810\\n\\n**\uc88b\uc558\ub358 \uc810\ub3c4 \ubb38\uc11c\ud654**\\n\\n[\ud300 \ube14\ub85c\uadf8](https://tripdraw.blog)\ub3c4 \uba3c\uc800 \ub3c4\uc785\ud558\uc790\uace0 \uc81c\uc548\ud558\uace0, \ub0b4\uac00 \ud588\ub358 \ubd80\ubd84\uc740 \ubb38\uc11c\ud654\ub97c \uaf64 \ub9ce\uc774 \ud574\uc11c \ud300\uc6d0\ub4e4\uacfc \uacf5\uc720\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\ubc31\uc5d4\ub4dc \ud06c\ub8e8 4\uba85\uc774\uc11c \uac19\uc774 \ud55c \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c\ub294 \uae30\ub2a5 \uad6c\ud604\ud55c\ub2e4\uace0 \ubb38\uc11c\ud654\uac00 \uc870\uae08 \ubbf8\ud761\ud574\uc11c \ubcf4\ucda9\uc744 \ud574\uc57c\uaca0\ub2e4. \\n\\n**\ub0b4\uac00 \ub514\uc790\uc778\ud55c \ud2b8\ub9bd\ub4dc\ub85c\uc6b0 \ub85c\uace0**\\n\\n\\n\\n\ud2b8\ub9bd\ub4dc\ub85c\uc6b0 \ub85c\uace0\ub97c \ub9cc\ub4e4\uc5c8\ub2e4. \\n\ud300\uc6d0\ub4e4\uc774 \ub300\ud45c \uc0c9\uc0c1(\ud30c\ub780\uc0c9)\uc744 \uc815\ud574\uc92c\uace0, \uc8fc\ub9d0 \ub3d9\uc548 \uc2e0\ub098\uac8c \ub85c\uace0 \ub514\uc790\uc778\uc744 \ud588\ub358 \uac83 \uac19\ub2e4. \\n\uc544\ub798\uc758 D \ubd80\ubd84\uc740 \uc720\ud29c\ube0c \uac15\uc758 \ub4e4\uc73c\uba74\uc11c \uc9c1\uc811 \ub9cc\ub4e4\uc5b4\uc11c \ubfcc\ub4ef\ud558\ub2e4. \\n\\n**\uae30\uc220 \uc120\ud0dd\uc758 \uc774\uc720**\\n\\n\uae30\uc220\uc758 \ud559\uc2b5 \ube44\uc6a9, \ud604\uc7ac \uad6c\uc870\uc5d0 \uc801\ud569\ud55c\uc9c0, \uc2e4\uc81c \uac00\uc9c0\uace0 \uc788\ub294 \ub9ac\uc18c\uc2a4\ub97c \uace0\ub824\ud574\uc11c \uae30\uc220 \uc120\ud0dd\uc744 \ud558\uace0, \ub3c4\uc785\ud588\ub358 \ubd80\ubd84\uc774 \uc88b\uc558\ub2e4. \\n100% \uc88b\uc740 \uc120\ud0dd\uc77c \uc21c \uc5c6\uc9c0\ub9cc, \uadf8\ub798\ub3c4 \uc120\ud0dd\uc5d0 \ub300\ud55c \uadfc\uac70\uac00 \uc874\uc7ac\ud55c\ub2e4\uba74 \ud655\ub960\uc744 \ub192\ud600\uc8fc\ub294 \uac83 \uac19\ub2e4. \\n\\n### \ub9c8\uce58\uba70\\n\\n\ud50c\ub808\uc774\uc2a4\ud1a0\uc5b4\uc5d0 \uc571\uc774 \uc62c\ub77c\uac00 \uc788\ub294 \uac70 \ub108\ubb34 \uc2e0\uae30\ud558\ub2e4. \\n\uc548\ub4dc\ub85c\uc774\ub4dc \ube0c\ub808\uba58 \uc74c\uc545\ub300(\uba67\ub3fc\uc9c0, \uc218\ub2ec, \ud551\uad6c), \uadf8\ub9ac\uace0 \ubc31\uc5d4\ub4dc \ud300\uc6d0\ub4e4(\uccb4\uc778\uc800, \ud6c4\ucd94, \ub9ac\uc624) \ub108\ubb34 \uace0\uc0dd\uc774 \ub9ce\uc558\ub2e4."},{"id":"cloudwatch","metadata":{"permalink":"/cloudwatch","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131.md","source":"@site/blog/2023-3/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131.md","title":"CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131","description":"CloudWatch","date":"2023-08-17T00:00:00.000Z","formattedDate":"2023\ub144 8\uc6d4 17\uc77c","tags":[{"label":"cloudwatch","permalink":"/tags/cloudwatch"},{"label":"log","permalink":"/tags/log"},{"label":"monitoring","permalink":"/tags/monitoring"}],"readingTime":5.35,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131","slug":"cloudwatch","tags":["cloudwatch","log","monitoring"]},"prevItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 3 \ud68c\uace0","permalink":"/woowacourse-level3-retrospective"},"nextItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac","permalink":"/route-image-async-with-event"}},"content":"## CloudWatch\\n\\nAWS \ub9ac\uc18c\uc2a4\uc640 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc758 \uc9c0\ud45c\uc640 \ub85c\uadf8\uc5d0 \ub300\ud55c \ubaa8\ub2c8\ud130\ub9c1\uc744 \uc81c\uacf5\ud558\ub294 \uc11c\ube44\uc2a4\ub2e4. \\n\uc9c0\ud45c\ub97c \uac10\uc2dc\ud558\uc5ec \uc54c\ub9bc\uc744 \ubcf4\ub0b4\ub294 \uae30\ub2a5\ub3c4 \uc81c\uacf5\ud55c\ub2e4. \\n\ud504\ub9ac\ud2f0\uc5b4\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 \uacbd\uc6b0 \ub300\uc2dc\ubcf4\ub4dc\ub2f9 3$/M \uc758 \ube44\uc6a9\uc774 \uccad\uad6c\ub418\uace0, \uc9c0\ud45c\ub098 \ub85c\uadf8\uc758 \uc591\uc5d0 \ub530\ub77c \ube44\uc6a9\uc774 \ucd94\uac00\uc801\uc73c\ub85c \uccad\uad6c\ub41c\ub2e4. \\n\uc694\uae08 \uc815\ubcf4\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \uc815\ubcf4\ub294 [\ub2e4\uc74c \ub9c1\ud06c](https://aws.amazon.com/ko/cloudwatch/pricing/)\uc5d0\uc11c \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n## CloudWatch Metrics\\n\\n\uae30\ubcf8\uc801\uc73c\ub85c 5\ubd84\ub9c8\ub2e4 \uc9c0\ud45c\uc5d0 \ub300\ud55c \uc815\ubcf4\uac00 \uc218\uc9d1\ub41c\ub2e4. \\n\uc138\ubd80 \ubaa8\ub2c8\ud130\ub9c1(Detailed Monitoring)\uc744 \ud65c\uc131\ud654\ud558\uba74 1\ubd84\ub9c8\ub2e4 \uc9c0\ud45c\ub97c \uc218\uc9d1\ud55c\ub2e4. \\n\ub300\uc2dc\ubcf4\ub4dc\uc5d0\uc11c InstanceId\ub85c \uac80\uc0c9\ud558\uc5ec \uc218\uc9d1\ub41c \uc9c0\ud45c\ub97c \ud655\uc778\ud560 \uc218 \uc788\ub2e4.\\n\\n![./cloudwatch1.png](./cloudwatch1.png)\\n\\nCPUUtilization, NetworkIn, NetworkOut\uacfc \uac19\uc740 \uae30\ubcf8\uc801\uc778 \uc9c0\ud45c\ub97c \uc81c\uacf5\ud558\uace0, \uba54\ubaa8\ub9ac, \ub514\uc2a4\ud06c \uacf5\uac04\uacfc \uac19\uc740 \uc9c0\ud45c\ub97c \ud655\uc778\ud558\ub824\uba74 \uc0ac\uc6a9\uc790 \uc9c0\uc815 \uc9c0\ud45c\ub97c \uc124\uc815\ud574\uc57c \ud55c\ub2e4.\\n\\n## CloudWatch Agent \uc124\uce58\\n\\nCloudWatch Agent \uc0ac\uc6a9\uc790 \uc9c0\uc815 \uc9c0\ud45c\uc640 \ub85c\uadf8\ub97c \uc218\uc9d1\ud560 \uc218 \uc788\ub2e4. \\n\\n### IAM \uc5ed\ud560 \uc124\uc815\\n\\n\uae30\ubcf8\uc801\uc73c\ub85c EC2 \uc778\uc2a4\ud134\uc2a4\uac00 CloudWatchAgentServerPolicy\uc5d0 \ub300\ud55c \uad8c\ud55c\uc774 \uc788\uc5b4\uc57c \ud55c\ub2e4. \\nIAM \u2192 \uc5ed\ud560\uc5d0\uc11c \uc5ed\ud560 \uc0dd\uc131\uc744 \ud074\ub9ad\ud55c\ub2e4.\\n\\n![./cloudwatch2.png](./cloudwatch2.png)\\n\\nCloudWatchAgentServerPolicy \uad8c\ud55c \uc815\ucc45\uc744 \uc120\ud0dd\ud558\uace0, \uc801\ub2f9\ud55c \uc5ed\ud560 \uc774\ub984\uc744 \uc785\ub825\ud574\uc11c \uc5ed\ud560\uc744 \uc0dd\uc131\ud55c\ub2e4.\\n\\n![./cloudwatch3.png](./cloudwatch3.png)\\n\\nEC2 \uc778\uc2a4\ud134\uc2a4 \ubaa9\ub85d\uc73c\ub85c \ub4e4\uc5b4\uac00\uc11c, CloudWatch Agent\ub97c \uc124\uce58\ud560 EC2 \uc778\uc2a4\ud134\uc2a4\ub97c \ud074\ub9ad\ud55c\ub2e4. \\n\uc791\uc5c5 \u2192 \ubcf4\uc548 \u2192 IAM \uc5ed\ud560 \uc218\uc815\uc5d0\uc11c \uc774\uc804\uc5d0 \uc0dd\uc131\ud55c \uc5ed\ud560\uc744 \uc9c0\uc815\ud55c\ub2e4.\\n\\n![./cloudwatch4.png](./cloudwatch4.png)\\n\\n### \uc124\uce58\\n\\n\ud658\uacbd\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\\nOS: ubuntu 22.04 \\n\uc778\uc2a4\ud134\uc2a4 \uc720\ud615: t4g.small (ARM64) \\n\\n\uc544\ub798 \uba85\ub839\uc5b4\ub97c \uc785\ub825\ud558\uc5ec \uc124\uce58\ud55c\ub2e4.\\n\\n```bash\\nwget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/arm64/latest/amazon-cloudwatch-agent.deb\\nsudo dpkg -i -E ./amazon-cloudwatch-agent.deb\\n```\\n\\n[\uc0ac\uc6a9 \uc124\uba85\uc11c](https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-commandline-fleet.html)\uc5d0 \uac01 \uc778\uc2a4\ud134\uc2a4 \uc720\ud615\ub9c8\ub2e4 \ub2e4\uc6b4\ub85c\ub4dc \ub9c1\ud06c\uac00 \uc790\uc138\ud558\uac8c \uc548\ub0b4\ub418\uc5b4 \uc788\ub2e4.\\n\\n### Wizard\\n\\nCloudWatch Wizard\ub97c \uc0ac\uc6a9\ud558\uba74 \uac04\ub2e8\ud558\uac8c \uc124\uc815 \ud30c\uc77c \uc0dd\uc131\ud560 \uc218 \uc788\ub2e4. \\n\ub85c\uadf8\ub97c \uc218\uc9d1\ud558\ub3c4\ub85d \uc124\uc815\ud558\ub294 \uacbd\uc6b0 Wizard \uc2e4\ud589 \uba85\ub839\uc5b4 \uc785\ub825 \uc804 log \ud30c\uc77c\uc758 \uc808\ub300 \uacbd\ub85c\ub97c \ubcf5\uc0ac\ud574\ub450\ub294 \uac83\uc774 \uc88b\ub2e4. \\n\uc544\ub798\uc758 \uba85\ub839\uc5b4\ub97c \uc785\ub825\ud558\uc5ec Wizard\ub97c \uc2e4\ud589\ud560 \uc218 \uc788\ub2e4. \\n\\n```bash\\nsudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard\\n```\\n\\n\uc124\uc815\uc744 \uc9c4\ud589\ud558\ub2e4 \ubcf4\uba74 \uc124\uc815 \ud30c\uc77c\uc774 \uc5b4\ub5bb\uac8c \uad6c\uc131\ub420\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\ub85c\uadf8\ub97c \ucd94\uac00\ud560 \uac83\uc774\ub0d0\uace0 \ubb3c\uc5b4\ubcf4\ub294 \uc785\ub825\ucc3d\uc774 \ub098\uc624\uba74 \uc900\ube44\ud574\ub480\ub358 \ub85c\uadf8 \ud30c\uc77c\uc758 \uc808\ub300 \uacbd\ub85c\ub97c \uc785\ub825\ud55c\ub2e4. \\n\\n![./cloudwatch5.png](./cloudwatch5.png)\\n\\n\uc911\uac04\uc5d0 SSM parameter store\uc5d0 \uc124\uc815 \ud30c\uc77c\uc744 \uc800\uc7a5\ud560 \uac83\uc774\ub0d0\uace0 \ubb3c\uc5b4\ubcf4\ub294 \ucc3d\uc774 \ub098\uc628\ub2e4. \\n\\n```bash\\nDo you want to store the config in the SSM parameter store?\\n1. yes\\n2. no\\n```\\n\\n\ucd94\uac00\uc801\uc73c\ub85c \uc124\uc815\ud558\uc9c0 \uc54a\ub294 \uacbd\uc6b0 2\ubc88\uc744 \uc120\ud0dd\ud55c\ub2e4. \\nParameter Store \uad00\ub9ac\uc5d0 \ub300\ud55c \ub0b4\uc6a9\uc740 \ub2e4\uc74c\uc758 [\ubb38\uc11c](https://dev.classmethod.jp/articles/manage-the-cloudwatch-agent-from-the-parameter-store/)\ub97c \ucc38\uace0\ud558\uba74 \uc88b\uc744 \uac70 \uac19\ub2e4. \\n\uc124\uc815\uc774 \uc644\ub8cc\ub418\uba74 `/opt/aws/amazon-cloudwatch-agent/bin/config.json` \uc5d0 \uc124\uc815\uc5d0 \ub300\ud55c \ub0b4\uc6a9\uc774 \uc800\uc7a5\ub41c\ub2e4. \\n\\n### \uc124\uc815 \ud30c\uc77c \uc801\uc6a9\\n\\n\uc544\ub798\uc758 \uba85\ub839\uc5b4\ub97c \uc785\ub825\ud558\uc5ec \uc124\uc815\ud30c\uc77c\uc744 \uc801\uc6a9\ud560 \uc218 \uc788\ub2e4. \\nfile \ub4a4\uc5d0\ub294 \uc124\uc815 \ud30c\uc77c\uc5d0 \ub300\ud55c \uc808\ub300\uacbd\ub85c(\uc544\ub798 \uba85\ub839\uc5b4 \uae30\uc900 \uae30\ubcf8 \uc0dd\uc131 \uc704\uce58)\ub97c \uc785\ub825\ud558\uba74 \ub41c\ub2e4. \\n\\n```bash\\nsudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json\\n```\\n\\n### types.db: no such file or directory \uc5d0\ub7ec\\n\\n\ub2e4\uc74c\uacfc \uac19\uc740 \uc5d0\ub7ec\uac00 \ubc1c\uc0dd\ud55c\ub2e4\uba74 types.db \ud30c\uc77c\uc744 \uc0dd\uc131\ud574\uc11c \ubb38\uc81c\ub97c \ud574\uacb0\ud560 \uc218 \uc788\ub2e4.\\n\\n```bash\\nError running agent: Error loading config file /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.toml: error parsing socket_listener, open /usr/share/collectd/types.db: no such file or directory\\n```\\n\\ntypes.db \ud30c\uc77c \uc0dd\uc131\\n\\n```bash\\nsudo mkdir /usr/share/collectd\\nsudo touch /usr/share/collectd/types.db\\n```\\n\\n### \uc9c0\ud45c \ud655\uc778\\n\\nCloudWatch Metrics\uc5d0 \uac00\ubcf4\uba74 CWAgent\ub77c\ub294 \ub124\uc784\uc2a4\ud398\uc774\uc2a4\uac00 \ucd94\uac00\ub41c \uac83\uc744 \ubcfc \uc218 \uc788\ub2e4. \\n\\n![./cloudwatch6.png](./cloudwatch6.png)\\n\\n\ub2e4\uc74c\uacfc \uac19\uc774 \uc124\uc815 \ud30c\uc77c\uc5d0 \ub124\uc784\uc2a4\ud398\uc774\uc2a4\ub97c \ucd94\uac00\ud558\uc5ec \uc9c0\ud45c\uc5d0 \ub300\ud55c \ub124\uc784\uc2a4\ud398\uc774\uc2a4\ub97c \ubcc0\uacbd\ud560 \uc218 \uc788\ub2e4. \\n\\n```json\\n{\\n \\"metrics\\": {\\n \\"namespace\\": \\"2023-hello-world\\",\\n ......\\n },\\n} \\n```\\n\\n### \ub85c\uadf8\\n\\nCloudWatch \u2192 \ub85c\uadf8 \uadf8\ub8f9\uc73c\ub85c \uac00\uba74 Wizard\ub85c \ucd94\uac00\ud55c \ub85c\uadf8\ub97c \ud655\uc778\ud560 \uc218 \uc788\ub2e4.\\n\\n![./cloudwatch7.png](./cloudwatch7.png)\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n[CloudWatch\ub780 \ubb34\uc5c7\uc785\ub2c8\uae4c?](https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html) \\n[Amazon CloudWatch \uc694\uae08](https://aws.amazon.com/ko/cloudwatch/pricing/) \\n[Linux \uc778\uc2a4\ud134\uc2a4 \uc9c0\ud45c](https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) \\n[\uc11c\ubc84\uc5d0 CloudWatch \uc5d0\uc774\uc804\ud2b8 \uc124\uce58 \ubc0f \uc2e4\ud589](https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-commandline-fleet.html) \\n[CloudWatch Agent\ub97c Parameter Store\uc5d0\uc11c \uad00\ub9ac\ud574 \ubcf4\uae30](https://dev.classmethod.jp/articles/manage-the-cloudwatch-agent-from-the-parameter-store/) \\n[CloudWatch\uc5d0\uc774\uc804\ud2b8 \uad6c\uc131 \ud30c\uc77c](https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/monitoring/CloudWatch-Agent-Configuration-File-Details.html)"},{"id":"route-image-async-with-event","metadata":{"permalink":"/route-image-async-with-event","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac.mdx","source":"@site/blog/2023-3/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac.mdx","title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac","description":"\uc774\uc804 \uae00","date":"2023-08-13T00:00:00.000Z","formattedDate":"2023\ub144 8\uc6d4 13\uc77c","tags":[{"label":"async","permalink":"/tags/async"},{"label":"event","permalink":"/tags/event"}],"readingTime":11.2,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac","slug":"route-image-async-with-event","tags":["async","event"]},"prevItem":{"title":"CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131","permalink":"/cloudwatch"},"nextItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604","permalink":"/route-image-implementation"}},"content":"## \uc774\uc804 \uae00\\n\\n[\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd](./route-image-intro) \\n[\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604](./route-image-implementation)\\n\\n## \uac1c\uc694\\n\\n\ud604\uc7ac \uc5ec\ud589\uc744 \ub9c8\uce58\ub294 \uacbd\uc6b0, \uac10\uc0c1\uc744 \uc0dd\uc131\ud558\ub294 \uacbd\uc6b0 \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc694\uccad\uc774 \uc774\ub8e8\uc5b4\uc9c4\ub2e4. \\n\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc758 \uacbd\uc6b0 \uc704\uce58 \uc815\ubcf4\uc758 \uac1c\uc218\uc5d0 \uc815\ube44\ub840\ud558\uc5ec \uc0dd\uc131 \uc2dc\uac04\uc774 \uc99d\uac00\ud55c\ub2e4. \\n\ub530\ub77c\uc11c \ube44\ub3d9\uae30\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc694\uccad\uc744 \ucc98\ub9ac\ud558\uc5ec \uc0ac\uc6a9\uc790\uc758 \uacbd\ud5d8\uc744 \uac1c\uc120\uc2dc\ud0ac \uc218 \uc788\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\\n### \uc8fc\uae30\ub2a5\uc758 \uc751\ub2f5\uc18d\ub3c4 \uac1c\uc120\\n\\n\uc5ec\ud589 \uc885\ub8cc\uc640 \uac10\uc0c1 \uc0dd\uc131\uc774 \uc8fc\uae30\ub2a5\uc774\uace0, \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uae30\ub2a5\uc740 \ubd80\uae30\ub2a5\uc774\ub2e4. \\n\ud558\uc9c0\ub9cc \ud604\uc7ac \uc5ec\ud589 \uc885\ub8cc\uc640 \uac10\uc0c1 \uc0dd\uc131\uc758 \uc751\ub2f5 \uc18d\ub3c4\uac00 \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc2dc\uac04\uc5d0 \uc601\ud5a5\uc744 \ubc1b\uace0 \uc788\ub2e4. \\n\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc740 \ube44\ub3d9\uae30 \ucc98\ub9ac\ud558\uc5ec\ub3c4 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc0ac\uc6a9\uc5d0 \ubb38\uc81c\uac00 \ub418\uc9c0 \uc54a\ub294\ub2e4. \\n\uc18c\uc694 \uc2dc\uac04\uc774 1\ucd08 \uc774\uc0c1 \uac78\ub9ac\ub294 \uacbd\uc6b0\uac00 \uc874\uc7ac\ud558\uae30\uc5d0 \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc744 \ube44\ub3d9\uae30 \ucc98\ub9ac\ud558\uace0 \uc5ec\ud589 \uc885\ub8cc\uc640 \uac10\uc0c1 \uc0dd\uc131 \uae30\ub2a5\uc758 \uc751\ub2f5 \uc2dc\uac04\uc744 \uac1c\uc120\ud558\ub294 \uac83\uc774 \ub354 \uc911\uc694\ud558\ub2e4. \\n\\n### \ud655\uc7a5\uc131 \ub300\ube44\\n\\n\ud604\uc7ac 10\ubd84 \uac04\uaca9\uc73c\ub85c \uc704\uce58 \uc815\ubcf4\ub97c \uc11c\ubc84\uc5d0 \uc800\uc7a5\ud558\uace0 \uc788\ub2e4. \\n\uc870\uae08 \ub354 \uc9e7\uc740 \uac04\uaca9\uc73c\ub85c \uc704\uce58 \uc815\ubcf4\ub97c \uadf8\ub9ac\ub294 \uacbd\uc6b0 \ud558\ub098\uc758 \uc5ec\ud589\uc5d0 \ub9ce\uc740 \uc704\uce58 \uc815\ubcf4\uac00 \uc800\uc7a5\ub420 \uc218\ubc16\uc5d0 \uc5c6\uace0 \ub530\ub77c\uc11c \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc5d0 \uac78\ub9ac\ub294 \uc2dc\uac04\uc774 \ub354 \uae38\uc5b4\uc9c8 \uc218 \uc788\ub2e4. \\n\ub530\ub77c\uc11c \ucd94\ud6c4\uc5d0 \ub354 \uc9e7\uc740 \uac04\uaca9\uc73c\ub85c \uc704\uce58 \uc815\ubcf4\ub97c \uc800\uc7a5\ud558\ub294 \uacbd\uc6b0\ub97c \ub300\ube44\ud558\uc5ec \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc740 \ube44\ub3d9\uae30\ub85c \ucc98\ub9ac\ud558\ub294 \uac83\uc774 \ud569\ub2f9\ud558\ub2e4. \\n\\n## \ube44\ub3d9\uae30 \ucc98\ub9ac\\n\\n@Async\ub97c \uc0ac\uc6a9\ud558\uba74 \uac04\ub2e8\ud558\uac8c \uba54\uc11c\ub4dc\ub97c \ube44\ub3d9\uae30\ub85c \ub3d9\uc791\ud558\ub3c4\ub85d \ub9cc\ub4e4 \uc218 \uc788\ub2e4. \\n\\n### \ube44\ub3d9\uae30 \uc124\uc815\\n\\n\uc0ac\uc6a9\ud558\uae30 \uc804\uc5d0 \uc124\uc815 \ud30c\uc77c\uc744 \ud558\ub098 \ub9cc\ub4e4\uc5b4\uc11c EnableAsync \uc124\uc815\uc744 \ud574\uc57c\ud55c\ub2e4. \\n\ud574\ub2f9 \uc124\uc815\uc744 \uc801\uc6a9\ud558\uba74 \ube44\ub3d9\uae30\uc801\uc73c\ub85c \uc2e4\ud589\ud558\ub824\ub294 \uba54\uc11c\ub4dc\uc5d0 @Async \uc560\ub108\ud14c\uc774\uc158\uc744 \ubd99\uc5ec\uc8fc\uae30\ub9cc \ud558\uba74 \ube44\ub3d9\uae30\ub85c \ub3d9\uc791\ud55c\ub2e4. \\n\\n```java title=\\"AsyncConfig\\"\\n@EnableAsync\\n@Configuration\\npublic class AsyncConfig {\\n}\\n```\\n\\n\uc2a4\ud504\ub9c1 \ubd80\ud2b8\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 \uacbd\uc6b0 \uae30\ubcf8\uc801\uc73c\ub85c \ube44\ub3d9\uae30 \ucc98\ub9ac\ub97c \ud560 \ub54c \ub9e4\ubc88 \uc0c8\ub85c\uc6b4 \uc2a4\ub808\ub4dc\ub97c \uc0dd\uc131\ud558\uae30 \ub54c\ubb38\uc5d0 \uc2a4\ub808\ub4dc \ud480 \uc124\uc815\uc744 \ub530\ub85c \ud574\uc918\uc57c \ud55c\ub2e4. \ud558\uc9c0\ub9cc \uc2a4\ud504\ub9c1 \ubd80\ud2b8\ub97c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 ThreadPoolTaskExecutor\ub97c \ub530\ub85c \uc124\uc815\ud558\uc9c0 \uc54a\uc544\ub3c4 \uae30\ubcf8\uc801\uc73c\ub85c \uc2a4\ud504\ub9c1 \ubd80\ud2b8\uac00 \uc0dd\uc131\uc744 \ub3c4\uc640\uc900\ub2e4. \\n\\n> In the absence of an Executor bean in the context, Spring Boot auto-configures a ThreadPoolTaskExecutor with sensible defaults that can be automatically associated to asynchronous task execution (@EnableAsync) and Spring MVC asynchronous request processing.\\n> 7.7. Task Execution and Scheduling, Spring Boot Docs\\n\\n### @Async \uc801\uc6a9\\n\\n\uc774\ubbf8\uc9c0 \uc0dd\uc131\uae30\uc5d0 Async \uc560\ub108\ud14c\uc774\uc158\uc744 \ubd99\uc5ec \ube44\ub3d9\uae30\ub85c \ub3d9\uc791\ud558\ub3c4\ub85d \ud55c\ub2e4. \\n\\n```java title=\\"RouteImageGenerator\\"\\n@Async\\npublic void generate(\\n List latitudes,\\n List longitudes,\\n List pointedLatitudes,\\n List pointedLongitudes,\\n Long tripId\\n) {\\n // \uc774\ubbf8\uc9c0 \uc0dd\uc131\\n RouteImageDrawer routeImageDrawer = RouteImageDrawer.from(IMAGE_SIZE);\\n Coordinates coordinates = Coordinates.of(latitudes, longitudes);\\n Coordinates pointedCoordinates = Coordinates.of(pointedLatitudes, pointedLongitudes);\\n drawImage(coordinates, routeImageDrawer, pointedCoordinates);\\n\\n // \uc774\ubbf8\uc9c0 \uc800\uc7a5\\n String imageName = routeImageUploader.upload(routeImageDrawer.bufferedImage());\\n\\n // \uc790\uc6d0 \ud560\ub2f9 \ud574\uc81c\\n routeImageDrawer.dispose();\\n\\n // \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uac12 \ubcc0\uacbd\\n Trip trip = tripRepository.findById(tripId)\\n .orElseThrow();\\n trip.changeRouteImageUrl(imageUrl);\\n tripRepository.save(trip);\\n}\\n```\\n\\n### \ube44\ub3d9\uae30 \ucc98\ub9ac\uc2dc \ubb38\uc81c\uc810\\n\\n\ud604\uc7ac \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc744 \ud558\uace0 \uc800\uc7a5 \ud6c4, \uc800\uc7a5 \uacbd\ub85c\ub97c DB\uc5d0 \ubc18\uc601\ud574\uc57c \ud55c\ub2e4. \\n\ub530\ub77c\uc11c \ud328\ud0a4\uc9c0 \uac04 \uc21c\ud658 \ucc38\uc870 \ud615\ud0dc\uac00 \ub418\uba70 \uc758\uc874\uc131 \ubc29\ud5a5\uc774 \ubb38\uc81c\uac00 \uc0dd\uae34\ub2e4. \\n\\n```mermaid\\ngraph LR\\n trip[trip: \uc5ec\ud589 \uad00\ub828 \ud328\ud0a4\uc9c0] --\x3e draw[draw: \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uae30\ub2a5\uc744 \ud3ec\ud568\ud558\uace0 \uc788\ub294 \ud328\ud0a4\uc9c0]\\n draw --\x3e trip\\n```\\n\\n\uc774\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud574\uc11c\ub294 \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uacfc \uc774\ubca4\ud2b8\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc774 \uc788\ub2e4. \\n\uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud55c\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uad6c\uc870\uac00 \ub41c\ub2e4. \\n\\n```mermaid\\ngraph LR\\n\\tsubgraph draw\\n\\t\\tdirection LR\\n\\t\\tRG[RouteImageGenerator] -- DB \ubc18\uc601 \uc694\uccad --\x3e ILR[ImageLinkTripRepository]\\n\\tend\\n subgraph trip\\n\\t\\tdirection LR\\n\\t\\tTS[TripService] -- \uc774\ubbf8\uc9c0 \uc0dd\uc131 --\x3e RG\\n\\t\\tILRI[ImageLinkTripRepositoryImpl] -- \uad6c\ud604 --\x3e ILR\\n\\tend\\n\\n\\ttrip --\x3e draw\\n```\\n\\n\ud328\ud0a4\uc9c0 \uac04 \uc758\uc874\uc131\uc740 \ud574\uacb0\ub418\uc5c8\uc9c0\ub9cc, \uc774\ubbf8\uc9c0 \uacbd\ub85c \uc800\uc7a5\uc744 \uc704\ud574 tripId\ub97c \ubc1b\uc544\uc57c\ud558\ub294 \ub4f1\uc758 \ub17c\ub9ac\uc801\uc778 \uc758\uc874\uc131\uc740 \uc544\uc9c1 \ud574\uacb0\ub418\uc9c0 \uc54a\uc558\ub2e4. \\n\ub530\ub77c\uc11c \uc774\ubca4\ud2b8\ub97c \uc0ac\uc6a9\ud558\uae30\ub85c \ud588\ub2e4. \\n\\n## \uc774\ubca4\ud2b8 \uc0ac\uc6a9\\n\\n\uc2a4\ud504\ub9c1\uc758 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc774\ubca4\ud2b8\ub97c \uc0ac\uc6a9\ud558\uba74 \ube44\uc988\ub2c8\uc2a4 \ub85c\uc9c1\uc758 \ube44\uad00\uc2ec\uc0ac(ex. \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131)\uc744 \ud6a8\uc728\uc801\uc778 \ubc29\ubc95\uc73c\ub85c \ucc98\ub9ac\ud560 \uc218 \uc788\ub2e4.\\n\\n### \uc774\ubca4\ud2b8 \ubc1c\ud589\\n\\n\uc774\ubca4\ud2b8\ub97c \uc0ac\uc6a9\ud558\ub824\uba74 \uba3c\uc800 \uc774\ubca4\ud2b8\ub97c \ubc1c\ud589\ud574\uc57c \ud55c\ub2e4. \\n\uc2a4\ud504\ub9c1\uc5d0\uc11c\ub294 ApplicationEventPublisher \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc774\ubca4\ud2b8\ub97c \ubc1c\ud589\ud560 \uc218 \uc788\ub2e4. \\n\ud574\ub2f9 \uc778\ud130\ud398\uc774\uc2a4\ub294 \ub0b4\ubd80\uc801\uc73c\ub85c ApplicationContext\uac00 \uad6c\ud604\ud558\uc5ec \uc774\ubca4\ud2b8\ub97c \ubc1c\ud589\ud55c\ub2e4. \\n\\n```java title=\\"TripService & TripUpdateEvent\\"\\npublic void updateTripById(LoginUser loginUser, Long tripId, TripUpdateRequest tripUpdateRequest) {\\n ...\\n\\n // \uc774\ubca4\ud2b8 \ubc1c\ud589\\n applicationEventPublisher.publishEvent(new TripUpdateEvent(trip.id()));\\n}\\n\\npublic record TripUpdateEvent(Long tripId) {\\n}\\n```\\n\\n\uc774\ubca4\ud2b8\ub97c \ubc1c\ud589\ud560 \ub54c \ubc1c\ud589\ud558\ub294 \uc774\ubca4\ud2b8\uba85\uc774 \uc911\uc694\ud558\ub2e4. \\n\uc774\ubca4\ud2b8\ub97c \uad6c\ub3c5\ud558\ub294 \ub3c4\uba54\uc778\uc758 \ud589\uc704\ub97c \ub2f4\uace0 \uc788\ub294 \uc774\ubca4\ud2b8\ub97c \ubc1c\ud589(ex. RouteImageGenerateEvent)\ud55c\ub2e4\uba74 \ub17c\ub9ac\uc801\uc778 \uc758\uc874 \uad00\uacc4\uac00 \ub0a8\uc544\uc788\uae30\uc5d0 \uc774\ubca4\ud2b8\ub97c \uc801\uc808\ud788 \uc0ac\uc6a9\ud588\ub2e4\uace0 \ubcf4\uae30 \uc5b4\ub835\ub2e4. \\n\ubc1c\ud589\ud558\ub294 \uc774\ubca4\ud2b8\uba85\uc740 \uc8fc\uae30\ub2a5\uc774 \uc5b4\ub5a4 \ud589\uc704(ex. TripUpdateEvent)\ub97c \ud588\ub294\uc9c0\uc5d0 \ub300\ud55c \uc815\ubcf4\uac00 \ub2f4\uaca8\uc788\ub294 \uc774\ubca4\ud2b8\uba85\uc73c\ub85c \ubc1c\ud589\ud558\ub294 \uac83\uc774 \uc911\uc694\ud558\ub2e4. \\n\\n### \uc774\ubca4\ud2b8 \uad6c\ub3c5\\n\\n\uc774\ubca4\ud2b8\ub97c \uad6c\ub3c5\ud558\uc5ec \uc2e4\ud589\ud558\ub294 \uba54\uc11c\ub4dc\ub294 \ube44\ub3d9\uae30\ub85c \ucc98\ub9ac\ud558\uae30 \uc704\ud558\uc5ec `@Async` \uc560\ub108\ud14c\uc774\uc158\uc744 \uc801\uc6a9\ud588\ub2e4. \\n\uc774\ubca4\ud2b8\uc758 \uad6c\ub3c5\uc740 \uc5ec\ud589\uc774 \uc815\uc0c1\uc801\uc73c\ub85c \uc885\ub8cc\ub420 \ub54c \uc5ec\ud589\uc5d0 \ub300\ud55c \uc815\ubcf4\ub97c \uac00\uc9c0\uace0 \uacbd\ub85c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\uae30 \uc704\ud574 `@TransactionalEventListener`\ub97c \uc0ac\uc6a9\ud588\ub2e4. \\n\\n:::note TransactionPhase \uc124\uc815\\nTransactionPhase\uc744 \uc0ac\uc6a9\ud558\uc5ec \ud2b8\ub79c\uc7ad\uc158 \uc774\ubca4\ud2b8\ub97c \uc5b4\ub5a4 \ub2e8\uacc4\uc5d0\uc11c \uc218\uc2e0\ud558\uace0 \ucc98\ub9ac\ud560\uc9c0\ub97c \uc9c0\uc815\ud560 \uc218 \uc788\ub2e4.\\n\\nAFTER_COMMIT(\uae30\ubcf8\uac12): \ud2b8\ub79c\uc7ad\uc158\uc774 \uc815\uc0c1\uc801\uc73c\ub85c \ucee4\ubc0b \ub418\ub294 \uacbd\uc6b0 \uc774\ubca4\ud2b8 \uc2e4\ud589 \\nAFTER_ROLLBACK: \ud2b8\ub79c\uc7ad\uc158\uc774 \ub864\ubc31\ub418\ub294 \uacbd\uc6b0 \uc774\ubca4\ud2b8 \uc2e4\ud589 \\nAFTER_COMPLETION: \ud2b8\ub79c\uc7ad\uc158\uc774 \ucee4\ubc0b \ub610\ub294 \ub864\ubc31 \ub418\uc5c8\uc744 \uacbd\uc6b0 \uc774\ubca4\ud2b8 \uc2e4\ud589 \\nBEFORE_COMMIT: \ud2b8\ub79c\uc7ad\uc158\uc774 \ucee4\ubc0b \ub418\uae30 \uc804 \uc774\ubca4\ud2b8 \uc2e4\ud589 \\n:::\\n\\n\uc774\ubbf8\uc9c0 \uc0dd\uc131\uc758 \uacbd\uc6b0 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \uc81c\uc678\ud558\uae30 \uc704\ud574 @Transactional \uc560\ub108\ud14c\uc774\uc158\uc744 \uc0ac\uc6a9\ud558\uc9c0 \uc54a\uc558\ub2e4. \\n\\n```java title=\\"TripUpdateEventHandler\\"\\n@Component\\npublic class TripUpdateEventHandler {\\n\\n private final RouteImageGenerator routeImageGenerator;\\n private final TripRepository tripRepository;\\n\\n public TripUpdateEventHandler(RouteImageGenerator routeImageGenerator, TripRepository tripRepository) {\\n this.routeImageGenerator = routeImageGenerator;\\n this.tripRepository = tripRepository;\\n }\\n\\n @Async\\n @TransactionalEventListener(phase = AFTER_COMMIT)\\n public void handle(TripUpdateEvent tripUpdateEvent) {\\n Trip trip = tripRepository.getTripWithPoints(tripUpdateEvent.tripId());\\n\\n String imageUrl = routeImageGenerator.generate(\\n trip.getLatitudes(),\\n trip.getLongitudes(),\\n trip.getPointedLatitudes(),\\n trip.getPointedLongitudes()\\n );\\n\\n trip.changeRouteImageUrl(imageUrl);\\n tripRepository.save(trip);\\n }\\n}\\n```\\n\\n\uc774\ubca4\ud2b8\ub97c \uc0ac\uc6a9\ud568\uc73c\ub85c\uc368 \ud328\ud0a4\uc9c0 \uac04 \uc21c\ud658 \ucc38\uc870 \ubb38\uc81c\uac00 \ub2e4\uc74c\uacfc \uac19\uc774 \ud574\uacb0\ub418\uc5c8\ub2e4. \\n\ub610\ud55c \uc8fc\uae30\ub2a5\uacfc \ubd80\uae30\ub2a5\uc744 \ubd84\ub9ac\ud568\uc73c\ub85c\uc368 \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uae30\ub2a5\uc5d0 \ub300\ud55c \uc804\uccb4\uc801\uc778 \uacb0\ud569\ub3c4\ub97c \ub0ae\ucd94\uc5c8\ub2e4.\\n\\n```mermaid\\ngraph LR\\n subgraph trip\\n TripServcie -- \ubc1c\ud589 --\x3e TripUpdateEvent\\n TripRepository\\n end\\n\\n subgraph draw\\n TripUpdateEventHandler -- \uad6c\ub3c5 \ud6c4 \uc774\ubbf8\uc9c0 \uc0dd\uc131 --\x3e TripUpdateEvent\\n TripUpdateEventHandler -- \uc0dd\uc131\ub41c \uc774\ubbf8\uc9c0 \uacbd\ub85c \uc800\uc7a5 --\x3e TripRepository\\n end\\n```\\n\\n### \ud14c\uc2a4\ud2b8\\n\\n\ube44\ub3d9\uae30\ub85c \ub3d9\uc791\ud558\ub294 \uba54\uc11c\ub4dc\ub97c \ud14c\uc2a4\ud2b8\ud558\uae30 \uc704\ud574\uc11c\ub294 \uc544\ub798\uc640 \uac19\uc740 \ubc29\ubc95\uc774 \uc788\ub2e4. \\n\\nimport Tabs from \\"@theme/Tabs\\";\\nimport TabItem from \\"@theme/TabItem\\";\\n\\n\\n\\n\\n```java\\n@SpringBootTest\\npublic class TripUpdateEventHandlerIntegrationTest {\\n\\n ...\\n\\n @Test\\n void \uc5ec\ud589\uc218\uc815_\uc774\ubca4\ud2b8\ub97c_\ubc1c\uc0dd\uc2dc\ud0a4\uba74_\uc774\ubbf8\uc9c0\ub97c_\uc0dd\uc131_\uc694\uccad\uc744_\ud55c\ub2e4() {\\n // given\\n TripUpdateEvent tripUpdateEvent = new TripUpdateEvent(1L);\\n given(tripRepository.getTripWithPoints(tripUpdateEvent.tripId()))\\n .willReturn(\uc5ec\ud589());\\n\\n // when\\n transactionTemplate.executeWithoutResult(action -> applicationEventPublisher.publishEvent(tripUpdateEvent));\\n\\n // then\\n then(routeImageGenerator)\\n .should(Mockito.timeout(5000).times(1))\\n .generate(any(), any(), any(), any());\\n }\\n}\\n```\\n\\n\\n\\n\\n\\n```java\\n@ContextConfiguration(classes = TestSyncConfig.class)\\n@SpringBootTest\\npublic class TripUpdateEventHandlerIntegrationTest {\\n\\n ...\\n\\n @Test\\n void \uc5ec\ud589\uc218\uc815_\uc774\ubca4\ud2b8\ub97c_\ubc1c\uc0dd\uc2dc\ud0a4\uba74_\uc774\ubbf8\uc9c0\ub97c_\uc0dd\uc131_\uc694\uccad\uc744_\ud55c\ub2e4() {\\n // given\\n TripUpdateEvent tripUpdateEvent = new TripUpdateEvent(1L);\\n given(tripRepository.getTripWithPoints(tripUpdateEvent.tripId()))\\n .willReturn(\uc5ec\ud589());\\n\\n // when\\n transactionTemplate.executeWithoutResult(action -> applicationEventPublisher.publishEvent(tripUpdateEvent));\\n\\n // then\\n then(routeImageGenerator)\\n .should(times(1))\\n .generate(any(), any(), any(), any());\\n }\\n}\\n```\\n\\n\\n\\n\\n\ucc98\uc74c\uc5d0\ub294 \ud14c\uc2a4\ud2b8\uc5d0\uc11c\ub9cc \ub3d9\uae30\ub85c \uc124\uc815 \ud6c4 \uac80\uc99d\ud558\ub824\uace0 \ud588\ub2e4. \\n\ud1b5\ud569 \ud14c\uc2a4\ud2b8\uc5d0\uc120 `\ud2b8\ub79c\uc7ad\uc158\uc774 \uc815\uc0c1 \uc885\ub8cc\ub418\uc5c8\uc744 \ub54c \ube44\ub3d9\uae30\ub85c \uc774\ubca4\ud2b8\ub97c \uad6c\ub3c5\ud558\uc5ec \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uba54\uc11c\ub4dc\ub97c \ud638\ucd9c\ud558\ub294\uc9c0` \uac80\uc99d\uc774 \ud544\uc694\ud588\uae30 \ub54c\ubb38\uc5d0 \ucd5c\uc885\uc801\uc73c\ub85c `Mockito.timeout` \uba54\uc11c\ub4dc\ub97c \uc0ac\uc6a9\ud558\uc5ec \ube44\ub3d9\uae30 \uba54\uc11c\ub4dc\uac00 \ud1b5\uacfc\ub420 \ub54c\uae4c\uc9c0 \ub300\uae30\ud558\ub294 \ubc29\ud5a5\uc73c\ub85c \ubcc0\uacbd\ud588\ub2e4. \\n\\n## \uacb0\uacfc\\n\\n![./time.png](./time.png)\\n\\n\uc704 \uc751\ub2f5 \uc2dc\uac04\uc740 \uc704\uce58 \uc815\ubcf4 1000\uac1c\ub97c \uae30\uc900\uc73c\ub85c \ud14c\uc2a4\ud2b8\ud55c \uac12\uc774\ub2e4. \\n\uc751\ub2f5 \uc2dc\uac04\uc5d0 \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc2dc\uac04\uc774 \ud3ec\ud568\ub418\uc9c0 \uc54a\uc544\uc11c \uc131\ub2a5\uc774 \uac1c\uc120\ub41c \uac83\uc744 \ubcfc \uc218 \uc788\ub2e4. \\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n[7.7. Task Execution and Scheduling, Spring Boot Docs](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.task-execution-and-scheduling) \\n[Spring Events, Baeldung](https://www.baeldung.com/spring-events) \\n[\ud68c\uc6d0\uc2dc\uc2a4\ud15c \uc774\ubca4\ud2b8\uae30\ubc18 \uc544\ud0a4\ud14d\ucc98 \uad6c\ucd95\ud558\uae30](https://techblog.woowahan.com/7835/)"},{"id":"route-image-implementation","metadata":{"permalink":"/route-image-implementation","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604.mdx","source":"@site/blog/2023-3/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604.mdx","title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604","description":"\uac1c\uc694","date":"2023-08-02T00:00:00.000Z","formattedDate":"2023\ub144 8\uc6d4 2\uc77c","tags":[{"label":"image","permalink":"/tags/image"},{"label":"awt","permalink":"/tags/awt"}],"readingTime":11.665,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604","slug":"route-image-implementation","tags":["image","awt"]},"prevItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac","permalink":"/route-image-async-with-event"},"nextItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c","permalink":"/route-image-python"}},"content":"## \uac1c\uc694\\n\\n\uc5ec\ud589\uc5d0 \ub300\ud55c \uacbd\ub85c\ub97c \ubcf4\uc5ec\uc8fc\uae30 \uc704\ud574 \uacbd\ub85c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\ub294 \uae30\ub2a5\uc744 \ucd94\uac00\ud588\ub2e4. \\n\uacbd\ub85c \uc774\ubbf8\uc9c0\uc5d0 \ub300\ud55c \uc694\uad6c\uc0ac\ud56d \ubc0f \uae30\uc220 \uc120\ud0dd\uc5d0 \ub300\ud55c \ub0b4\uc6a9\uc740 [\ub9c1\ud06c](./route-image-intro)\uc5d0 \uc788\ub2e4.\\n\\n### \uad6c\ud604 \uacb0\uacfc\\n\\n![./result.png](./result.png)\\n\\n\uc608\uc2dc \ub370\uc774\ud130\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n**\uc11c\uc6b8\uc5ed(\uc810)** \u2192 \uc2e0\uc0ac\uc5ed \u2192 \ub178\ub7c9\uc9c4\uc5ed \u2192 \ud64d\ub300\uc785\uad6c\uc5ed \u2192 \uc885\ub85c3\uac00\uc5ed \u2192 \uc625\uc218\uc5ed \u2192 **\uad6c\ub85c\uc5ed(\uc810)** \u2192 \uc2e0\ub9bc\uc5ed \u2192 \ubc1c\uc0b0\uc5ed\\n\\n```java title=\\"\uc608\uc2dc \ub370\uc774\ud130\\"\\nList x = List.of(\\n 126.97094933811682, 127.02154822802501, 126.94218991864345, 126.92402556641424,\\n 126.99265358592287, 127.01779856076462, 126.88474839801178, 126.92900751277035, 126.83930056313639\\n);\\nList y = List.of(\\n 37.55302829553499, 37.51619698970427, 37.51294119442773, 37.5565933969331,\\n 37.57034879708931, 37.54027238225762, 37.50129417536773, 37.48258811529137, 37.557607696911184\\n);\\nList xPoints = List.of(126.97094933811682, 126.88474839801178);\\nList yPoints = List.of(37.55302829553499, 37.50129417536773);\\n```\\n\\n### IMAGE_SIZE & ROUTE_SIZE\\n\\n```java title=\\"RouteImageGenerator.java\\"\\nprivate static final int IMAGE_SIZE = 800;\\nprivate static final int ROUTE_SIZE = 600;\\n```\\n\\n\ucf54\ub4dc\ub97c \ubcf4\uba74 IMAGE_SIZE\uc640 ROUTE_SIZE\uac00 \uc788\ub2e4. \\nIMAGE_SIZE\ub294 \ub9d0 \uadf8\ub300\ub85c \uc774\ubbf8\uc9c0\uc758 width\uc640 height\ub97c \uc758\ubbf8\ud55c\ub2e4. \\nROUTE_SIZE\uc758 \uacbd\uc6b0 \uc0c1\ud558\uc88c\uc6b0 100px \ub9cc\ud07c\uc758 \uac04\uaca9\uc744 \uc704\ud574 \uc874\uc7ac\ud55c\ub2e4. \\n\ub530\ub77c\uc11c \uc2e4\uc81c \uacbd\ub85c \uc774\ubbf8\uc9c0\uc758 \ud06c\uae30\ub294 600 * 600 \uc0ac\uc774\uc988\ub85c \uc0dd\uc131\ub41c\ub2e4. \\n\\n![./600.png](./600.png)\\n\\n**\uc0ac\uc774\uc988 \ubcc0\uacbd\uc758 \uc774\uc720**\\n\\n255 * 255 \uc815\ub3c4\uc758 \uc791\uc740 \uc0ac\uc774\uc988\ub85c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud574\ubcf4\ub824\uace0 \ud588\ub294\ub370, \uc774\ubbf8\uc9c0\uc758 \uc120\uba85\ub3c4\uac00 \uc88b\uc9c0 \uc54a\uc544 800 * 800 \uc0ac\uc774\uc988\ub85c \ubcc0\uacbd\ud588\ub2e4.\\n\\n## \uc8fc\uc694 \ud074\ub798\uc2a4\\n\\n### \uc694\uc57d\\n\\n| \ud074\ub798\uc2a4\uba85 | \uc124\uba85 | \ud2b9\uc774\uc0ac\ud56d |\\n| --- | --- | --- |\\n| Coordinate | \uc704\ub3c4, \uacbd\ub3c4\ub85c \uc774\ub8e8\uc5b4\uc9c4 \uc704\uce58 \uac12 | \uc88c\ud45c\ub97c \ub73b\ud558\uc9c0\ub9cc \uc5ec\ud589 \ub3c4\uba54\uc778\uc5d0 \ud3ec\ud568\ub41c Point \ud074\ub798\uc2a4\uc640 \uad6c\ubd84\ud558\uae30 \uc704\ud574 longitude, latitude\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\uace0 x, y \uc0ac\uc6a9 |\\n| Coordinates | Coordinate\uc758 \uc77c\uae09 \uceec\ub809\uc158 | - |\\n| Position | \uc2e4\uc81c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc5d0 \uc0ac\uc6a9\ud560 \uc704\uce58 \uac12 | Integer \ud0c0\uc785\uc758 x, y \uc0ac\uc6a9 |\\n| Positions | Positions\uc758 \uc77c\uae09 \uceec\ub809\uc158 | - |\\n| RouteImageDrawer | \uc2e4\uc81c \uc774\ubbf8\uc9c0\uc5d0 \uacbd\ub85c\ub97c \uadf8\ub824\uc8fc\ub294 \ud074\ub798\uc2a4 BufferedImage, Graphics2D\ub97c \uac00\uc9c0\uace0 \uc788\uc74c | \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc5d0 \ud544\uc694\ud55c \uc0c1\uc218\uac00 \uc815\uc758\ub418\uc5b4 \uc788\uc74c |\\n| RouteImageUploader | BufferedImage\ub97c \ubc1b\uc544 \uc11c\ubc84\uc5d0 \uc5c5\ub85c\ub4dc \ud558\ub294 \ud074\ub798\uc2a4 | \ud604\uc7ac \uc5c5\ub85c\ub4dc \uc704\uce58\uac00 \uc815\ud574\uc9c0\uc9c0 \uc54a\uc544 \uc77c\ub2e8 \uae30\ubcf8(\ud504\ub85c\uc81d\ud2b8 \ub8e8\ud2b8) \uc704\uce58\uc5d0 \uc0dd\uc131 |\\n| RouteImageGenerator | \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\uace0 \uc5c5\ub85c\ub4dc\ud558\ub294 \uc11c\ube44\uc2a4 | \uc5ec\ud589 \uc885\ub8cc, \uac10\uc0c1 \uc800\uc7a5\uc2dc \ud574\ub2f9 \ud074\ub798\uc2a4\ub97c \ud1b5\ud574 \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc694\uccad |\\n| BufferedImage(AWT) | \uc774\ubbf8\uc9c0 \ub370\uc774\ud130\ub97c \ucc98\ub9ac\ud558\uace0 \uc870\uc791\ud558\ub294 \ub370 \uc0ac\uc6a9 | \uc67c\ucabd \uc0c1\ub2e8\uc758 \uc88c\ud45c\uac00 (0, 0) |\\n| Graphics2D(AWT) | \uc120 \uadf8\ub9ac\uae30, \uc0c9\uc0c1 \uad00\ub9ac \ub4f1\uc744 \uc9c0\uc6d0\ud558\ub294 \ud074\ub798\uc2a4 \uc2e4\uc81c \ud574\ub2f9 \ud074\ub798\uc2a4\uc758 draw \uba54\uc11c\ub4dc\ub97c \uacbd\ub85c\ub97c \uadf8\ub9bc | JDK 1.2 \uc774\ud6c4\uc5d0 \ucd94\uac00\ub428, 2D(\ud3c9\uba74) \uadf8\ub798\ud53d \ud658\uacbd\uc744 \uc9c0\uc6d0, bufferedImage.createGraphics \uba54\uc11c\ub4dc\ub97c \ud1b5\ud574 \uc0dd\uc131 |\\n\\n### \uc758\uc874\uad00\uacc4\\n\\n```mermaid\\ngraph TD\\n C1[Coordinates] --\x3e C[Coordinate]\\n P1[Positions] --\x3e P[Position]\\n\\n\\tRID[RouteImageDrawer] -- \\"\uc911\uc559 \uc815\ub82c\ub41c Positions\ub97c \ubc1b\uc544 \uc774\ubbf8\uc9c0 \uc0dd\uc131\\" --\x3e P1\\n\\tRID --\x3e B[BufferedImage]\\n\\tRID --\x3e G[Graphics2D]\\n\\n\\tC1 -- \\"calculatePositions \uc2e4\uc81c \uc774\ubbf8\uc9c0\uc5d0 \uc0dd\uc131\uc5d0 \ud544\uc694\ud55c \uc704\uce58 \uacc4\uc0b0\\" --\x3e P1\\n\\n\\tRIU[RouteImageUploader] --\x3e B\\n\\tRIG[RouteImageGenerator] --\x3e RID\\n\\tRIG --\x3e RIU\\n\\tRIG --\x3e C1\\n\\tRIG --\x3e P1\\n```\\n\\n### Coordinates(\uc704\ub3c4, \uacbd\ub3c4\uc758 \uc77c\uae09 \uceec\ub809\uc158)\\n\\n`List` 2\uac1c(\uc704\ub3c4, \uacbd\ub3c4)\uc778 \ud615\ud0dc\ub85c \uad00\ub9ac\ud558\ub294 \ubc29\ubc95\uc774 \uc788\uc5c8\uc9c0\ub9cc, \uc704\uce58 \uc810\uc744 \uc5ec\ub7ec\uac1c \ucc0d\ub294 \ubd80\ubd84\uc5d0\uc11c \ub85c\uc9c1\uc774 \ubcf5\uc7a1\ud574 \uc9c8 \uac83 \uac19\uc544\uc11c Coordinate(x, y)\uc640 \uc77c\uae09 \uceec\ub809\uc158\uc778 Coordinates\ub85c \uad00\ub9ac\ud558\uae30\ub85c \ud588\ub2e4. \\nCoordinates \ud074\ub798\uc2a4\uc5d0\ub294 \ub2e4\uc74c \ub450 \uac1c\uc758 \uc778\ud130\ud398\uc774\uc2a4\uac00 \uc874\uc7ac\ud55c\ub2e4.\\n\\n- calculatePositions: \uacbd\ub85c \uc774\ubbf8\uc9c0\uc758 \ud06c\uae30\ub97c \ubc1b\uc544 \uc2e4\uc81c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc2dc \uc0ac\uc6a9\ub420 Positions\ub97c \ubc18\ud658\\n- indexOf: \ub2e4\ub978 Coordinates\ub97c \ubc1b\uc544 \ub3d9\uc77c\ud55c \uc704\uce58\uc810\uc5d0 \ud574\ub2f9\ud558\ub294 \uc778\ub371\uc2a4\ub97c \ubc18\ud658\ud558\ub294 \\n\\nPositions \uacc4\uc0b0 \ub85c\uc9c1\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\uc704\ub3c4, \uacbd\ub3c4 \uac01\uac01\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc2dc \ud544\uc694\ud55c \uac12\uc73c\ub85c \ubcc0\ud658\ud55c\ub2e4.\\n\\n```java title=\\"Coordinates.java\\"\\n// \ud638\ucd9c\\n// List xPositions = toPositions(xValues, maxDifference, routeImageSize);\\n// List yPositions = toPositions(yValues, maxDifference, routeImageSize);\\n\\nprivate List toPositions(List values, Double maxDifference, Integer routeImageSize) {\\n Double minValue = Collections.min(values);\\n return values.stream()\\n .map(value -> normalizeCoordinate(value, maxDifference, minValue))\\n .map(value -> mapToPosition(value, routeImageSize))\\n .toList();\\n}\\n\\nprivate double normalizeCoordinate(Double coordinate, Double maxDifference, Double minValue) {\\n return (coordinate - minValue) / maxDifference;\\n}\\n\\nprivate int mapToPosition(Double coordinate, Integer routeImageSize) {\\n return (int) (coordinate * routeImageSize);\\n}\\n```\\n\\n\uc704\ub3c4\ub85c \uc608\uc2dc\ub4e0 \ub0b4\uc6a9\uc774\ub2e4.\\n\\n1. Collections.min(values) \u2192 \uc704\ub3c4 \ub9ac\uc2a4\ud2b8\uc758 \ucd5c\uc18c\uac12\uc744 \uad6c\ud55c\ub2e4.\\n2. normalizeCoordinate \u2192 \uac01\uac01\uc758 \uc704\ub3c4 \uac12\uc5d0\uc11c \ucd5c\uc18c\uac12\uc744 \ube7c\uace0 0 ~ 1 \uc0ac\uc774 \uac12\uc73c\ub85c \ubcc0\ud658 \ud6c4 **\uc704\uacbd\ub3c4\uc758 \ucd5c\ub300 \ucc28\uc774**\ub85c \ub098\ub208\ub2e4.\\n3. mapToPosition \u2192 \uadf8\ub798\ud504 \ud06c\uae30\ub97c \ubc1b\uc544 0 ~ 1 \uc0ac\uc774 \uac12\uc744 \uc2e4\uc81c \uc774\ubbf8\uc9c0\ub97c \uc704\ud55c \uc704\uce58\uac12\uc73c\ub85c \ubcc0\ud658\ud55c\ub2e4.\\n\\n### Positions(\uc2e4\uc81c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc5d0 \uc0ac\uc6a9\ud560 \uc704\uce58)\\n\\nPositions \ud074\ub798\uc2a4\uc5d0\ub294 \ub2e4\uc74c \ub2e4\uc12f \uac1c\uc758 \uc778\ud130\ud398\uc774\uc2a4\uac00 \uc874\uc7ac\ud55c\ub2e4.\\n\\n- align: \uc774\ubbf8\uc9c0 \uc0ac\uc774\uc988\uc640 \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0ac\uc774\uc988\ub97c \ubc1b\uc544 Position \uac12\ub4e4\uc744 \uc911\uc559 \uc815\ub82c\ud55c\ub2e4.\\n- getPositionsByIndexes: \uc778\ub371\uc2a4 \ub9ac\uc2a4\ud2b8\ub97c \ubc1b\uc544 \uc785\ub825\ubc1b\uc740 \uc778\ub371\uc2a4\uc5d0 \ud574\ub2f9\ud558\ub294 \uac12\ub4e4\uc744 \ubc18\ud658\ud55c\ub2e4.\\n- size: \ud06c\uae30\ub97c \ubc18\ud658\ud55c\ub2e4.\\n- xPositions: x \uac12\ub4e4\uc744 \ubc18\ud658\ud55c\ub2e4.\\n- yPositions: y \uac12\ub4e4\uc744 \ubc18\ud658\ud55c\ub2e4.\\n\\n\uc911\uc559 \uc815\ub82c \ub85c\uc9c1\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\\n```java title=\\"Positions.java\\"\\npublic Positions align(int imageSize, int routeSize) {\\n int xOffset = calculateOffset(Position::x, imageSize);\\n int yOffset = calculateOffset(Position::y, imageSize);\\n\\n return items.stream()\\n .map(item -> new Position(item.x() + xOffset, imageSize - (item.y() + yOffset)))\\n .collect(collectingAndThen(toList(), Positions::new));\\n}\\n\\nprivate int calculateOffset(ToIntFunction positionToInteger, int imageSize) {\\n List positions = items.stream()\\n .mapToInt(positionToInteger)\\n .boxed()\\n .toList();\\n\\n int midValue = (Collections.min(positions) + Collections.max(positions)) / 2;\\n return imageSize / 2 - midValue;\\n}\\n```\\n\\n\uc0c1\ud558\uc88c\uc6b0 \uc5ec\ubc31\uc744 \ub3d9\uc77c\ud558\uac8c \uc8fc\uae30 \uc704\ud574\uc11c offset \uac12\uc744 \uad6c\ud574\uc11c x, y \uac12\uc5d0 \uac01\uac01 \ub354\ud558\ub294 \ud615\ud0dc\ub85c \uc911\uc559 \uc815\ub82c\uc744 \uc218\ud589\ud588\ub2e4. \\nBufferedImage\ub97c \uc0ac\uc6a9\ud560 \ub54c \uc67c\ucabd \uc0c1\ub2e8\uc758 \uc88c\ud45c (0, 0) \uae30\uc900\uc73c\ub85c \uc544\ub798\ub85c \ub0b4\ub824\uac08\uc218\ub85d y \uac12\uc774 \ucee4\uc9c0\uace0, \uc624\ub978\ucabd\uc73c\ub85c \uac08 \uc218\ub85d x \uac12\uc774 \ucee4\uc9c4\ub2e4. \\n\\n![./800.png](./800.png)\\n\\n\ub530\ub77c\uc11c \ucd5c\uc885\uc801\uc73c\ub85c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\uae30 \uc704\ud55c \uac12\uc744 \ub2e4\uc74c\uacfc \uac19\uc774 \uad6c\ud588\ub2e4.\\n\\nx \uac12 \u2192 \uacc4\uc0b0\ud55c offset \uadf8\ub300\ub85c \ub354\ud55c\ub2e4. \\ny \uac12 \u2192 imageSize(800)\uc5d0\uc11c y + offset \uac12\uc744 \ube80\ub2e4. \\n\\n### RouteImageDrawer(\uc2e4\uc81c \uc774\ubbf8\uc9c0\uc5d0 \uacbd\ub85c\ub97c \uadf8\ub824\uc8fc\ub294 \ud074\ub798\uc2a4)\\n\\nBufferedImage, Graphics2D\ub97c \ud544\ub4dc\ub85c \uac00\uc9c0\uace0 \uc788\ub294 \ud074\ub798\uc2a4\ub2e4. \\n\uadf8\ub9bc\uc744 \uadf8\ub9ac\uae30 \uc704\ud574 \uc124\uc815\ud55c \uc0c1\uc218\ub4e4\uc774 \uc874\uc7ac\ud55c\ub2e4.\\n\\n```java title=\\"RouteImageDrawer.java\\"\\n// RGB\uc5d0 \uac01\uac01 8\ube44\ud2b8\uc529 \ud560\ub2f9\ud55c \uac12\uc744 24\ube44\ud2b8 \ud2b8\ub8e8\uceec\ub7ec\ub77c \ubd80\ub978\ub2e4.\\n// \ud574\ub2f9 \uc124\uc815\uc740 24\ube44\ud2b8 + 8\ube44\ud2b8(alpha, \ud22c\uba85\ub3c4)\ub97c \ucd94\uac00\ud55c 32\ube44\ud2b8 \uc774\ubbf8\uc9c0 \ud0c0\uc785\uc774\ub2e4.\\n// \uc774\ub97c RGBA\ub77c\uace0 \ubd80\ub978\ub2e4.\\nprivate static final int IMAGE_TYPE = BufferedImage.TYPE_INT_ARGB;\\n// \ubc30\uacbd \ud22c\uba85\uc0c9\\nprivate static final Color TRANSPARENT = new Color(0, 0, 0, 0);\\n// \uacbd\ub85c\ub97c \uc704\ud55c STROKE\\nprivate static final int LINE_STROKE_WIDTH = 7;\\nprivate static final Stroke LINE_STROKE = new BasicStroke(LINE_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);\\n// \uc704\uce58 \uc810\uc744 \uc704\ud55c STROKE\\nprivate static final int POINT_STROKE_WIDTH = 20;\\nprivate static final Stroke POINT_STROKE = new BasicStroke(POINT_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);\\n// \uc548\ud2f0\uc568\ub9ac\uc5b4\uc2f1 \ub4f1 \ud654\uc9c8 \uac1c\uc120\uc744 \uc704\ud55c \uc124\uc815\\nprivate static final Map renderingHints = Map.of(\\n RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON,\\n RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY,\\n RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC\\n);\\n```\\n\\nRouteImageDrawer \ud074\ub798\uc2a4\uc5d0\ub294 \ub2e4\uc74c \uc138 \uac1c\uc758 \uc778\ud130\ud398\uc774\uc2a4\uac00 \uc874\uc7ac\ud55c\ub2e4.\\n\\n- drawLine: \uc120\uc744 \uadf8\ub9b0\ub2e4.\\n- drawPoint: \uc810\uc744 \ucc0d\ub294\ub2e4.\\n- dispose: \uc790\uc6d0 \ud560\ub2f9\uc744 \ud574\uc81c\ud55c\ub2e4. \\n\\ndispose\uc758 \uacbd\uc6b0 \ub0b4\ubd80\uc5d0\uc11c \uc0dd\uc131\ub41c graphics2D\uc5d0 \ub300\ud55c \uc790\uc6d0 \ud560\ub2f9\uc744 \ud574\uc81c\ud558\ub294 \uba54\uc11c\ub4dc\uc778 graphics2D.dispose\ub97c \ud638\ucd9c\ud55c\ub2e4.\\n\\n## \uc774\ubbf8\uc9c0 \uc0dd\uc131 Flow\\n\\n### 1. \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc900\ube44\\n\\n```mermaid\\nsequenceDiagram\\n \uc678\ubd80 \ud074\ub798\uc2a4 ->> RouteImageGenerator: \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc694\uccad(\uc704\uacbd\ub3c4, \uc704\uce58 \uc810\uc744 \ucc0d\uc744 \uac12 \uc804\ub2ec)\\n RouteImageGenerator->>RouteImageDrawer: ImageSize\ub97c \uc804\ub2ec\ud558\uc5ec \uac1d\uccb4 \uc0dd\uc131, \ub0b4\ubd80\uc5d0\uc11c BufferedImage, Graphincs2D \uc0dd\uc131\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): \uc704\uacbd\ub3c4 \uc774\uc6a9\ud558\uc5ec Coordinates1 \uc0dd\uc131\\n RouteImageGenerator->>Coordinates2(\uc704\uce58\uc810): \uc704\uce58\uc810 \uc774\uc6a9\ud558\uc5ec Coordinates2 \uc0dd\uc131\\n\\n```\\n\\n### 2. \uc120 \uadf8\ub9ac\uae30 \uc694\uccad\\n\\n```mermaid\\nsequenceDiagram\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): \uc815\ub82c\ub41c Positions\ub97c \uc0dd\uc131 \uc694\uccad\\n Coordinates1(\uc704\uacbd\ub3c4)->>RouteImageGenerator: \uc815\ub82c\ub41c Positions \ubc18\ud658\\n RouteImageGenerator->>RouteImageDrawer: \uc815\ub82c\ub41c Positions\ub97c \uacbd\ub85c \uadf8\ub9ac\uae30 \uc694\uccad\\n```\\n\\n### 3. \uc704\uce58 \uc810 \uadf8\ub9ac\uae30 \uc694\uccad\\n\\n```mermaid\\nsequenceDiagram\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): Coordinate2(\uc704\uce58\uc810)\ub97c \uc804\ub2ec\ud558\uace0 \ud574\ub2f9 \uc704\uce58\uc810\uacfc \uc77c\uce58\ud558\ub294 Coordinate\uc758 \uc778\ub371\uc2a4 \uc0dd\uc131 \uc694\uccad\\n Coordinates1(\uc704\uacbd\ub3c4)->>RouteImageGenerator: \uc704\uce58\uc810\uc5d0 \ud574\ub2f9\ud558\ub294 \uc778\ub371\uc2a4(List) \ubc18\ud658\\n RouteImageGenerator->>\uc815\ub82c\ub41c Positions: \uc778\ub371\uc2a4(List)\ub97c \uc804\ub2ec\ud558\uc5ec \uc778\ub371\uc2a4\uc5d0 \ud574\ub2f9\ud558\ub294 Positions \uc0dd\uc131 \uc694\uccad\\n \uc815\ub82c\ub41c Positions->>RouteImageGenerator: \uc704\uce58\uc810\uc5d0 \ud574\ub2f9\ud558\ub294 Positions \ubc18\ud658(pointPositions)\\n\\n RouteImageGenerator->>RouteImageDrawer: pointPositions\ub97c \uc804\ub2ec\ud558\uc5ec \uc704\uce58 \uc810 \uadf8\ub9ac\uae30 \uc694\uccad\\n```\\n\\n### 4. \uc5c5\ub85c\ub4dc \uc694\uccad\\n\\n```mermaid\\nsequenceDiagram\\n \\tRouteImageGenerator->>RouteImageUploader: bufferedImage(RouteImageDrawer\uc5d0\uc11c getter \uc0ac\uc6a9)\ub97c \uc804\ub2ec\ud558\uc5ec \uc774\ubbf8\uc9c0 \uc800\uc7a5 \uc694\uccad\\n \\tRouteImageUploader->>RouteImageGenerator: \uc800\uc7a5 \ud6c4 \uc800\uc7a5\ub41c \uc774\ubbf8\uc9c0\uba85(\ub610\ub294 url) \ubc18\ud658\\n \\tRouteImageGenerator->>\uc678\ubd80 \ud074\ub798\uc2a4: \uc800\uc7a5\ub41c \uc774\ubbf8\uc9c0\uba85(\ub610\ub294 url) \ubc18\ud658\\n```\\n\\n### \uc804\uccb4 Flow\\n\\n```mermaid\\nsequenceDiagram\\n \uc678\ubd80 \ud074\ub798\uc2a4 ->> RouteImageGenerator: \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc694\uccad(\uc704\uacbd\ub3c4, \uc704\uce58 \uc810\uc744 \ucc0d\uc744 \uac12 \uc804\ub2ec)\\n RouteImageGenerator->>RouteImageDrawer: ImageSize\ub97c \uc804\ub2ec\ud558\uc5ec \uac1d\uccb4 \uc0dd\uc131, \ub0b4\ubd80\uc5d0\uc11c BufferedImage, Graphincs2D \uc0dd\uc131\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): \uc704\uacbd\ub3c4 \uc774\uc6a9\ud558\uc5ec Coordinates1 \uc0dd\uc131\\n RouteImageGenerator->>Coordinates2(\uc704\uce58\uc810): \uc704\uce58\uc810 \uc774\uc6a9\ud558\uc5ec Coordinates2 \uc0dd\uc131\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): \uc815\ub82c\ub41c Positions\ub97c \uc0dd\uc131 \uc694\uccad\\n Coordinates1(\uc704\uacbd\ub3c4)->>RouteImageGenerator: \uc815\ub82c\ub41c Positions \ubc18\ud658\\n RouteImageGenerator->>RouteImageDrawer: \uc815\ub82c\ub41c Positions\ub97c \uacbd\ub85c \uadf8\ub9ac\uae30 \uc694\uccad\\n RouteImageGenerator->>Coordinates1(\uc704\uacbd\ub3c4): Coordinate2(\uc704\uce58\uc810)\ub97c \uc804\ub2ec\ud558\uace0 \ud574\ub2f9 \uc704\uce58\uc810\uacfc \uc77c\uce58\ud558\ub294 Coordinate\uc758 \uc778\ub371\uc2a4 \uc0dd\uc131 \uc694\uccad\\n Coordinates1(\uc704\uacbd\ub3c4)->>RouteImageGenerator: \uc704\uce58\uc810\uc5d0 \ud574\ub2f9\ud558\ub294 \uc778\ub371\uc2a4(List) \ubc18\ud658\\n RouteImageGenerator->>\uc815\ub82c\ub41c Positions: \uc778\ub371\uc2a4(List)\ub97c \uc804\ub2ec\ud558\uc5ec \uc778\ub371\uc2a4\uc5d0 \ud574\ub2f9\ud558\ub294 Positions \uc0dd\uc131 \uc694\uccad\\n \uc815\ub82c\ub41c Positions->>RouteImageGenerator: \uc704\uce58\uc810\uc5d0 \ud574\ub2f9\ud558\ub294 Positions \ubc18\ud658(pointPositions)\\n\\n RouteImageGenerator->>RouteImageDrawer: pointPositions\ub97c \uc804\ub2ec\ud558\uc5ec \uc704\uce58 \uc810 \uadf8\ub9ac\uae30 \uc694\uccad\\n RouteImageGenerator->>RouteImageUploader: bufferedImage(RouteImageDrawer\uc5d0\uc11c getter \uc0ac\uc6a9)\ub97c \uc804\ub2ec\ud558\uc5ec \uc774\ubbf8\uc9c0 \uc800\uc7a5 \uc694\uccad\\n RouteImageUploader->>RouteImageGenerator: \uc800\uc7a5 \ud6c4 \uc800\uc7a5\ub41c \uc774\ubbf8\uc9c0\uba85(\ub610\ub294 url) \ubc18\ud658\\n RouteImageGenerator->>\uc678\ubd80 \ud074\ub798\uc2a4: \uc800\uc7a5\ub41c \uc774\ubbf8\uc9c0\uba85(\ub610\ub294 url) \ubc18\ud658\\n\\t\\n```"},{"id":"route-image-python","metadata":{"permalink":"/route-image-python","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c.mdx","source":"@site/blog/2023-3/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c.mdx","title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c","description":"\uac1c\uc694","date":"2023-07-31T00:00:00.000Z","formattedDate":"2023\ub144 7\uc6d4 31\uc77c","tags":[{"label":"Image","permalink":"/tags/image"},{"label":"Python","permalink":"/tags/python"}],"readingTime":6.185,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c","slug":"route-image-python","tags":["Image","Python"]},"prevItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604","permalink":"/route-image-implementation"},"nextItem":{"title":"Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30","permalink":"/mock-static-method"}},"content":"### \uac1c\uc694\\n\\n\uc774\uc804\uc5d0 \uae30\uc220 \uad6c\ud604 \uac00\ub2a5 \uc5ec\ubd80\ub97c \uc870\uc0ac\ud558\uba74\uc11c \ud30c\uc774\uc36c\uc744 \uc0ac\uc6a9\ud55c \ub0b4\uc6a9\uc744 \uc815\ub9ac\ud55c \ub0b4\uc6a9\uc774\ub2e4. \\n\\n### \uc0ac\uc6a9 \uae30\uc220\\n\\n\uc5b8\uc5b4: Python 3.10 \\n\uc774\ubbf8\uc9c0 \uc0dd\uc131: matplotlib \\n\uc11c\ube44\uc2a4: AWS Lambda, AWS API Gateway \\n\uc774\ubbf8\uc9c0 \uc800\uc7a5 \ubc0f URL: AWS S3, AWS CloudFront \\n\\n\ud50c\ub85c\uc6b0\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\\n```mermaid\\ngraph LR\\n Server -- \uc0dd\uc131 \uc694\uccad --\x3e AG[API Gateway] --\x3e Lambda --\x3e S3\\n Client --\x3e CloudFront --\x3e S3\\n```\\n\\n### \uc694\uad6c\uc0ac\ud56d\\n\\n![./route.png](./route.png)\\n\\n\uc6b0\uce21 \uc0c1\ub2e8\uc758 \uacbd\ub85c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\ub824\uace0 \ud55c\ub2e4. \\n\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc5d0 \ub300\ud55c \uc694\uad6c\uc0ac\ud56d\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4.\\n\\n- \uc704\ub3c4, \uacbd\ub3c4\ub85c \uc774\ub8e8\uc5b4\uc9c4 \ubc30\uc5f4\uc744 \uc785\ub825\ubc1b\ub294\ub2e4. \\n- \uc774\ubbf8\uc9c0 \uc0dd\uc131\\n- \uc120\uacfc \uc810 \ud45c\ud604\\n- \ud22c\uba85\ud55c \ubc30\uacbd\uc0c9\\n- \uc704\uacbd\ub3c4 \ucc28\uc774\uac00 \ud06c\ub4e0 \uc791\ub4e0 \uc81c\uacf5\ud558\ub294 \uc774\ubbf8\uc9c0 \ub0b4\uc5d0 \uacbd\ub85c\uac00 \ub2e4 \ud3ec\ud568\ub418\uc5b4 \uc788\uc5b4\uc57c \ud55c\ub2e4. \\n\\n### \uc774\ubbf8\uc9c0 \ucd9c\ub825 \ubc29\uc2dd\\n\\n1. \uc704\uacbd\ub3c4\ub97c \ucc98\ub9ac\ud55c \uac12\uc73c\ub85c \uc9c1\uc811 \uacbd\ub85c\ub97c \uadf8\ub9b0 \ub2e4\uc74c \uc774\ubbf8\uc9c0 \ud615\ud0dc\ub85c \uc800\uc7a5\\n2. \ud50c\ub86f\uc744 \uadf8\ub824\uc8fc\ub294 \ub77c\uc774\ube0c\ub7ec\ub9ac \uc0ac\uc6a9\ud558\uc5ec \uc774\ubbf8\uc9c0 \ud615\ud0dc\ub85c \uc800\uc7a5\\n\\n\uc774\ubbf8\uc9c0 \ucd9c\ub825 \ubc29\uc2dd\uc758 \uacbd\uc6b0 1\ubc88\uacfc 2\ubc88\uc744 \uace0\ubbfc\ud588\uc5c8\ub2e4. \\n\ud30c\uc774\uc36c\uc73c\ub85c\ub294 \ud50c\ub86f\uc744 \uadf8\ub824\uc8fc\ub294 \ub77c\uc774\ube0c\ub7ec\ub9ac\uc778 matplotlib\uc744 \uc0ac\uc6a9\ud588\ub2e4. \\n\\n### \ub85c\uceec\uc5d0\uc11c \uae30\ub2a5 \uad6c\ud604\\n\\n```python\\nimport time\\n\\nimport matplotlib.pyplot as plt\\n\\n\\ndef draw(point):\\n start = time.time()\\n x, y = zip(*point)\\n pixel_x, pixel_y = convert_to_pixel_values(x, y)\\n draw_lines(pixel_x, pixel_y)\\n end = time.time()\\n print(end - start)\\n \\ndef convert_to_pixel_values(x, y):\\n max_diff = max(max(x) - min(x), max(y) - min(y))\\n return scale_to_pixel_values(x, max_diff), scale_to_pixel_values(y, max_diff)\\n\\n\\ndef scale_to_pixel_values(points, max_diff):\\n min_value = min(points)\\n scaled_coordinates = [(p - min_value) / max_diff for p in points]\\n return scaled_coordinates\\n\\n\\ndef draw_lines(x, y):\\n figure = plt.gcf()\\n figure.set_size_inches(5, 5)\\n plt.plot(x, y, c = \'w\',linewidth=5)\\n plt.scatter(x[3],y[3], c = \'w\', s = 125)\\n plt.axis(\'off\')\\n plt.savefig(\'name.png\', transparent=True, format=\'png\')\\n\\npoint = [\\n [126.96352960597338, 37.590841000217125],\\n [126.96987292787792, 37.58435564234159],\\n [126.98128481452298, 37.58594375113966],\\n [126.99360339342958, 37.58248524741927],\\n [126.99867565340067, 37.56778118088622],\\n [127.001935378366117, 37.55985240444085],\\n [126.9831048919687, 37.548030119488665],\\n [126.97189273528845, 37.5119879225856],\\n [127.02689859997221, 37.48488593333883]\\n]\\n\\ndraw(point)\\n```\\n\\n\uc0dd\uc131 \uacb0\uacfc\ub294 \uc544\ub798\uc640 \uac19\ub2e4. (\uc608\uc2dc\ub97c \uc704\ud574 \uac80\uc740\uc0c9\uc73c\ub85c \ucd9c\ub825)\\n\\n![./routeImage.png](./routeImage.png)\\n\\n### AWS Lambda\\n\\n\uc378\ub124\uc77c \uc0dd\uc131 \uc11c\ubc84\ub97c \ub530\ub85c \ub450\uae30\ub294 \uae30\ub2a5 \ub300\ube44 \ube44\uc6a9\uc774 \ub108\ubb34 \ud074 \uac83\uc774\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\ub530\ub77c\uc11c \uc11c\ubc84\ub9ac\uc2a4\ub85c \ud30c\uc77c\uc744 \ucc98\ub9ac\ud588\ub2e4. \\n\ucd94\uac00\ub85c s3 \uc811\uadfc\uc740 boto3\ub97c \uc0ac\uc6a9\ud588\ub2e4. \\n\\n### \ub78c\ub2e4 S3 \uc811\uadfc\uc744 \uc704\ud55c IAM \uc0dd\uc131\\n\\nAmazonS3FullAccess, AmazonS3ObjectLambdaExecutionRolePolicy \ub450\uac00\uc9c0\ub97c \ucd94\uac00\ud574\uc11c Lambda \uc804\uc6a9 \uc5ed\ud560\uc744 \ub9cc\ub4e4\uc5b4 \uc0ac\uc6a9\ud588\ub2e4. \\n\\n### \ub78c\ub2e4 \ubc30\ud3ec\uc6a9 \ucf54\ub4dc\\n\\n\uae30\uc220 \uad6c\ud604 \uac00\ub2a5 \uc5ec\ubd80\ub97c \ud655\uc778\ud560 \ub550 \uc704\uce58 \uc810\uc744 \ucc0d\ub294 \uae30\ub2a5\uc744 \ub78c\ub2e4\uc5d0 \ubc30\ud3ec\ud558\uc9c0 \uc54a\uc558\ub2e4. \\n\\n```python\\n\\nimport io\\nimport uuid\\n\\nimport boto3\\nimport matplotlib.pyplot as plt\\n\\nPIXEL = 255\\nBUCKET_NAME = \'image-plot\'\\nS3 = \'s3\'\\n\\ndef lambda_handler(event, context):\\n x = event[\'x\']\\n y = event[\'y\']\\n image_name = str(uuid.uuid4())\\n\\n img_data = draw(x, y)\\n s3 = boto3.client(S3)\\n s3.put_object(Body=img_data.getvalue(), ContentType=\'image/png\', Bucket=BUCKET_NAME, Key=image_name)\\n url = f\'https://{BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com/{image_name}\'\\n\\n return {\\n \'statusCode\': 200,\\n \'body\': url\\n }\\n\\ndef draw(x, y):\\n pixel_x, pixel_y = convert_to_pixel_values(x, y)\\n img_data = draw_lines(pixel_x, pixel_y)\\n plt.close()\\n return img_data\\n\\ndef convert_to_pixel_values(x, y):\\n max_diff = max(max(x) - min(x), max(y) - min(y))\\n return scale_to_pixel_values(x, max_diff), scale_to_pixel_values(y, max_diff)\\n\\ndef scale_to_pixel_values(points, max_diff):\\n min_value = min(points)\\n scaled_coordinates = [(p - min_value) / max_diff for p in points]\\n pixel_values = [int(p * PIXEL) for p in scaled_coordinates]\\n return pixel_values\\n\\ndef draw_lines(x, y):\\n plt.plot(x, y, \'k-\', linewidth=10)\\n plt.axis(\'off\')\\n img_data = io.BytesIO()\\n plt.savefig(img_data, transparent=True, format=\'png\')\\n img_data.seek(0)\\n return img_data\\n\\n```\\n\\n### Layer \ucd94\uac00\ub97c \uc704\ud55c zip \ud30c\uc77c \uc0dd\uc131\\n\\nmatplotlib\uc758 \uacbd\uc6b0 \uc678\ubd80 \ub77c\uc774\ube0c\ub7ec\ub9ac\uae30 \ub54c\ubb38\uc5d0 \ub530\ub85c Layer\ub97c \ucd94\uac00\ud574\uc57c \ud55c\ub2e4. \\nzip \ud30c\uc77c\uc744 \ub9cc\ub4e4\uc5b4\uc11c \uc5c5\ub85c\ub4dc\ud574\uc57c\ud55c\ub2e4. \\n\uc774\ub54c python\uc758 Lambda \ub7f0\ud0c0\uc784\uc5d0 \ub300\ud55c \uacc4\uce35 \uacbd\ub85c\ub294 python\uc774\ub2e4. \\n\ub530\ub77c\uc11c \uc555\ucd95\ud55c zip \ud30c\uc77c\uc740 \ub2e4\uc74c\uacfc \uac19\uc740 \uad6c\uc870\ub97c \ub744\uc5b4\uc57c \ud55c\ub2e4. \\n\\n```\\npillow.zip\\n\u2502 python/PIL\\n\u2514 python/Pillow-5.3.0.dist-info\\n```\\n\\nUbuntu \uae30\uc900 \ub2e4\uc74c \uba85\ub839\uc5b4\ub97c \uc785\ub825\ud558\uc5ec \uc0dd\uc131\uc744 \uc9c4\ud589\ud588\ub2e4. \\n\\n```\\nsudo apt update\\nsudo apt install zip\\nsudo apt install python3-pip\\n\\nmkdir python\\npip3 install matplotlib -t python # pip3 install \uc124\uce58\ud560_\ud328\ud0a4\uc9c0 -t \uc124\uce58_\uacbd\ub85c\\nzip -r my_layer.zip python # zip -r \uc555\ucd95_\ud30c\uc77c\uba85 \uc555\ucd95_\ud30c\uc77c\uc774_\uc874\uc7ac\ud558\ub294_\uacbd\ub85c\\n```\\n\\n### `No module named \'numpy.core._multiarray_umath\'` \uc5d0\ub7ec\\n\\nLayer \ucd94\uac00 \ud6c4 \ub78c\ub2e4 \uc2e4\ud589 \uc2dc \ubc1c\uc0dd\ud55c \uc5d0\ub7ec\uc600\ub2e4. \\n\ucc98\uc74c\uc5d0 mac\uc5d0\uc11c zip \ud30c\uc77c\uc744 \uc0dd\uc131\ud574\uc11c \uc5c5\ub85c\ub4dc\ud588\ub294\ub370 \ud574\ub2f9 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud588\ub2e4. \\n\uc774\ub294 lambda\uac00 \ub3cc\uc544\uac00\ub294 \ub3d9\uc77c\ud55c \ud658\uacbd\uc5d0\uc11c layer\ub97c \uc704\ud55c zip \ud30c\uc77c\uc744 \ub9cc\ub4e4\uc9c0 \uc54a\uc544\uc11c \ubc1c\uc0dd\ud558\ub294 \ubb38\uc81c\ub2e4. \\n\uac04\ub2e8\ud558\uac8c ec2 \uc778\uc2a4\ud134\uc2a4\ub97c \ud558\ub098 \ub9cc\ub4e4\uc5b4\uc11c \ub530\ub85c Layer\ub97c \uc0dd\uc131\ud558\uba74 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud558\uc9c0 \uc54a\ub294\ub2e4. \\n\\n### \uc801\uc815\uae30\uc220\uc5d0 \ub300\ud55c \uc0dd\uac01\\n\\n\ud504\ub85c\uc81d\ud2b8\uc5d0 Lambda\uc640 Python\uc744 \uc0ac\uc6a9\ud558\ub824\uace0 \ud588\uc9c0\ub9cc \uc544\uc27d\uac8c\ub3c4 \ubc18\ub824\ub2f9\ud588\ub2e4. \\nAWS Lambda\ub97c \uc0ac\uc6a9\ud558\ub294 \uac83\uc740 \uc778\uc2a4\ud134\uc2a4\uc5d0 \ud574\ub2f9 \ucf54\ub4dc\ub97c \ubc30\ud3ec\ud558\ub294 \uac83\ubcf4\ub2e4 \ub354 \ud6a8\uc728\uc801\uc778 \ubc29\ubc95\uc77c \uc218 \uc788\ub2e4. \\n\ud558\uc9c0\ub9cc \ud604\uc7ac \ud504\ub85c\uc81d\ud2b8\uc5d0\uc11c \uac00\uc6a9 \uac00\ub2a5\ud55c \uc790\uc6d0, \uae30\uc220\uc758 \ub09c\uc774\ub3c4, \uc0ac\uc6a9\ud558\ub294 \ud300\uc6d0\uc744 \uace0\ub824\ud55c\ub2e4\uba74 Lambda\ub294 \uc801\uc815\uae30\uc220\uc774 \uc544\ub2d0 \uc218 \uc788\ub2e4. \\n\ub530\ub77c\uc11c \ud574\ub2f9 \uc774\ubbf8\uc9c0 \uc0dd\uc131\uae30\ub97c \uc5b4\ub5bb\uac8c \uc801\uc6a9\ud560\uc9c0 \uc870\uae08 \ub354 \uace0\ub824\ub97c \ud574\uc57c \ub420 \uac83\uc73c\ub85c \ubcf4\uc778\ub2e4. \\n\\n**\ucd5c\uc885\uc801\uc73c\ub85c Java AWT\ub97c \uc0ac\uc6a9\ud558\uae30\ub85c \uacb0\uc815\ud588\ub2e4.**\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n[AWS Lambda](https://aws.amazon.com/ko/lambda/) \\n[Lambda Layer](https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/configuration-layers.html) \\n[Python Lambda \ud568\uc218\uc5d0 \ub300\ud55c .zip \ud30c\uc77c \uc544\uce74\uc774\ube0c \uc791\uc5c5](https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/python-package.html) \\n[No module named \'numpy.core._multiarray_umath\'](https://gist.github.com/ksmin23/0f3f243408a8497f766b43cf589fea7b) \\n[\uc0ac\ub840\ubcc4\ub85c \uc54c\uc544\ubcf8 \uc548\uc804\ud55c S3 \uc0ac\uc6a9 \uac00\uc774\ub4dc](https://techblog.woowahan.com/6217/)"},{"id":"mock-static-method","metadata":{"permalink":"/mock-static-method","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-07-30-Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30.mdx","source":"@site/blog/2023-3/2023-07-30-Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30.mdx","title":"Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30","description":"\uac1c\uc694","date":"2023-07-30T00:00:00.000Z","formattedDate":"2023\ub144 7\uc6d4 30\uc77c","tags":[{"label":"Mockito","permalink":"/tags/mockito"},{"label":"static","permalink":"/tags/static"}],"readingTime":2.635,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30","slug":"mock-static-method","tags":["Mockito","static"]},"prevItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c","permalink":"/route-image-python"},"nextItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd","permalink":"/route-image-intro"}},"content":"### \uac1c\uc694\\n\\n\uc815\uc801 \ud329\ud130\ub9ac \uba54\uc11c\ub4dc\ub97c \ubaa8\ud0b9\ud55c\ub2e4\ub294 \uac83\uc740 \uac1d\uccb4\uc9c0\ud5a5\uc801\uc778 \uad00\uc810\uc5d0\uc11c \ubcfc \ub54c \uc548\ud2f0\ud328\ud134\uc774\ub2e4. \\n\ud558\uc9c0\ub9cc \ud2b9\uc218\ud55c \uacbd\uc6b0\uc5d0\ub294 \uc815\uc801 \uba54\uc11c\ub4dc\ub97c \ubaa8\ud0b9\ud558\ub294 \uac83\uc774 \ud544\uc694\ud560 \uc218 \uc788\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\\n\uc608\ub97c \ub4e4\uc5b4 \ub808\uac70\uc2dc \ucf54\ub4dc\ub97c \ud14c\uc2a4\ud2b8 \ud55c\ub2e4\ub358\uc9c0, IO \uad00\ub828\ud55c \ubd80\ubd84\uc744 \ud14c\uc2a4\ud2b8 \ud560 \ub54c \uc815\ub9d0 \ud544\uc694\ud55c \ubd80\ubd84\uc5d0\ub9cc \uc801\uc6a9\ud560 \uc218 \uc788\uc744 \uac83\uc774\ub2e4. \\n\\n\ud504\ub85c\uc81d\ud2b8\ub97c \uc9c4\ud589\ud558\uba70 ImageIo.write \uba54\uc11c\ub4dc\uac00 \ud638\ucd9c\ub418\ub294 \uc9c0 \uac80\uc99d\uc774 \ud544\uc694\ud588\ub2e4. \\n\ud574\ub2f9 static \uba54\uc11c\ub4dc\ub97c \ud638\ucd9c\ud558\ub294 \ubd80\ubd84\uc744 \ub530\ub85c RouteImageUploader \ud074\ub798\uc2a4\ub85c \ucd5c\ub300\ud55c \ubd84\ub9ac\ud588\ub2e4. \\n\uc774\ubbf8\uc9c0 \uc800\uc7a5 \uae30\ub2a5 \uc790\uccb4\uac00 \uc678\ubd80\ub85c \ub098\uac00\ub294 \uc0c1\ud638\uc791\uc6a9\uc774\uace0, \ud638\ucd9c \ud69f\uc218\ub97c \uac80\uc0ac\ud558\ub294\ub370\ub294 mock\uc744 \uc0ac\uc6a9\ud558\ub294\uac8c \uc801\uc808\ud558\ub2e4\uace0 \ud310\ub2e8\ud588\ub2e4. \\n\\n```java\\npublic void upload(BufferedImage bufferedImage) {\\n File file = new File(\ud30c\uc77c\uacbd\ub85c);\\n try {\\n ImageIO.write(bufferedImage, ROUTE_IMAGE_FORMAT, file);\\n } catch (IOException e) {\\n throw new DrawException(IMAGE_SAVE_FAIL);\\n }\\n}\\n```\\n\\n### Mocking static methods\\n\\nMockito 3.4.0 \uc774\ud6c4\uc5d0\ub294 static method\ub97c \ubaa8\ud0b9\ud560 \uc218 \uc788\ub294 Mockito.mockStatic \uba54\uc11c\ub4dc\ub97c \uc9c0\uc6d0\ud55c\ub2e4. \\nmockStatic\uc744 \uc0ac\uc6a9\ud558\uba74 `MockedStatic`\uc774 \ubc18\ud658\ub418\ub294\ub370 \uc0ac\uc6a9 \ud6c4 \uaf2d close\ub97c \ud574\uc918\uc57c \ud55c\ub2e4. \\n\\nJUnit\uc758 @BeforeAll\ub85c \uc124\uc815\ud558\uace0 @AfterAll \uba54\uc11c\ub4dc\ub85c \uc885\ub8cc\ud558\ub294 \ubc29\ubc95\ub3c4 \uc788\uc9c0\ub9cc `MockedStatic`\uc758 \uc0c1\uc704 \uc778\ud130\ud398\uc774\uc2a4\uc778 ScopedMock\uc774 AutoCloseable\uc744 \uad6c\ud604\ud558\uace0 \uc788\uae30\uc5d0 try-with-resources\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc774 \ub354\uc6b1 \uc88b\uc740 \uac83 \uac19\ub2e4. \\n\\n```java\\n// given\\nBufferedImage bufferedImage = new BufferedImage(800, 800, BufferedImage.TYPE_INT_ARGB);\\nRouteImageUploader routeImageUploader = new RouteImageUploader();\\n\\n// expect\\ntry (MockedStatic imageIO = Mockito.mockStatic(ImageIO.class)) {\\n routeImageUploader.upload(bufferedImage);\\n imageIO.verify(\\n () -> ImageIO.write(any(BufferedImage.class), any(String.class), any(File.class)),\\n times(1)\\n );\\n}\\n```\\n\\n### \ub9c8\uce58\uba70\\n\\n\uc815\uc801 \uba54\uc11c\ub4dc\ub97c \ubaa8\ud0b9\ud558\ub294 \uac83\uc740 \uc548\ud2f0\ud328\ud134\uc774\uc73c\ub85c \uc801\uc808\ud55c \ucd94\uc0c1\ud654\ub97c \uc774\uc6a9\ud574 \ud14c\uc2a4\ud2b8 \ud558\uae30 \uc88b\uc740 \ucf54\ub4dc\ub97c \ub9cc\ub4dc\ub294 \uc5f0\uc2b5\uc744 \ud558\uc790. \\n\ud558\uc9c0\ub9cc \ucd94\uc0c1\ud654\ub97c \ud558\uba74 \ud560 \uc218\ub85d \ucf54\ub4dc\uc758 \ubcf5\uc7a1\ub3c4\ub294 \uc99d\uac00\ud55c\ub2e4. \\n\ud56d\uc0c1 \uc0c1\ud669\uc744 \uace0\ub824\ud558\uace0 \uac04\uacb0\ud568\uc744 \ud3ec\uae30\ud560 \ub9cc\ud07c \uc911\uc694\ud55c \ubd80\ubd84\uc778\uc9c0 \uc801\uc808\ud55c \ud2b8\ub808\uc774\ub4dc\uc624\ud504\ub97c \uace0\ub824\ud558\uc790. \\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n[Mocking static methods](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#static_mocks) \\n[Mockito mock static methods](https://www.baeldung.com/mockito-mock-static-methods) \\n[Enable mocking static methods in Mockito](https://github.com/mockito/mockito/issues/1013)"},{"id":"route-image-intro","metadata":{"permalink":"/route-image-intro","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd.mdx","source":"@site/blog/2023-3/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd.mdx","title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd","description":"./route.png","date":"2023-07-27T00:00:00.000Z","formattedDate":"2023\ub144 7\uc6d4 27\uc77c","tags":[{"label":"image","permalink":"/tags/image"},{"label":"awt","permalink":"/tags/awt"}],"readingTime":5.865,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd","slug":"route-image-intro","tags":["image","awt"]},"prevItem":{"title":"Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30","permalink":"/mock-static-method"},"nextItem":{"title":"\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1","permalink":"/java-spring-springboot"}},"content":"![./route.png](./route.png)\\n\\n### \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc758 \ucc45\uc784\\n\\n\uc704 \uc640\uc774\uc5b4 \ud504\ub808\uc784\uc5d0\uc11c `\uc5ec\ud589 \ud788\uc2a4\ud1a0\ub9ac`\uc640 `\uc5ec\ud589\uc5d0 \ub300\ud55c \uac10\uc0c1\uc744 \uc704\ud55c \uacbd\ub85c \uc774\ubbf8\uc9c0`\uc758 \uacbd\uc6b0 \ub124\uc774\ubc84 \uc9c0\ub3c4\ub97c \uc0ac\uc6a9\ud558\uc5ec \ud574\ub2f9 \uae30\ub2a5\uc744 \uad6c\ud604\ud560 \uc218 \uc5c6\uc73c\ub2c8 \ub2f9\uc5f0\ud788 \ub9f5 API\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \ub3c4\ud615 \uadf8\ub9ac\uae30 API(\ub124\uc774\ubc84 \ub9f5 API \uae30\uc900 Polyline)\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc5c6\ub2e4. \\n\ub530\ub77c\uc11c \uc774\ubbf8\uc9c0\ub97c \uc9c1\uc811 \uc0dd\uc131\ud558\uac70\ub098, \ud074\ub77c\uc774\uc5b8\ud2b8\uc5d0\uc11c \uc9c1\uc811 \uc704\uacbd\ub3c4\ub97c \uc774\uc6a9\ud558\uc5ec \uadf8\ub824\uc57c \ud55c\ub2e4.\\n\\n\ud574\ub2f9 \uc694\uad6c\uc0ac\ud56d\uc744 \ud574\uacb0\ud558\uae30 \uc704\ud574\uc11c\ub294 \ub2e4\uc74c\uacfc \uac19\uc740 \uae30\ub2a5\uc744 \uac00\uc9c4 \ub77c\uc774\ube0c\ub7ec\ub9ac\uac00 \ud544\uc694\ud558\ub2e4.\\n\\n- \uc774\ubbf8\uc9c0 \uc0dd\uc131\\n- \uc120\uacfc \uc810 \ud45c\ud604\\n- \ud22c\uba85\ud55c \ubc30\uacbd\uc0c9\\n\\n\ud604\uc7ac \ud074\ub77c\uc774\uc5b8\ud2b8\uc758 \ubc14\uc05c \uc77c\uc815\uacfc \uae30\ub2a5 \uad6c\ud604\uc5d0 \uc788\uc5b4 \uc57d\uac04\uc758 \uc5f0\uc0b0\uc774 \ub4e4\uc5b4\uac04\ub2e4\ub294 \ubd80\ubd84\uc744 \uace0\ub824\ud558\uc5ec \ubc31\uc5d4\ub4dc\uc5d0\uc11c \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud558\uae30\ub85c \uacb0\uc815\uc744 \ub0b4\ub838\ub2e4.\\n\\n### \uace0\ub824\ud55c \uae30\uc220\\n\\n\ubc31\uc5d4\ub4dc\uc5d0\uc11c \uc774\ubbf8\uc9c0 \uc0dd\uc131\uc744 \ud558\uae30 \uc704\ud574 \ub2e4\uc74c\uacfc \uac19\uc740 \ub77c\uc774\ube0c\ub7ec\ub9ac \ub610\ub294 \uae30\uc220\ub4e4\uc744 \ud655\uc778\ud574 \ubcf4\uc558\ub2e4. \\n\\n- Python\uc758 Matplotlib\\n- **AWT(Abstract Window Toolkit) [\ucd5c\uc885 \uc120\ud0dd]**\\n- \uc774\ubbf8\uc9c0 \ucc98\ub9ac \ub77c\uc774\ube0c\ub7ec\ub9ac \ubc0f Java\uc5d0\uc11c \ub0b4\ubd80\uc801\uc73c\ub85c Matplotlib \uc0ac\uc6a9\ud560 \uc218 \uc788\ub294 \ub77c\uc774\ube0c\ub7ec\ub9ac (\uc6d0\ud558\ub294 \uae30\ub2a5 \uc5c6\uc74c)\\n- Java Swing, Java FX (\ub2e8\uc21c\ud55c \uc120 \uadf8\ub9ac\uae30 + \uc810 \ucc0d\uae30\ub77c \ubd88\ud544\uc694)\\n\\n## Python & Matplotlib\\n\\n\ub370\uc774\ud130 \uc2dc\uac01\ud654 \ub77c\uc774\ube0c\ub7ec\ub9ac \\n\uc774\ubbf8\uc9c0 \uc0dd\uc131 \ubc0f \ub85c\uceec\uc5d0 \uc800\uc7a5\uae4c\uc9c0 \uac78\ub9ac\ub294 \uc2dc\uac04: 0.2\ucd08 \\n\\n- \ucf54\ub4dc\uac00 \uac04\ub2e8\ud574\uc11c \uc720\uc9c0 \ubcf4\uc218\uc131\uc774 \uc88b\ub2e4. \\n- AWS Lambda \uac19\uc740 \uc11c\ubc84\ub9ac\uc2a4 \ucef4\ud4e8\ud305 \uc11c\ube44\uc2a4\ub098 FastAPI\uc640 \uac19\uc740 \uc6f9 \ud504\ub808\uc784\uc6cc\ud06c\ub85c \ucd94\uac00\uc801\uc778 API\ub97c \uad6c\ud604\ud574\uc57c \ud55c\ub2e4.\\n- Spring Boot\uc5d0\uc11c \ucd94\uac00\uc801\uc778 API \ud638\ucd9c\uc744 \ud574\uc57c\ud558\uace0, \ud655\uc7a5\uc131\uacfc \ube44\ub3d9\uae30 \ucc98\ub9ac \ub4f1 \uace0\ub824 \ud574\uc57c \ud560 \ubd80\ubd84\uc774 \ub9ce\ub2e4.\\n\\n### Java AWT \uc774\uc678\uc758 \ub77c\uc774\ube0c\ub7ec\ub9ac\\n\\nPython\uc774 \uc544\ub2cc Java\uc5d0\uc11c\uc758 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub3c4 \uace0\ub824\ub97c \ud574\ubd24\uc9c0\ub9cc \uc694\uad6c\uc0ac\ud56d\uc5d0 \uc801\ud569\ud558\uc9c0 \uc54a\uac70\ub098, \uc801\uc740 \uc694\uad6c\uc0ac\ud56d\uc5d0 \ube44\ud574 \ubb34\uac70\uc6b4 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub4e4\uc774 \ub9ce\uc544\uc11c \uc81c\uc678\ud588\ub2e4.\\n\\n\ub77c\uc774\ube0c\ub7ec\ub9ac | \uc124\uba85 | \uc81c\uc678 \uc774\uc720\\n-- | -- | --\\nSwing | AWT \uc774\ud6c4\uc5d0 \ub098\uc628 GUI \ub77c\uc774\ube0c\ub7ec\ub9ac, \ub124\uc774\ud2f0\ube0c UI\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\uace0 \ubaa8\ub4e0 \uc6b4\uc601\uccb4\uc81c \uc0c1\uc5d0\uc11c \ub3d9\uc77c\ud55c UI\ub97c \uac00\uc9c0\ub3c4\ub85d \ud568 | \uc694\uad6c\uc0ac\ud56d\uc5d0 \ube44\ud574 \ubb34\uac81\uace0 \ubcf5\uc7a1\ub3c4\uac00 \ub192\uc74c\\nJavaFX | Swing \uc774\ud6c4\uc5d0 \ub098\uc628 GUI \ub77c\uc774\ube0c\ub7ec\ub9ac, 3\ucc28\uc6d0 \uadf8\ub798\ud53d\uc744 \uc9c0\uc6d0\ud568 | \uc694\uad6c\uc0ac\ud56d\uc5d0 \ube44\ud574 \ubb34\uac81\uace0 \ubcf5\uc7a1\ub3c4\uac00 \ub192\uc74c\\n[simple-java-plot](https://github.com/yuriy-g/simple-java-plot) | AWT\ub85c \uad6c\ud604\ub41c \ud50c\ub85c\ud305 \ub77c\uc774\ube0c\ub7ec\ub9ac | AWT \uae30\ubc18\uc774\uae34 \ud558\uc9c0\ub9cc \uc9c1\uc811 AWT\ub97c \uc0ac\uc6a9\ud558\ub294 \uac83\uc5d0 \ube44\ud574 \uba54\ub9ac\ud2b8\uac00 \uc5c6\uc74c, \ucee4\uc2a4\ud140 \uc124\uc815 \uae30\ub2a5\uc774 \uc5c6\uc74c\\n[matplotlib4j](https://github.com/sh0nk/matplotlib4j) | Matplotlib\ub97c Java\uc5d0\uc11c \uc0ac\uc6a9\ud560 \uc218 \uc788\uac8c \ud558\ub294 \ub77c\uc774\ube0c\ub7ec\ub9ac | \ub0b4\ubd80\uc801\uc73c\ub85c \ud30c\uc774\uc36c \uc0ac\uc6a9\ud558\uae30\uc5d0 \ubb34\uac70\uc6c0, \ubc30\uacbd \ud22c\uba85\ud654 \uae30\ub2a5 \uc5c6\uc74c\\n\\n### Java & AWT(Abstract Window Toolkit)\\n\\n\uadf8\ub798\ud53d\uacfc \uc774\ubbf8\uc9c0\ub97c \uadf8\ub9ac\uae30 \uc704\ud55c \ub3c4\uad6c \\n\uc774\ubbf8\uc9c0 \uc0dd\uc131 \ubc0f \ub85c\uceec\uc5d0 \uc800\uc7a5\uae4c\uc9c0 \uac78\ub9ac\ub294 \uc2dc\uac04: 1.75\ucd08 \\n\\n- \ud50c\ub85c\ud305 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \uc0ac\uc6a9\ud558\ub294 \uac83\ubcf4\ub2e4 \uad6c\ud604\uc758 \ub09c\uc774\ub3c4\uac00 \ub2e4\uc18c \uc874\uc7ac\ud55c\ub2e4.\\n- \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc2dc\uac04\uc774 \ub2e4\uc18c \uc18c\uc694\ub418\uae30 \ub54c\ubb38\uc5d0 \ube60\ub978 \uc751\ub2f5 \ubc18\ud658\uc744 \uc704\ud574 \ube44\ub3d9\uae30 \ucc98\ub9ac\ub97c \uace0\ub824\ud560 \uc218 \uc788\uc744 \uac83 \uac19\ub2e4.\\n- \ucd94\uac00\uc801\uc778 api \ud638\ucd9c\uc744 \ud558\uc9c0 \uc54a\uc544\ub3c4 \ub41c\ub2e4.\\n\\n### \uae30\uc220 \uc120\ud0dd\\n\\nAWT\uc758 \uacbd\uc6b0 Matplotlib\uc5d0 \ube44\ud574 \uad6c\ud604\uc758 \ub09c\uc774\ub3c4\uac00 \ub2e4\uc18c \uc788\uace0, \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uc2dc\uac04\uc774 \ub354 \ub9ce\uc774 \uac78\ub9ac\ub294 \ub2e8\uc810\uc774 \uc788\ub2e4. \\n\ud558\uc9c0\ub9cc \ucd94\uac00\uc801\uc778 api \ud638\ucd9c\uc744 \ud558\uc9c0 \uc54a\uc544\ub3c4 \ub418\ub294 \ubd80\ubd84, Python\uc744 \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \ucd94\uac00\uc801\uc778 \uc6f9 \ud504\ub808\uc784\uc6cc\ud06c\uc758 \ud559\uc2b5 \ube44\uc6a9\uc744 \uace0\ub824\ud558\uc5ec AWT\ub97c \uc0ac\uc6a9\ud558\uae30\ub85c \uacb0\uc815\ud588\ub2e4.\\n\\n### \uc720\uc9c0 \ubcf4\uc218\\n\\nAWT\ub77c\ub294 \uc0dd\uc18c\ud55c \uae30\uc220\uc744 \uc0ac\uc6a9\ud558\uae30 \ub54c\ubb38\uc5d0 \uc720\uc9c0 \ubcf4\uc218\uc131\uc744 \uc704\ud574 \ud300\uc6d0\ub4e4\uacfc \uacf5\uc720\ud558\ub294 \uac83\uc774 \uc911\uc694\ud558\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\ub530\ub77c\uc11c \ub2e4\uc74c\uacfc \uac19\uc740 \ubc29\ubc95\uc73c\ub85c \uacf5\uc720\ud558\uae30\ub85c \ud588\ub2e4. \\n\\n1. \ucf54\ub4dc \ub9ac\ubdf0\uc640 PR\uc744 \ud1b5\ud574 \uc791\uc131\ud55c AWT \ucf54\ub4dc\uc5d0 \ub300\ud55c \uc124\uba85 \ubc0f \ub9ac\ubdf0 \ubc1b\ub294\ub2e4. \\n2. AWT\ub97c \uc0ac\uc6a9\ud55c \ubd80\ubd84\uc744 \ubb38\uc11c\ud654\ud558\uc5ec \uacf5\uc720\ud55c\ub2e4.\\n\\n### \ub808\ubca8 3\ub97c \ub9c8\ubb34\ub9ac\ud558\uba70 \ub0b4\uc6a9 \ucd94\uac00\\n\\n\uae30\uc220 \uc120\ud0dd\uc744 \ud558\uae30 \uc704\ud55c \uc2e4\ud589 \uc2dc\uac04 \uce21\uc815\uc5d0 \uc624\ub958\uac00 \uc788\uc5c8\ub2e4. \\nAWT\ub97c \uc0ac\uc6a9\ud558\ub294 \ubd80\ubd84\uc5d0\uc11c \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc2e4\ud589 \uc2dc\uac04\uc744 \uc81c\uc678\ud558\uba74 \ud30c\uc774\uc36c\uacfc \ube44\uc2b7\ud55c \uc2dc\uac04\uc548\uc5d0 \uc774\ubbf8\uc9c0\ub97c \uc0dd\uc131\ud560 \uc218 \uc788\uc5c8\ub2e4."},{"id":"java-spring-springboot","metadata":{"permalink":"/java-spring-springboot","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-07-24-\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1.mdx","source":"@site/blog/2023-3/2023-07-24-\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1.mdx","title":"\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1","description":"\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1","date":"2023-07-24T00:00:00.000Z","formattedDate":"2023\ub144 7\uc6d4 24\uc77c","tags":[{"label":"Java","permalink":"/tags/java"},{"label":"Spring Boot","permalink":"/tags/spring-boot"},{"label":"Spring","permalink":"/tags/spring"}],"readingTime":4.725,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1","slug":"java-spring-springboot","tags":["Java","Spring Boot","Spring"]},"prevItem":{"title":"\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd","permalink":"/route-image-intro"},"nextItem":{"title":"\uc6f9\uc18c\ucf13","permalink":"/websocket"}},"content":"## \uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1\\n\\n\ud300 \ud504\ub85c\uc81d\ud2b8\ub97c \uc9c4\ud589\ud558\uba74\uc11c \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1\uc744 \uc0ac\uc6a9\ud558\uac8c \ub418\uc5c8\ub2e4. \\n2.7 \ubc84\uc804\uc744 \uc0ac\uc6a9\ud560 \uc218\ub3c4 \uc788\uc5c8\uc9c0\ub9cc LTS \uae30\uac04\uacfc \ucde8\uc57d\uc810 \ud328\uce58\ub85c \uc778\ud55c \ubc84\uc804\uc5c5 \ub4f1\uc744 \uace0\ub824\ud588\uc744 \ub54c 3.1\uacfc \uc790\ubc14 17\uc744 \uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \ub354 \ud6a8\uc728\uc801\uc774\ub77c\uace0 \ud310\ub2e8\ud588\ub2e4.\\n\\n## \uc790\ubc14 \ubcc0\uacbd \uc0ac\ud56d\\n\\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 2\uae4c\uc9c0\ub294 \uc790\ubc14 11\uc744 \uc0ac\uc6a9\ud588\uc5c8\ub2e4. \\n\ub530\ub77c\uc11c \uc790\ubc14 11\ubd80\ud130 \uc790\ubc14 17\uae4c\uc9c0\uc758 \ubcc0\uacbd\uc0ac\ud56d\uc744 \uc815\uc2dd \ub9b4\ub9ac\uc988 \uae30\uc900\uc73c\ub85c \uc815\ub9ac\ud574\ubcf4\ub824\uace0 \ud55c\ub2e4.\\n\\n### Switch Expressions(Java 14)\\n\\nJava 14\uc5d0\uc11c\ub294 \uae30\uc874\uc758 Switch \ubb38\uc744 \uac04\uacb0\ud558\uac8c \uc791\uc131\ud560 \uc218 \uc788\ub294 Switch \uc2dd\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\n\\n```java\\nenum RESULT {\\n WIN, LOSE, DRAW\\n}\\n\\nRESULT result = RESULT.WIN;\\n\\nint prize = switch (result) {\\n case WIN -> 10_000_000;\\n case LOSE, DRAW -> 5_000_000;\\n\\tdefault -> 0;\\n};\\n```\\n\\n\uc8fc\uc694 \ud2b9\uc9d5\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4.\\n\\n- `->` \uc5f0\uc0b0\uc790\ub97c \uc774\uc6a9\ud558\uc5ec \uac01 case\uc5d0 \ub300\ud55c \uacb0\uacfc\ub97c \ubc14\ub85c \ubc18\ud658\ud560 \uc218 \uc788\ub2e4.\\n- case\ub97c \ucf64\ub9c8(`,`)\ub85c \uc5f0\uacb0\ud558\uc5ec \ud558\ub098\uc758 case\uc5d0 \uc5ec\ub7ec \uac12\uc744 \uc9c0\uc815\ud560 \uc218 \uc788\ub2e4.\\n- break \ubb38\uc774 \ud544\uc694 \uc5c6\ub2e4.\\n- default \ube14\ub85d\uc744 \ud1b5\ud574 \uae30\ubcf8 \uac12\uc744 \uc9c0\uc815\ud560 \uc218 \uc788\ub2e4.\\n\\n### Text Block(Java 15)\\n\\nJava 15\uc5d0\ub294 \uc0c8\ub85c\uc6b4 \ubb38\uc790\uc5f4 \ud45c\ud604\ubc29\uc2dd\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\n\uae34 \ubb38\uc790\uc5f4\uc744 + \uc5f0\uc0b0\uc790\uc758 \ub3c4\uc6c0 \uc5c6\uc774 \uac00\ub3c5\uc131\uc788\uac8c \uc791\uc131\ud560 \uc218 \uc788\ub2e4.\\n\\n```java\\n@Repository\\npublic interface PostRepository extends JpaRepository {\\n @Query(\\"\\"\\"\\n SELECT p FROM Post p\\n WHERE p.title LIKE %:keyword%\\n OR p.content LIKE %:keyword%\\n \\"\\"\\")\\n List findPostsByTitleOrContentContainingKeyword(String keyword);\\n}\\n```\\n\\n### NPE \uba54\uc2dc\uc9c0(Java 15)\\n\\n```java\\nString name = null;\\nname.chars();\\n\\n/** \\n# before\\njava.lang.NullPointerException\\n\\tat com.example.DiscountPolicyTest.test(NullPointerExceptionTest.java:61)\\n\\n# after\\nCannot invoke \\"String.chars()\\" because \\"name\\" is null\\njava.lang.NullPointerException: Cannot invoke \\"String.chars()\\" because \\"name\\" is null\\n*/\\n```\\n\\n### Record(Java 16)\\n\\nLombok\uc758 `@Data`, kotlin\uc758 data \ud074\ub798\uc2a4\uc640 \uc720\uc0ac\ud55c \uae30\ub2a5\uc744 \uc81c\uacf5\ud55c\ub2e4. \\nRecord\ub97c \uc120\uc5b8\ud558\ub294 \uacbd\uc6b0 \uc811\uadfc\uc790, \uc0dd\uc131\uc790, equals & hashcode, toString\uc774 \uc81c\uacf5\ub41c\ub2e4. \\n\ub370\uc774\ud130 \uc804\uc1a1 \uc6a9\ub3c4\ub85c \uc801\ud569\ud574 \ubcf4\uc778\ub2e4. \\n\\n```java\\npublic record PostDto(String title, String content) {\\n}\\n```\\n\\n### \ucd94\uac00\uc801\uc778 \ubcc0\uacbd\uc0ac\ud56d\\n\\n\uc774\uc678\uc5d0\ub3c4 stream\uc758 toList, \uc778\uc2a4\ud134\uc2a4\uc758 \ud0c0\uc785\uc744 \uac04\ud3b8\ud558\uac8c \uccb4\ud06c\ud558\ub294 Pattern Matching Instanceof, Sealed class \ub4f1\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\n\\n## \uc2a4\ud504\ub9c1, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 \ubcc0\uacbd \uc0ac\ud56d\\n\\n\uc2a4\ud504\ub9c1\uacfc \uc2a4\ud504\ub9c1 \ubd80\ud2b8\uc5d0\ub3c4 \ub9ce\uc740 \ubcc0\uacbd \uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4. \\n\ub530\ub77c\uc11c \ud544\uc694\ud574\ubcf4\uc774\ub294 \uba87\uac1c \uc815\ub3c4\ub9cc \uc815\ub9ac\ud588\ub2e4. \\n\\n### \uc2a4\ud504\ub9c1 \uc694\uad6c\uc0ac\ud56d\\n\\nJava 17, Jakarta EE 9 \uc774\uc0c1\uc774\uc5b4\uc57c \ud55c\ub2e4.\\n\\n### \ub124\uc784\uc2a4\ud398\uc774\uc2a4 \ubcc0\uacbd\\n\\nJakarta EE 9\uac00 \uc801\uc6a9\ub418\uba74\uc11c \ub124\uc784\uc2a4\ud398\uc774\uc2a4\ub3c4 \uc804\ubc18\uc801\uc73c\ub85c javax -> jakarta\ub85c \ubcc0\uacbd\ub418\uc5c8\ub2e4. \\n\\n### PathPatternParser - trailing slash \ud5c8\uc6a9\ud558\uc9c0 \uc54a\uc74c\\n\\n6.0 \uc774\uc804\uc758 \uacbd\uc6b0 \uae30\ubcf8 \uc124\uc815 \uae30\uc900\uc73c\ub85c `@GetMapping(\\"/hello\\")`\uc640 `@GetMapping(\\"/hello/\\")`\uac00 \ub3d9\uc77c\ud588\ub2e4. \\n6.0 \uc774\ud6c4\uc758 PathPatternParser\uac00 \uae30\ubcf8\uc73c\ub85c \uc0ac\uc6a9\ub418\uace0, `/hello`\uc640 `/hello/`\ub294 \uc11c\ub85c \ub2e4\ub978 URL\ub85c \ub9e4\uce6d\ub41c\ub2e4. \\n\\n> PathPatternParser used by default (with the ability to opt into PathMatcher). \\n\\n### HTTP interface client\\n\\n\uc790\ubc14 \uc778\ud130\ud398\uc774\uc2a4\uc640 \uc5b4\ub178\ud14c\uc774\uc158\uc744 \uc774\uc6a9\ud558\uc5ec HTTP \uc694\uccad\uc744 \uc704\ud55c \uc11c\ube44\uc2a4\ub97c \uc815\uc758\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\n\uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [\ud1a0\ube44\ub2d8\uc758 \uac15\uc758](https://www.youtube.com/watch?v=Kb37Q5GCyZs)\ub97c \ucc38\uace0\ud558\uba74 \uc88b\uc744 \uac83 \uac19\ub2e4.\\n\\n### \uc2a4\ud504\ub9c1 \ubd80\ud2b8 \ucd5c\uc18c \uc694\uad6c\uc0ac\ud56d\\n\\nGradle 7.3, Java 17, Kotlin 1.6, Jakarta EE 9, Spring Framework 6 \\n\uc774\uc678\uc5d0\ub3c4 \uc11c\ub4dc\ud30c\ud2f0\ub4e4\uc758 \ucd5c\uc2e0 \ub9b4\ub9ac\uc988 \ubc84\uc804\uc744 \uc0ac\uc6a9\ud568\uc73c\ub85c, \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 \ud574\ub2f9 \ubc84\uc804\uc5d0 \ub9de\ub294 \ub9b4\ub9ac\uc988 \ub178\ud2b8\ub97c \ucc38\uace0\ud560 \uc218 \uc788\uc744 \uac83 \uac19\ub2e4. \\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n[\uc5b4\ub290\xa0\uc6d4\uae09\uc7c1\uc774\uac1c\ubc1c\uc790\xa0\uc758 \uc2a4\ud504\ub9c1 \ubd80\ud2b8 \ub530\ub77c\uc7a1\uae30](https://www.youtube.com/watch?v=1WT6oxchM9M) \\n[\uc790\ubc14 9-16 \uc8fc\uc694 \ud2b9\uc9d5 \ubcf5\uc2b5\ud558\uae30](https://www.youtube.com/watch?v=7SlDdzVk6GE) \\n[Java EE\uc5d0\uc11c Jakarta EE\ub85c\uc758 \uc804\ud658](https://www.samsungsds.com/kr/insights/java_jakarta.html) \\n[Spring 6\uc758 \uc0c8\ub85c\uc6b4 HTTP Interface\uc640 3 \uac00\uc9c0 REST Clients \ub77c\uc774\ube0c \ucf54\ub529](https://www.youtube.com/watch?v=Kb37Q5GCyZs) \\n[What\'s New in Spring Framework 6.x](https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-Spring-Framework-6.x) \\n[Spring Boot 3.0 Release Notes](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes) \\n[Spring Boot 3.1 Release Notes](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.1-Release-Notes)"},{"id":"websocket","metadata":{"permalink":"/websocket","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-26-WebSocket.mdx","source":"@site/blog/2023-2/2023-06-26-WebSocket.mdx","title":"\uc6f9\uc18c\ucf13","description":"\uc6f9\uc18c\ucf13","date":"2023-06-26T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 26\uc77c","tags":[{"label":"WebSocket","permalink":"/tags/web-socket"}],"readingTime":4.165,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6f9\uc18c\ucf13","slug":"websocket","tags":["WebSocket"]},"prevItem":{"title":"\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1","permalink":"/java-spring-springboot"},"nextItem":{"title":"Docusaurus","permalink":"/docusaurus"}},"content":"### \uc6f9\uc18c\ucf13\\n\\n\ub2e8\uc77c TCP \uc5f0\uacb0\uc744 \ud1b5\ud574 \ud074\ub77c\uc774\uc5b8\ud2b8\uc640 \uc11c\ubc84 \uac04 \uc804\uc774\uc911 \uc591\ubc29\ud5a5 \ud1b5\uc2e0\uc744 \uc9c0\uc6d0\ud558\ub294 \ud504\ub85c\ud1a0\ucf5c \\n\uc6f9 \ud658\uacbd\uc5d0\uc11c \uc5f0\uc18d\ub41c \ub370\uc774\ud130\ub97c \uc2e4\uc2dc\uac04\uc73c\ub85c \ucc98\ub9ac\ud560 \uc218 \uc788\ub2e4. \\n\\n\uc6f9\uc18c\ucf13\uc740 HTTP\uc758 \ud3ec\ud2b8\ub97c \uadf8\ub300\ub85c \uc0ac\uc6a9\ud558\uace0 \uac01\uac01 \ud3ec\ud2b8 80\uacfc \ud3ec\ud2b8 443\uc744 \uc0ac\uc6a9\ud558\uc5ec HTTP(ws://) \ubc0f HTTPS(wss://)\ub85c \uc11c\ubc84\uc5d0 \uc5f0\uacb0\ud55c\ub2e4. \\n\\n### \uc6f9\uc18c\ucf13 \ub4f1\uc7a5 \ubc30\uacbd\\n\\n\uc6f9\uc18c\ucf13\uc774 \ub4f1\uc7a5\ud558\uae30 \uc774\uc804, \uc2e4\uc2dc\uac04\uc131\uc744 \ubcf4\uc7a5\ud558\uae30 \uc704\ud574 Polling, Long polling, Streaming \uac19\uc740 \uae30\uc220\uc744 \uc0ac\uc6a9\ud588\uc5b4\uc57c \ud588\ub2e4. \\n\uc774\ub294 \uc2e4\uc2dc\uac04\uc131\uc774\ub098 \uc591\ubc29\ud5a5\uc131\uc744 \ub9cc\uc871\uc2dc\ud0a4\uc9c0 \ubabb\ud588\uace0, HTTP\ub97c \uc774\uc6a9\ud558\uae30 \ub54c\ubb38\uc5d0 \uacfc\ub3c4\ud55c \uc624\ubc84\ud5e4\ub4dc\uac00 \ubc1c\uc0dd\ud588\ub2e4. \\n\\n:::note polling, long polling, streaming\\n\\nPolling: \uc8fc\uae30\uc801\uc73c\ub85c \uc11c\ubc84\uc5d0 \uc694\uccad\uc744 \ubcf4\ub0b4 \uc218\uc2e0\ud560 \uc815\ubcf4\uac00 \uc788\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ubc29\ubc95\\n- \uc11c\ubc84\uc5d0\uc11c \ubcf4\ub0bc \ub0b4\uc6a9\uc774 \uc5c6\uc5b4\ub3c4 \ud074\ub77c\uc774\uc5b8\ud2b8\ub294 \uc54c \uc218 \uc5c6\ub2e4. \\n- \uacc4\uc18d\ud574\uc11c \uc694\uccad\uc744 \ubcf4\ub0b4 \ud655\uc778\uc744 \ud574\uc57c\ud558\uae30 \ub54c\ubb38\uc5d0 \uc11c\ubc84\uc5d0 \ubd88\ud544\uc694\ud55c \ubd80\ud558\ub97c \uc8fc\uc5b4\uc57c \ud55c\ub2e4. \\n\\nLong Polling: \ud074\ub77c\uc774\uc5b8\ud2b8\uc758 \uc694\uccad\uc5d0 \ub300\ud574 \uc751\ub2f5\uc744 \ubcf4\ub0b4\uc9c0 \uc54a\uace0 \uc788\ub2e4\uac00 \uc774\ubca4\ud2b8\uac00 \ubc1c\uc0dd\ud588\uc744\ub54c \uc751\ub2f5\ud558\ub294 \ubc29\ubc95\\n- \ud3f4\ub9c1 \ubc29\uc2dd\ubcf4\ub2e4 \uc11c\ubc84\uc5d0 \uc801\uc740 \ubd80\ud558\ub97c \uc904 \uc218 \uc788\uc9c0\ub9cc, \uc694\uccad\uc758 \uc8fc\uae30\uac00 \uc9e7\uc73c\uba74 \ud3f4\ub9c1\uacfc \ucc28\uc774\uac00 \uc5c6\uc5b4\uc9c4\ub2e4.\\n\\nStreaming: \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 request\ub97c \ubcf4\ub0b4\uba74 \ucee4\ub125\uc158\uc744 \ub9fa\uace0, \uc774 \ucee4\ub125\uc158\uc744 \uc720\uc9c0\ud558\uba74\uc11c \uc11c\ubc84\uac00 \uacc4\uc18d \ub370\uc774\ud130\ub97c \ubcf4\ub0b4\ub294 \ubc29\ubc95\\n- \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc11c\ubc84\uc5d0 \uc694\uccad\uc744 \ud558\uace0 \uc2f6\ub2e4\uba74 \uc0c8\ub85c\uc6b4 \ucee4\ub125\uc158\uc744 \ub9fa\uc5b4\uc57c \ud55c\ub2e4. \\n:::\\n\\n### \uc6f9\uc18c\ucf13\uc758 \ub3d9\uc791\\n\\n```mermaid\\nsequenceDiagram\\n participant Client\\n participant Server\\n Client->>Server: Handshake - Upgrade\ub97c \uc774\uc6a9\ud55c WebSocket \uc804\ud658 \uc694\uccad\\n Server->>Client: Handshake - HttpStatus 101(Switching Protocols)\\n\\n Client->>Server: \uc591\ubc29\ud5a5 \ud1b5\uc2e0\\n Server->>Client: \\n\\n Client->>Server: \uc885\ub8cc\\n Server->>Client: \\n```\\n\\n### 1. Upgrade \uc694\uccad\\n\\nWebSocket \ud504\ub85c\ud1a0\ucf5c\ub85c \uc804\ud658\ud558\ub294 HTTP \uc694\uccad\uc744 \ubcf4\ub0b8\ub2e4. \\n\uc774\ub294 HTTP\uc640 \uac19\uc774 80, 443 \ud3ec\ud2b8\ub97c \uc0ac\uc6a9\ud55c\ub2e4. \\n\uc6f9\uc18c\ucf13\uc73c\ub85c \uc804\ud658\ud558\uae30 \uc704\ud574\uc11c\ub294 Upgrade: websocket, Connection: Upgrade \ud5e4\ub354\uac00 \ud544\uc694\ud558\ub2e4. \\nSec-WebSocket-Key\ub294 \uc11c\ubc84\uc5d0\uc11c Sec-WebSocket-Accept\ub97c \uacc4\uc0b0\ud558\uc5ec \uc751\ub2f5\ud558\uace0 \uc774 \uac12\uc774 \uc608\uc0c1\ud55c \uac12\uacfc \ub2e4\ub974\uba74 \uc5f0\uacb0\uc774 \uc218\ub9bd\ub418\uc9c0 \uc54a\ub294\ub2e4. \\nSec-WebSocket-Protocol\uc758 \uacbd\uc6b0 \uc11c\ube0c\ud504\ub85c\ud1a0\ucf5c\uc758 \ubaa9\ub85d\uc73c\ub85c \uc11c\ubc84 \uce21\uc5d0\uc11c\ub294 \ud574\ub2f9 \ubaa9\ub85d \uc911 \ud558\ub098\ub97c \uc120\ud0dd\ud558\uc5ec \ubc18\ud658\ud574\uc57c \ud55c\ub2e4. \\n\ub9cc\uc57d \uc11c\ubc84\uce21\uc5d0\uc11c \uc5ec\ub7ec \uac1c \uc9c0\uc6d0\uc774 \uac00\ub2a5\ud55c \uacbd\uc6b0 \uc9c0\uc6d0 \uac00\ub2a5\ud55c \ud504\ub85c\ud1a0\ucf5c \uc911 \uccab\ubc88\uc9f8 \ud504\ub85c\ud1a0\ucf5c\uc744 \ud074\ub77c\uc774\uc5b8\ud2b8\uce21\uc73c\ub85c \ubcf4\ub0b8\ub2e4. \\n\\n```\\nGET /chats HTTP/1.1\\nHost: localhost:8080\\nUpgrade: websocket\\nConnection: Upgrade\\nSec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==\\nSec-WebSocket-Protocol: v10.stomp, v11.stomp\\nSec-WebSocket-Version: 13\\nOrigin: http://localhost:8080\\n```\\n\\n### 2. Switching Protocols\\n\\n\uc11c\ubc84\ub294 101 Switching Protocols \uc751\ub2f5\uc744 \ubc18\ud658\ud55c\ub2e4. \\nSec-WebSocket-Accept\uc740 Sec-WebSocket-Key \ub4a4\uc5d0 `258EAFA5-E914-47DA-95CA-C5AB0DC85B11`\ub97c \ubd99\uc774\uace0 SHA1\ub85c \ud574\uc2f1 \ud6c4 Base64\ub85c \uc778\ucf54\ub529\ud558\uc5ec \ubc18\ud658\ud55c\ub2e4. \\n\uc774\ub294 \uc11c\ubc84 \uc6f9\uc18c\ucf13 \ud504\ub85c\ud1a0\ucf5c\uc758 \uc9c0\uc6d0 \uc5ec\ubd80\ub97c \ud074\ub77c\uc774\uc5b8\ud2b8\uc5d0\uac8c \uba85\ud655\ud788 \uc54c\ub9ac\uae30 \uc704\ud574 \uc874\uc7ac\ud55c\ub2e4. \\n\\n```\\nHTTP/1.1 101 Switching Protocols \\nUpgrade: websocket\\nConnection: Upgrade\\nSec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=\\nSec-WebSocket-Protocol: v10.stomp\\n```\\n\\n### 3. \ud1b5\uc2e0 \ud6c4 \uc885\ub8cc\\n\\n\uc5f0\uacb0\uc774 \uc218\ub9bd\ub418\uba74 \uc6f9\uc18c\ucf13 \ud504\ub808\uc784 \ub2e8\uc704\ub85c \uc591\ubc29\ud5a5 \ud1b5\uc2e0\uc744 \ud55c\ub2e4. \\n\uc5f0\uacb0 \uc885\ub8cc\ub97c \uc6d0\ud558\ub294 \uacbd\uc6b0 \ud074\ub77c\uc774\uc5b8\ud2b8, \uc11c\ubc84 \ubaa8\ub450 \uc5f0\uacb0 \uc885\ub8cc\ub97c \uc694\uccad\ud560 \uc218 \uc788\ub2e4. \\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\nhttps://datatracker.ietf.org/doc/html/rfc6455 \\nhttps://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications \\nhttps://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_servers \\nhttps://docs.spring.io/spring-framework/reference/web/websocket.html"},{"id":"docusaurus","metadata":{"permalink":"/docusaurus","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-18-Docusaurus/2023-06-18-Docusaurus.mdx","source":"@site/blog/2023-2/2023-06-18-Docusaurus/2023-06-18-Docusaurus.mdx","title":"Docusaurus","description":"\ud300 \ube14\ub85c\uadf8 \ub610\ub294 \ubb38\uc11c\ud654\ub97c \uc704\ud574 Docusaurus\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc744 \uc815\ub9ac\ud558\ub824\uace0 \ud55c\ub2e4.","date":"2023-06-18T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 18\uc77c","tags":[{"label":"Documentation","permalink":"/tags/documentation"}],"readingTime":10.095,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"Docusaurus","slug":"docusaurus","tags":["Documentation"]},"prevItem":{"title":"\uc6f9\uc18c\ucf13","permalink":"/websocket"},"nextItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 2 \ud68c\uace0","permalink":"/woowacourse-level2-retrospective"}},"content":"\ud300 \ube14\ub85c\uadf8 \ub610\ub294 \ubb38\uc11c\ud654\ub97c \uc704\ud574 Docusaurus\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc744 \uc815\ub9ac\ud558\ub824\uace0 \ud55c\ub2e4. \\n\\n## \uc124\uce58\\n\\n[\uacf5\uc2dd \ud648\ud398\uc774\uc9c0](https://docusaurus.io/docs/installation)\uc5d0 \ub4e4\uc5b4\uac00\uc11c \ucd5c\uc2e0 \ubc84\uc804\uc744 \uc124\uce58\ud55c\ub2e4. \\n\\n```bash\\nyarn create docusaurus\\n````\\n\\n## \ubc30\ud3ec\\n\\n[\ubc30\ud3ec \uc548\ub0b4 \ubb38\uc11c](https://docusaurus.io/docs/next/deployment#deploying-to-github-pages) \\nnetlify\ub098 vercel \uac19\uc740 \uc11c\ubc84\ub9ac\uc2a4 \ud50c\ub7ab\ud3fc\uc744 \ucd94\ucc9c\ud558\uace0 \uc788\uace0, \uac04\ub2e8\ud558\uace0, \ube60\ub978 \uc2dc\uac04 \uc548\uc5d0 \ubc30\ud3ec\ub97c \ud560 \uc218 \uc788\ub2e4. \\n\uc774 \uae00\uc5d0\uc11c\ub294 github pages\ub97c \uc774\uc6a9\ud574\uc11c \ubc30\ud3ec\ud558\ub294 \ubc29\ubc95\uc744 \uc124\uba85\ud55c\ub2e4.\\n\\n### \ub808\ud3ec\uc9c0\ud1a0\ub9ac \uc0dd\uc131\\n\\ngithub pages\ub97c \uc774\uc6a9\ud558\ub824\uba74 [\uc608\uc2dc](https://github.com/greeng00se/greeng00se.github.io)\uc640 \uac19\uc774 `username.github.io` \ud615\ud0dc\uc758 \ub808\ud3ec\uc9c0\ud1a0\ub9ac\ub97c \uc0dd\uc131\ud574\uc57c \ud55c\ub2e4. \\n\uc774\ub54c organization\uc744 \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 `organization.github.io` \ud615\ud0dc\uc758 \ub808\ud3ec\uc9c0\ud1a0\ub9ac\ub97c \uc0dd\uc131\ud574\uc11c \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n### \uc124\uc815 \ud30c\uc77c \uc218\uc815\\n\\n```js title=\\"docusaurus.config\\"\\nmodule.exports = {\\n // ...\\n url: \'https://greeng00se.github.io\',\\n baseUrl: \'/\',\\n projectName: \'greeng00se.github.io\',\\n organizationName: \'greeng00se\',\\n trailingSlash: false,\\n // ...\\n};\\n```\\n\\n### \ud1a0\ud070 \uc124\uc815\\n\\ngithub action\uc744 \uc704\ud574 \ubc30\ud3ec\uc6a9 \ud1a0\ud070\uc744 \ud558\ub098 \uc0dd\uc131\ud558\uc5ec \uc0dd\uc131\ud55c \ub808\ud3ec\uc9c0\ud1a0\ub9ac\uc5d0 Repository secrets\uc73c\ub85c \uc124\uc815\ud55c\ub2e4. \\n\uc774 \uae00\uc5d0\uc11c\ub294 \ud1a0\ud070\uc744 \ud074\ub798\uc2dd \ubc29\uc2dd\uc73c\ub85c \uc0dd\uc131\ud588\uace0 \uc2a4\ucf54\ud504\ub294 [repo, user, workflow] \uc744 \uc124\uc815\ud588\ub2e4. \\n\\n![github](./github.png)\\n\\n### \ube0c\ub79c\uce58 \uc0dd\uc131\\n\\ngithub\uc5d0\uc11c gh-pages \ube0c\ub79c\uce58\ub97c \ud558\ub098 \uc0dd\uc131\ud55c\ub2e4. \\nrepository -> settings -> pages -> branch\uc5d0\uc11c \uc0dd\uc131\ud55c gh-pages\ub85c \ube0c\ub79c\uce58\ub97c \ubcc0\uacbd\ud55c\ub2e4. \\n\uc124\uc815\ud55c \ube0c\ub79c\uce58\uac00 \ubc30\ud3ec \ube0c\ub79c\uce58\uac00 \ub418\uba70, \ud574\ub2f9 \ube0c\ub79c\uce58\uc5d0 \uc788\ub294 \ud30c\uc77c\ub4e4\uc744 \uc774\uc6a9\ud574\uc11c \uc815\uc801 \uc6f9\uc0ac\uc774\ud2b8\ub97c \uc81c\uacf5\ud55c\ub2e4. \\n\\n### \uc6cc\ud06c\ud50c\ub85c \uc791\uc131\\n\\nDocusaurus 2.0 \uae30\uc900 Node.js 16.14 \uc774\uc0c1\uc758 \ubc84\uc804\uc744 \uc0ac\uc6a9\ud574\uc57c \ud569\ub2c8\ub2e4. \\n\ubc30\ud3ec\uc2dc\uc5d0\ub294 Repository secrets\uc73c\ub85c \uc124\uc815\ud55c DEPLOY_TOKEN \uc744 \uc774\uc6a9\ud569\ub2c8\ub2e4. \\n\\n```yml title=\\".github/workflows/deploy.yml\\"\\nname: blog\\n\\non:\\n push:\\n branches: [main]\\n\\njobs:\\n deploy:\\n name: Deploy to GitHub Pages\\n runs-on: ubuntu-latest\\n steps:\\n - uses: actions/checkout@v2\\n - uses: actions/setup-node@v3\\n with:\\n node-version: 18\\n cache: yarn\\n\\n - name: Install dependencies\\n run: yarn install --frozen-lockfile\\n - name: Build website\\n run: yarn build\\n\\n - name: Deploy to GitHub Pages\\n uses: peaceiris/actions-gh-pages@v3\\n with:\\n github_token: ${{ secrets.DEPLOY_TOKEN }}\\n publish_dir: ./build\\n user_name: github-actions[bot]\\n user_email: 41898282+github-actions[bot]@users.noreply.github.com\\n```\\n\\n## \ub313\uae00 \uae30\ub2a5\\n\\ngiscus\ub97c \uc774\uc6a9\ud558\uc5ec \ub313\uae00 \uae30\ub2a5\uc744 \ucd94\uac00\ud55c\ub2e4. \\n\\n### giscus \uc124\uc815\\n\\n1. \uacf5\uac1c \uc800\uc7a5\uc18c\uc5ec\uc57c \ud55c\ub2e4.\\n2. giscus \uc571\uc774 \uc124\uce58\ub418\uc5b4 \uc788\uc5b4\uc57c \ud55c\ub2e4.\\n3. Discussions \uae30\ub2a5\uc774 \ud574\ub2f9 \uc800\uc7a5\uc18c\uc5d0\uc11c \ud65c\uc131\ud654\ub418\uc5b4 \uc788\uc5b4\uc57c \ud55c\ub2e4.\\n\\n\uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [giscus](https://giscus.app/ko)\ub97c \ud655\uc778\ud558\uc790.\\n\\n### docusaurus \uc124\uc815\\n\\n[swizzling](https://docusaurus.io/ko/docs/next/swizzling)\uc744 \uc774\uc6a9\ud558\uc5ec \ucef4\ud3ec\ub10c\ud2b8\ub97c \uac10\uc2fc\ub2e4. \\n\uae30\uc874\uc5d0 \uac8c\uc2dc\ubb3c\uc744 giscus\uac00 \ud3ec\ud568\ub41c \ub9ac\uc561\ud2b8 \ucef4\ud3ec\ub10c\ud2b8\ub85c \uac10\uc2f8\ub294 \ud615\ud0dc\uac00 \ub41c\ub2e4. \\n\uc544\ub798 \uba85\ub839\uc5b4\ub97c \uc774\uc6a9\ud558\uc5ec BlogPostItem\uc744 \ucd94\ucd9c\ud560 \uc218 \uc788\ub2e4. \\n\\n```bash\\nyarn run swizzle @docusaurus/theme-classic BlogPostItem -- --wrap\\n```\\n\\n\uba85\ub839\uc5b4\ub97c \uc785\ub825\ud558\uba74 `/src/theme/BlogPostItem/index.js` \uc704\uce58\uc5d0 \ud30c\uc77c\uc774 \uc0dd\uc131\ub41c\ub2e4. \\n\ud30c\uc77c\uc758 \ub0b4\uc6a9\uc744 \uc544\ub798\uc640 \uac19\uc774 \uc218\uc815\ud558\uace0, \uc774\ub54c setAttribute \ubd80\ubd84\uc740 \uc801\uc808\ud558\uac8c \uc790\uc2e0\uc758 giscus \uc124\uc815\uc744 \uc774\uc6a9\ud55c\ub2e4. \\n\\n```js title=\\"/src/theme/BlogPostItem/index.js\\"\\nimport OriginalBlogPostItem from \\"@theme-original/BlogPostItem\\";\\nimport React, { useEffect, useRef } from \\"react\\";\\n// @ts-expect-error internal code\\nimport { useColorMode } from \\"@docusaurus/theme-common\\";\\nimport { useBlogPost } from \\"@docusaurus/theme-common/internal\\";\\n\\nconst giscusSelector = \\"iframe.giscus-frame\\";\\n\\nfunction BlogPostItem(props) {\\n const { colorMode } = useColorMode();\\n const { isBlogPostPage } = useBlogPost();\\n const giscusTheme = colorMode === \\"dark\\" ? \\"dark\\" : \\"light\\";\\n const containerRef = useRef(null);\\n\\n useEffect(() => {\\n if (!isBlogPostPage) return;\\n\\n const giscusEl = containerRef.current.querySelector(giscusSelector);\\n\\n const createGiscusEl = () => {\\n const script = document.createElement(\\"script\\");\\n\\n script.src = \\"https://giscus.app/client.js\\";\\n script.setAttribute(\\"data-repo\\", \\"teco-chat/teco-chat.github.io\\");\\n script.setAttribute(\\"data-repo-id\\", \\"R_kgDOJZ5j0Q\\");\\n script.setAttribute(\\"data-category\\", \\"Announcements\\");\\n script.setAttribute(\\"data-category-id\\", \\"DIC_kwDOJZ5j0c4CXS_Q\\");\\n script.setAttribute(\\"data-mapping\\", \\"pathname\\");\\n script.setAttribute(\\"data-strict\\", \\"0\\");\\n script.setAttribute(\\"data-reactions-enabled\\", \\"1\\");\\n script.setAttribute(\\"data-emit-metadata\\", \\"0\\");\\n script.setAttribute(\\"data-input-position\\", \\"bottom\\");\\n script.setAttribute(\\"data-theme\\", giscusTheme);\\n script.setAttribute(\\"data-lang\\", \\"ko\\");\\n script.crossOrigin = \\"anonymous\\";\\n script.async = true;\\n \\n containerRef.current.appendChild(script);\\n };\\n\\n const postThemeMessage = () => {\\n const message = {\\n setConfig: {\\n theme: giscusTheme,\\n }\\n };\\n\\n giscusEl.contentWindow.postMessage({ giscus: message }, \\"https://giscus.app\\");\\n };\\n\\n giscusEl ? postThemeMessage() : createGiscusEl();\\n }, [giscusTheme]);\\n\\n return (\\n <>\\n \\n {isBlogPostPage &&
}\\n \\n );\\n}\\n\\nexport default BlogPostItem;\\n```\\n\\n## \uc54c\uace0\ub9ac\uc544 \uc124\uc815 \ubc0f \uc9c1\uc811 \uad00\ub9ac\ud558\uae30\\n\\n\uc54c\uace0\ub9ac\uc544\ub97c \uc0ac\uc6a9\ud558\uba74 \uac80\uc0c9 \uae30\ub2a5\uc744 \ucd94\uac00\ud560 \uc218 \uc788\ub2e4. \\n\uc720\ub8cc \ud50c\ub79c\uc774\ub098 netlify\ub97c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \ud06c\ub864\ub7ec\ub97c \ub530\ub85c \uc81c\uacf5\ud574 \uc8fc\ub294 \uac83 \uac19\ub2e4. \\n\\n\ubb34\ub8cc \ud50c\ub79c\uc740 \uc9c1\uc811 \uc778\ub371\uc2a4\ub97c \uc218\uc9d1\ud558\ub294 \ubc29\ubc95\uacfc, [docsearch](https://docsearch.algolia.com/)\ub97c \uc774\uc6a9\ud558\ub294 \ubc29\ubc95\uc774 \uc788\ub2e4. \\ndocsearch\uc5d0 \ub4f1\ub85d\ud55c\ub2e4\uba74 \uc77c\uc8fc\uc77c\uc5d0 \ud55c \ubc88\uc529 \ud06c\ub864\ub9c1\uc774 \uc9c4\ud589\ub41c\ub2e4. \\n\uc774 \uae00\uc5d0\uc11c\ub294 \uc9c1\uc811 \uc778\ub371\uc2a4\ub97c \uc218\uc9d1\ud558\ub294 \ubc29\ubc95\uc744 \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n- [\uc9c1\uc811 \uc778\ub371\uc2a4 \uc218\uc9d1](https://docsearch.algolia.com/docs/legacy/run-your-own/) \\n- [\uc124\uc815 \ud30c\uc77c](https://docsearch.algolia.com/docs/legacy/config-file)\\n\\n### \uc54c\uace0\ub9ac\uc544 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc0dd\uc131 \ubc0f \ud0a4 \ud655\uc778\\n\\n\ud68c\uc6d0\uac00\uc785\uc744 \ud558\uace0 \uc0c8\ub85c\uc6b4 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc0dd\uc131\uc744 \ub204\ub978\ub2e4. \\n\uc0dd\uc131\uc744 \ub2e4 \ub9c8\uce58\uba74 \ub2e4\uc74c\uacfc \uac19\uc774 api \ud0a4\ub97c \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n![algolia](./algolia.png)\\n\\n### \ud0a4 \uc0dd\uc131\\n\\n\uc9c1\uc811 \uc778\ub371\uc2a4\ub97c \uc218\uc9d1\ud558\uae30 \uc704\ud55c \ud0a4\ub97c \uc0dd\uc131\ud55c\ub2e4. \\naddObject, editSettings, deleteIndex acl(\uc811\uadfc \uc81c\uc5b4 \ubaa9\ub85d)\uc774 \uc788\uc73c\uba74 \ub41c\ub2e4. \\n\\n![key](./key.png)\\n\\n### .env \ud30c\uc77c \uc0dd\uc131\\n\\n\ud504\ub85c\uc81d\ud2b8 \ud3f4\ub354 \uc0c1\ub2e8\uc5d0 .env \ud30c\uc77c\uc744 \uc0dd\uc131\ud55c\ub2e4. \\n\\n```bash title=\\".env\\"\\nAPPLICATION_ID=MVIU5UEMOM\\nAPI_KEY=\uc778\ub371\uc2a4_\uc0dd\uc131\uc6a9_\ud0a4\\n```\\n\\n### config \ud30c\uc77c \uc0dd\uc131\\n\\n\ub9c8\ucc2c\uac00\uc9c0\ub85c \ucd5c\uc0c1\ub2e8\uc5d0 config.json \ud30c\uc77c\uc744 \uc0dd\uc131\ud55c\ub2e4.\\n\uc124\uc815 \ud30c\uc77c\uc740 \ud574\ub2f9 [\ub9c1\ud06c](https://docsearch.algolia.com/docs/legacy/config-file)\ub97c \ucc38\uace0\ud55c\ub2e4. \\n\ub610\ub294 Docusaurus\uc758 [\uc124\uc815 \ud30c\uc77c](https://github.com/algolia/docsearch-configs/blob/master/configs/docusaurus-2.json)\uc744 \ucc38\uace0\ud55c\ub2e4.\\n\\n```json title=\\"config.json\\"\\n{\\n \\"index_name\\": \\"teco\\",\\n \\"start_urls\\": [\\n \\"https://teco-chat.github.io/\\"\\n ],\\n \\"sitemap_urls\\": [\\n \\"https://teco-chat.github.io/sitemap.xml\\"\\n ],\\n \\"sitemap_alternate_links\\": true,\\n \\"stop_urls\\": [\\n \\"/tests\\"\\n ],\\n \\"selectors\\": {\\n \\"lvl0\\": {\\n \\"selector\\": \\"(//ul[contains(@class,\'menu__list\')]//a[contains(@class, \'menu__link menu__link--sublist menu__link--active\')]/text() | //nav[contains(@class, \'navbar\')]//a[contains(@class, \'navbar__link--active\')]/text())[last()]\\",\\n \\"type\\": \\"xpath\\",\\n \\"global\\": true,\\n \\"default_value\\": \\"Documentation\\"\\n },\\n \\"lvl1\\": \\"header h1\\",\\n \\"lvl2\\": \\"article h2\\",\\n \\"lvl3\\": \\"article h3\\",\\n \\"lvl4\\": \\"article h4\\",\\n \\"lvl5\\": \\"article h5, article td:first-child\\",\\n \\"lvl6\\": \\"article h6\\",\\n \\"text\\": \\"article p, article li, article td:last-child\\"\\n },\\n \\"strip_chars\\": \\" .,;:#\\",\\n \\"custom_settings\\": {\\n \\"separatorsToIndex\\": \\"_\\",\\n \\"attributesForFaceting\\": [\\n \\"language\\",\\n \\"version\\",\\n \\"type\\",\\n \\"docusaurus_tag\\"\\n ],\\n \\"attributesToRetrieve\\": [\\n \\"hierarchy\\",\\n \\"content\\",\\n \\"anchor\\",\\n \\"url\\",\\n \\"url_without_anchor\\",\\n \\"type\\"\\n ]\\n },\\n \\"conversation_id\\": [\\n \\"833762294\\"\\n ],\\n \\"nb_hits\\": 46250\\n}\\n```\\n\\n### docker \uc774\uc6a9\ud558\uc5ec \ud06c\ub864\ub9c1\\n\\ndocker\uc640 jq\uac00 \ud544\uc694\ud558\ub2e4. \\njq\uac00 \uc124\uce58\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc73c\uba74 mac \uae30\uc900 brew\ub97c \uc774\uc6a9\ud574\uc11c \uc124\uce58\ud560 \uc218 \uc788\ub2e4. \\n\\n```bash\\nbrew install jq\\n```\\n\\n\ub2e4\uc74c \uba85\ub839\uc5b4\ub97c \uc774\uc6a9\ud558\uc5ec .env\uc640 config.json\uc744 \uc774\uc6a9\ud558\uc5ec \ud06c\ub864\ub9c1\uc744 \ud55c\ub2e4. \\n\\n```bash\\ndocker run -it --env-file=.env -e \\"CONFIG=$(cat ./config.json | jq -r tostring)\\" algolia/docsearch-scraper\\n```\\n\\n### docusaurus \uc124\uc815\\n\\n\uc804\uc5d0 \ud655\uc778\ud55c APP ID, Search-Only API KEY, IndexName\uc744 \uc774\uc6a9\ud558\uc5ec docusaurus.config \ud30c\uc77c\uc5d0 \uc124\uc815\ud55c\ub2e4. \\n\\n```js title=\\"docusaurus.config\\"\\nthemeConfig:\\n /** @type {import(\'@docusaurus/preset-classic\').ThemeConfig} */\\n ({\\n ...\\n algolia: {\\n appId: \'MVIU5UEMOM\', // Application ID\\n apiKey: \'b68f378013817d9a190df88cdde226a0\', // Search-Only API Key\\n indexName: \'teco\', // config.json\uc5d0 \uc124\uc815\ud55c \uc778\ub371\uc2a4\uba85\\n contextualSearch: true,\\n },\\n })\\n```\\n\\n## \ubd80\uac00 \uc124\uc815\\n\\n### \ud654\uba74 \uc0c1\ub2e8 Github Icon\\n\\n\ud30c\uc77c \ucd5c\ud558\ub2e8\uc5d0 \uc544\ub798 css \uad6c\ubb38\uc744 \ucd94\uac00\ud55c\ub2e4.\\n\\n```css title=\\"/src/css/custom.css\\"\\n.header-github-link:hover {\\n opacity: 0.6;\\n}\\n\\n.header-github-link:before {\\n content: \'\';\\n width: 24px;\\n height: 24px;\\n display: flex;\\n background: url(\\"data:image/svg+xml,%3Csvg viewBox=\'0 0 24 24\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cpath d=\'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\'/%3E%3C/svg%3E\\")\\n no-repeat;\\n}\\n\\nhtml[data-theme=\'dark\'] .header-github-link:before {\\n background: url(\\"data:image/svg+xml,%3Csvg viewBox=\'0 0 24 24\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cpath fill=\'white\' d=\'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\'/%3E%3C/svg%3E\\")\\n no-repeat;\\n}\\n```\\n\\nthemeconfig -> navbar\uc5d0 github link\ub97c \uc124\uc815\ud55c\ub2e4. \\n\\n```js title=\\"docusaurus.config\\"\\nnavbar: {\\n title: \'HELLO\',\\n items: [\\n {\\n href: \'https://github.com/greeng00se\',\\n position: \'right\',\\n className: \'header-github-link\',\\n \'aria-label\': \'GitHub repository\',\\n },\\n ],\\n},\\n```\\n\\n### \ucf54\ub4dc\ube14\ub7ed\\n\\njava\ub098 kotlin\uc758 \uacbd\uc6b0 \uae30\ubcf8\uc801\uc73c\ub85c \ud558\uc774\ub77c\uc774\ud305\uc744 \uc9c0\uc6d0\ud574 \uc8fc\uc9c0 \uc54a\ub294\ub2e4. \\nprism \uc124\uc815\uc744 \uc544\ub798\uc640 \uac19\uc774 \ubcc0\uacbd\ud574 \uc900\ub2e4. \\n\\n```js title=\\"docusaurus.config\\"\\nprism: {\\n theme: lightCodeTheme,\\n darkTheme: darkCodeTheme,\\n additionalLanguages: [\'java\', \'kotlin\'],\\n}\\n```\\n\\n### mermaid\\n\\nmermaid\ub97c \uc0ac\uc6a9\ud558\ub824\uba74 `@docusaurus/theme-mermaid` \ub97c \uc124\uce58\ud574\uc57c \ud55c\ub2e4.\\n\\n```bash\\nyarn add @docusaurus/theme-mermaid\\n```\\n\\n\uc124\uce58 \ud6c4 \uc544\ub798\uc640 \uac19\uc774 \uc124\uc815\uc744 \ucd94\uac00\ud55c\ub2e4.\\n\\n```js title=\\"docusaurus.config\\"\\nconst config = {\\n ...\\n markdown: {\\n mermaid: true,\\n },\\n themes: [\\n \'@docusaurus/theme-mermaid\'\\n ],\\n};\\n```\\n\\nthemeConfig\uc5d0\uc11c mermaid\uc758 \ud14c\ub9c8\ub97c \uc9c0\uc815\ud560 \uc218 \uc788\ub2e4. \\n\\n```js title=\\"docusaurus.config\\"\\nthemeConfig:\\n /** @type {import(\'@docusaurus/preset-classic\').ThemeConfig} */\\n ({\\n ...\\n mermaid: {\\n theme: {\\n light: \'neutral\', \\n dark: \'dark\'\\n },\\n },\\n }),\\n```\\n\\n### \uad6d\uc81c\ud654 \uc124\uc815\\n\\n\uad6d\uc81c\ud654 \uc124\uc815\uc744 \ud55c\ub2e4\uba74 `Older Entries` \ud615\ud0dc\uc758 \uc124\uba85\uc774 `\ub2e4\uc74c \ud398\uc774\uc9c0` \ub85c \ubcc0\uacbd\ub41c\ub2e4. \\n\uc124\uc815\ud30c\uc77c\uc5d0\uc11c i18n\uc5d0 \uc788\ub294 \ub85c\ucf00\uc77c \uc124\uc815\uc744 ko\ub85c \ubcc0\uacbd\ud558\uba74 \ub41c\ub2e4. \\n\\n```js title=\\"docusaurus.config\\"\\ni18n: {\\n defaultLocale: \\"ko\\",\\n locales: [\\"ko\\"],\\n},\\n```\\n\\n### \ube14\ub85c\uadf8 \uae00 author\\n\\n\ud300\uc6d0 \ubcc4\ub85c \ubb38\uc11c\ub97c \uad00\ub9ac\ud55c\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc774 \uc5b4\ub5a4 \ud300\uc6d0\uc774 \uae00\uc744 \uc791\uc131\ud588\ub294\uc9c0 \uc124\uc815\ud574\uc57c \ud55c\ub2e4. \\n\\n![author](./author.png)\\n\\n`authors.yml` \ud30c\uc77c\uc744 \uc774\uc6a9\ud558\uc5ec \uc0ac\uc6a9\uc790\uc5d0 \ub300\ud55c \uae30\ubcf8 \uc124\uc815\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\\n```yml title=\\"/blog/authors.yml\\"\\nherb:\\n name: \ud5c8\ube0c\\n title: Backend\\n url: https://github.com/greeng00se\\n image_url: https://github.com/greeng00se.png\\n\\nmallang:\\n name: \ub9d0\ub791\\n title: Backend\\n url: https://github.com/shin-mallang\\n image_url: https://github.com/shin-mallang.png\\n```\\n\\n\ube14\ub85c\uadf8 \uae00\uc744 \uc791\uc131\ud560 \ub54c \ub2e4\uc74c\uacfc \uac19\uc774 authors\uc5d0 \ub123\uc5b4\uc8fc\uae30\ub9cc \ud558\uba74 \ub41c\ub2e4. \\n\\n```mdx\\n---\\nslug: 1\\ntitle: Hello World\\nauthors: [herb, mallang]\\ntags: [hello, docusaurus]\\n---\\n\\n\uccab \ubc88\uc9f8 \ubb38\uc11c \ub0b4\uc6a9\\n```"},{"id":"woowacourse-level2-retrospective","metadata":{"permalink":"/woowacourse-level2-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-11-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca82 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-06-11-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca82 \ud68c\uace0.mdx","title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 2 \ud68c\uace0","description":"23\ub144\uc758 6\uc6d4\uc774 \uc624\uace0, \ub808\ubca8 2\uac00 \ub05d\ub0ac\ub2e4.","date":"2023-06-11T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 11\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":2.545,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 2 \ud68c\uace0","slug":"woowacourse-level2-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"Docusaurus","permalink":"/docusaurus"},"nextItem":{"title":"\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0","permalink":"/level2-interview-retrospective"}},"content":"23\ub144\uc758 6\uc6d4\uc774 \uc624\uace0, \ub808\ubca8 2\uac00 \ub05d\ub0ac\ub2e4. \\n\ube60\ub974\uac8c \uc9c0\ub098\uac00\uc11c \uc870\uae08 \uc544\uc27d\ub2e4. \\n\\n### \ud559\uc2b5\\n\\n\ud68c\uace0\ub97c \uc791\uc131\ud558\uae30 \uc804\uc5d0 \ub808\ubca8 2 \ub3d9\uc548 \ubcf4\ub0c8\ub358 PR\uacfc \ud68c\uace0\ub97c \ucb49 \uc77d\uc5b4\ubd24\ub2e4. \\n\ud56d\uc0c1 \uc544\uc26c\uc6b4 \uacf3\uc740 \uc788\uae30 \ub9c8\ub828\uc774\uc9c0\ub9cc, \uc798 \ud559\uc2b5\ud55c \uac83 \uac19\ub2e4. \\n\ubbf8\uc158\uc744 \ud558\uba74\uc11c \uae30\uc220\uc744 \uc5b4\ub5bb\uac8c \uc120\ud0dd\ud558\uace0, \uc801\uc6a9\ud560 \uac83\uc778\uc9c0 \uace0\ubbfc\ud558\ub294 \uacfc\uc815\uc5d0\uc11c \uaf64\ub098 \ub9ce\uc740 \uc131\uc7a5\uc744 \ud55c \uac83 \uac19\ub2e4. \\n\\n\uace0\ubbfc\uc740 \uae4a\uc5c8\uc9c0\ub9cc \uc774\ub860\uc801\uc778 \ud559\uc2b5\uc774 \ubd80\uc871\ud55c \ub808\ubca8 2\uc600\ub2e4. \\n\ubc29\ud559 \uadf8\ub9ac\uace0 \ub808\ubca8 3 \ub54c\ub294 \uc870\uae08 \ub354 \uc774\ub860\uc801\uc778 \ubd80\ubd84\uc744 \ud559\uc2b5\ud558\ub294\ub370 \uc9d1\uc911\ud574\uc57c\uaca0\ub2e4. \\n\\n\uc810\ucc28 \ud559\uc2b5 \ubc94\uc704\uac00 \ub113\uc5b4\uc9c0\uba74\uc11c \uc790\uc5f0\uc2a4\ub7fd\uac8c \ubaa8\ub974\ub294 \ub0b4\uc6a9\uc774 \uc313\uc5ec\uac04\ub2e4. \\n\ud544\uc694\ud55c \ub0b4\uc6a9\uc740 \uc55e\uc73c\ub85c \ucc9c\ucc9c\ud788 \ud559\uc2b5\ud558\uba74 \ub418\ub2c8\uae4c \uc870\uae09\ud574\uc9c0\uc9c0 \ub9d0\uc544\uc57c\uaca0\ub2e4. \\n\\n### \uc218\uba74\\n\\n\ub808\ubca8 2\ub97c \uc9c4\ud589\ud558\ub294 \ub3d9\uc548 \uc218\uba74\uc774 \ub9ce\uc774 \ubd80\uc871\ud588\uc5c8\uace0, \uacb0\uacfc\uc801\uc73c\ub85c\ub294 \uadf8\ub0a0\uc758 \ucee8\ub514\uc158\uc744 \ub9ce\uc774 \uc88c\uc6b0\ud588\ub358 \uac83 \uac19\ub2e4. \\n\uc55e\uc73c\ub85c \uc218\uba74 \uc2dc\uac04\uc744 \ub298\ub9ac\uace0, \uc88b\uc740 \uc218\uba74 \uc2b5\uad00\uc744 \uac00\uc9c0\ub3c4\ub85d \ub178\ub825\ud574\uc57c\uaca0\ub2e4. \\n\\n### \ud611\uc5c5\\n\\n\ub808\ubca8 2 \ub9c8\uc9c0\ub9c9\uc5d0 \ud611\uc5c5 \ubbf8\uc158\uc774 \uc788\uc5c8\ub2e4. \\n\uc9c0\uae08\uae4c\uc9c0\ub294 \ubc31\uc5d4\ub4dc \ud06c\ub8e8\ub4e4\uacfc \ud398\uc5b4 \ud504\ub85c\uadf8\ub798\ubc0d\uc744 \ud558\uba74\uc11c \ud611\uc5c5\uc744 \uacbd\ud5d8\ud588\ub2e4. \\n\uc774\ubc88\uc5d0\ub294 \ud504\ub7f0\ud2b8\uc5d4\ub4dc \ud06c\ub8e8\uc640 \ud611\uc5c5\uc744 \ud588\ub2e4. \uc18c\ud1b5\uc740 \uc798 \ub41c \uac83 \uac19\uc9c0\ub9cc API \uba85\uc138\ub97c \uc815\ud558\ub294 \ubd80\ubd84\uc774 \uc544\uc9c1 \ubbf8\uc219\ud55c \uac83 \uac19\ub2e4. \\n\\n\ub808\ubca8 3 \ub54c\ubd80\ud130 \ubcf8\uaca9\uc801\uc73c\ub85c \ud504\ub85c\uc81d\ud2b8\uac00 \uc2dc\uc791\ub41c\ub2e4. \\n\ud300\uc744 \uc704\ud574 \uc5b4\ub5a4 \uac83\uc744 \ud560 \uc218 \uc788\uc744\uc9c0 \uace0\ubbfc\uc744 \ub9ce\uc774 \ud574\ubd10\uc57c\uaca0\ub2e4. \\n\\n### \ub808\ubca8 2\ub97c \ub9c8\ubb34\ub9ac\ud558\uba70\\n\\n\ud68c\uace0 \uc791\uc131\ud558\uba74\uc11c \ub808\ubca8 2\uc5d0\uc11c \ud588\ub358 \uac83\ub4e4\uc744 \ubc18\ucd94\ud574 \ubd24\ub294\ub370 \ubd80\uc871\ud55c \uc810\uc740 \ub9ce\uc558\uc5b4\ub3c4 \uc88b\uc740 \ubc29\ud5a5\uc73c\ub85c \uac00\uace0 \uc788\ub294 \uac83 \uac19\ub2e4.\\n\uc77d\uace0 \uc2f6\uc740 \ucc45\ub3c4 \uc77d\uace0, \ubd80\uc871\ud55c \ubd80\ubd84 \ucc44\uc6b0\uba74\uc11c \uc26c\uc5b4\uc57c\uaca0\ub2e4."},{"id":"level2-interview-retrospective","metadata":{"permalink":"/level2-interview-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-08-\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-06-08-\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0.mdx","title":"\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0","description":"\ub808\ubca8 \uc778\ud130\ubdf0","date":"2023-06-08T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 8\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":3.435,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0","slug":"level2-interview-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 2 \ud68c\uace0","permalink":"/woowacourse-level2-retrospective"},"nextItem":{"title":"\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0","permalink":"/order-retrospective"}},"content":"### \ub808\ubca8 \uc778\ud130\ubdf0\\n\\n\ub808\ubca8 1 \ub54c\ub294 \uc900\ube44\ud574\ub454 \ub0b4\uc6a9\uc73c\ub85c \uc778\ud130\ubdf0\ub97c \uc9c4\ud589\ud574\uc11c \uadf8\ub807\uac8c \ud2b9\ubcc4\ud55c \ubd80\ubd84\uc774 \uc5c6\uc5c8\ub2e4. \\n\ub530\ub77c\uc11c \ub808\ubca8 1 \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0\ub294 \ub808\ubca8 1 \ud68c\uace0\ub97c \uc791\uc131\ud560 \ub54c \ub07c\uc6cc\ub123\uc5c8\ub2e4. \\n\uc774\ubc88\uc5d0\ub294 \ubc94\uc704\ub3c4 \uc81c\ud55c\ub418\uc5b4 \uc788\uc5b4 \uc5b4\ub5bb\uac8c \uc900\ube44\ud574\uc57c \ud560\uc9c0 \ub2f9\ud669\ud588\uace0, \ub2f5\ubcc0\uc5d0\ub3c4 \ubd80\uc871\ud55c \ubd80\ubd84\uc774 \ub9ce\uc558\uc5c8\ub2e4. \\n\uae30\uc5b5\uc774 \uc0ac\ub77c\uc9c0\uae30 \uc804\uc5d0 \ud070 \ubb38\uc81c \uc5c6\uc774 \ub2f5\ubcc0\ud55c \ub0b4\uc6a9\uc744 \uc81c\uc678\ud558\uace0, \uae30\uc5b5 \ub0a8\ub294 \uac83 \uc704\uc8fc\ub85c \uc791\uc131\ud574 \ubcf4\ub824\uace0 \ud55c\ub2e4. \\n\\n### API \ubb38\uc11c \ub3c4\uad6c \uc120\ud0dd\\n\\n\ud070 \ubb38\uc81c \uc5c6\uc774 \ub2f5\ubcc0\uc744 \ud588\ub294\ub370 \uc55e\uc73c\ub85c\ub3c4 \ud300 \ud504\ub85c\uc81d\ud2b8\ub97c \ud558\uba74\uc11c \ub3c4\uc6c0 \ub420 \uac83 \uac19\uc740 \ub0b4\uc6a9\uc774 \uc788\uc5b4\uc11c \ub0a8\uaca8\ub450\ub824\uace0 \ud55c\ub2e4. \\n\ubc31\uc5d4\ub4dc \ud300\uc6d0\uc774 \ud568\uaed8 \uc758\uc0ac\uacb0\uc815\uc744 \ud588\uace0, \ubbf8\uc158 \uae30\uac04\uc774 \uc9e7\uc740 \ub9cc\ud07c \ud300 \ucc28\uc6d0\uc5d0\uc11c \ube44\uad50\uc801 \ud559\uc2b5\ud558\uae30 \uc26c\uc6b4 Swagger\ub97c \uc120\ud0dd\ud588\ub2e4. \\n\ucd94\uac00\ub85c \ub4e4\uc5b4\uac00\ub294 \uc2dc\uac04 \ub300\ube44 \ud558\uc774 \ub9ac\ud134\uc774\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4\uace0 \ub2f5\ubcc0\ud588\ub2e4.\\n\\n\ud300 \ucc28\uc6d0\uc758 \ud559\uc2b5 \ube44\uc6a9\uc744 \uc5b8\uae09\ud574\uc11c, \ub2e4\uc74c\uacfc \uac19\uc740 \uc88b\uc740 \ud53c\ub4dc\ubc31\uc744 \ubc1b\uc558\ub2e4.\\n\\n> \ud2b9\ud788 \ud300\uc73c\ub85c \uc758\uc0ac\uacb0\uc815\ud558\ub294 \uacfc\uc815\uc744 \uacf5\uc720\ud574 \uc900 \uc810\uc774 \uc88b\uc558\uace0 \uae30\uc220\uc801 \uc758\uc0ac\uacb0\uc815 \uacfc\uc815\uc5d0\uc11c \ud300\uc758 \ud559\uc2b5\ube44\uc6a9\uc744 \uace0\ub824\ud55c \uc810\uc774 \uc88b\uc558\uc74c. \\n> \uc55e\uc73c\ub85c\ub3c4 \ud559\uc2b5 \ube44\uc6a9\uc740 \uc8fc\uc694\ud558\uac8c \uace0\ub824\ud574\uc57c \ud560 \uc0ac\ud56d\\n>\\n\\n### PUT\uacfc PATCH & \ud1a0\ud070\uacfc \uc138\uc158\\n\\nPUT\uacfc PATCH \ucc28\uc774\ub97c \uc124\uba85\ud558\ub294 \ubd80\ubd84\uc5d0\uc11c\ub294 PATCH\ub97c \uc0ac\uc6a9\ud560 \ub54c \ud398\uc774\ub85c\ub4dc\uac00 \uc801\uc5b4\uc9c4\ub2e4\ub294 \ub0b4\uc6a9\uc744 \ube7c\uba39\uace0 \ub2f5\ubcc0\uc744 \ud588\ub2e4. \\n\ud1a0\ud070\uacfc \uc138\uc158\uc758 \uacbd\uc6b0 \uae30\uc220\uc744 \uc798 \ubaa8\ub974\ub294 \uc0ac\ub78c\uc5d0\uac8c \uc124\uba85\ud574\ub2ec\ub77c\ub294 \uc81c\uc57d\uc870\uac74\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\n\\n\ud574\ub2f9 \ub0b4\uc6a9\uc744 \ub2f5\ubcc0\ud558\uba74\uc11c \uae30\uc220\uc801\uc778 \uae4a\uc774\uac00 \ub9ce\uc774 \ubd80\uc871\ud588\ub2e4\ub294 \uc0dd\uac01\uc774 \ub4e0\ub2e4. \\n\uc2e4\uc81c\ub85c \ub808\ubca8 2 \ub54c \uc774\ub860\uc801\uc778 \ud559\uc2b5 \uc2dc\uac04\uc774 \ub9e4\uc6b0 \uc801\uc5c8\uace0, \uc9d1\uc911\ub825\ub3c4 \ub9ce\uc774 \ubd80\uc871\ud588\ub2e4. \\n\uc55e\uc73c\ub85c \uc5b4\ub5bb\uac8c \uae4a\uc774\ub97c \ucc44\uc6b8\uc9c0 \uace0\ubbfc\uc744 \ud560 \uc218 \uc788\ub294 \uc9c8\ubb38\ub4e4\uc774\uc5c8\ub2e4. \\n\\n\ucd94\uac00\ub85c \uae30\uc220\uc744 \uc798 \ubaa8\ub974\ub294 \uc0ac\ub78c\uc5d0\uac8c \uc124\uba85\ud558\ub294 \uac00\uc815\uc744 \ub450\uace0 \ud559\uc2b5\uc744 \ud55c\ub2e4\uba74 \ud070 \ub3c4\uc6c0\uc774 \ub420 \uac70\ub77c\ub294 \ud53c\ub4dc\ubc31\uc744 \ubc1b\uc558\ub2e4. \\n\\n### \uadf8 \uc678 \uac1c\uc120\ud560 \uc810\\n\\n\uc778\ud130\ubdf0\ud560 \ub54c \ud2b9\uc720\uc758 \ub9d0\ubc84\ub987\uc744 \uac1c\uc120\ud558\uae30 \\n\uc0dd\uac01\ud560 \uc2dc\uac04\uc744 \uac00\uc84c\uc744 \ub54c \\"\ub2e4\uc2dc \ub9d0\uc500\ub4dc\ub824\ub3c4 \ub420\uae4c\uc694?\\"\ub77c\uace0 \ub9d0\ud558\uace0 \ub2f5\ubcc0\uc744 \uc774\uc5b4\ub098\uac00\uae30 \\n\uae30\uc220\uc801\uc73c\ub85c \uae4a\uc774\uac00 \ubd80\uc871\ud558\ub2e4\uace0 \uc0dd\uac01\uc774 \ub9ce\uc774 \ub4e4\uc5b4\uc11c \uc870\uae08 \ub354 \uae4a\uac8c \uacf5\ubd80\ud558\uace0 \uc815\ub9ac\ud558\uae30 \\n\uc774\uc804\uc5d0 \uacf5\ubd80\ud588\ub358\uac70 \ub418\ub3cc\uc544 \ubcf4\ub294 \uc2dc\uac04 \uac00\uc9c0\uae30"},{"id":"order-retrospective","metadata":{"permalink":"/order-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-04-\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-06-04-\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0","description":"\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158","date":"2023-06-04T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 4\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":4.595,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0","slug":"order-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0","permalink":"/level2-interview-retrospective"},"nextItem":{"title":"[\ud14c\ucf54\ucc57] 3. \uae30\ub2a5 \uad6c\ud604","permalink":"/tecochat-retrospective-3"}},"content":"### \uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158\\n\\n\ubc30\ud3ec \ubc0f \ud611\uc5c5\uc744 \ud560 \uc218 \uc788\ub294 \ubbf8\uc158\uc774\uc5c8\ub2e4. \\n\ub9c8\ucf54, \uc6b0\uac00, \uc6b0\ucf54, \uc6b0\uc2a4 \uadf8\ub9ac\uace0 \ub098\uae4c\uc9c0 \ud569\uccd0\uc11c 5\uba85\uc774 \ud55c \ud300\uc774 \ub418\uc5c8\ub2e4. \\n\\n### \ubc30\ud3ec\\n\\n\uc774\uc804 \ubbf8\uc158\ub4e4\uacfc \ub2ec\ub9ac AWS\ub97c \uc774\uc6a9\ud574 \ubc30\ud3ec\ub97c \ud574\uc57c \ud588\ub2e4. \\n\uac01\uc790 \ud558\ub098\uc758 EC2 \uc778\uc2a4\ud134\uc2a4\ub97c \uc81c\uacf5\ubc1b\uc744 \uc218 \uc788\uc5c8\uace0, \ud300 \ubcc4\ub85c DB\ub97c \uc704\ud55c \ucd94\uac00 \uc778\uc2a4\ud134\uc2a4\ub97c \uc81c\uacf5\ubc1b\uc558\ub2e4. \\n\ubc30\ud3ec \uc2a4\ud06c\ub9bd\ud2b8\ub97c \uc791\uc131\ud558\ub294 \uacbd\ud5d8\uc744 \ud574\ubcfc \uc218 \uc788\uc5c8\ub2e4. \\n\ubc30\ud3ec \uc2a4\ud06c\ub9bd\ud2b8\uc5d0 \uc2dc\uac04\uc744 \ub9ce\uc774 \ud22c\uc790\ud558\uc9c4 \uc54a\uc558\uace0, \ub2e4\uc74c\uacfc \uac19\uc774 \uac04\ub2e8\ud558\uac8c \uc791\uc131\ud588\ub2e4.\\n\\n```bash\\necho \\"Start Deploy Script\\"\\nREPOSITORY_NAME=/home/ubuntu/jwp-shopping-order\\nPROJECT_NAME=jwp-shopping-order\\n\\necho \\"Change Directory\\"\\ncd $REPOSITORY_NAME\\n\\necho \\"Git Pull\\"\\ngit pull origin step2\\n\\necho \\"Build\\"\\n./gradlew bootJar\\n\\necho \\"Copy, Start Server\\"\\nmv ./build/libs/$PROJECT_NAME.jar .\\n\\nPID=$(pgrep -f $PROJECT_NAME)\\n\\nif [ -n $PID ]; then\\n kill -9 $PID\\n\\tsleep 5\\nfi\\n\\nnohup java -Dspring.profiles.active=prod -jar $PROJECT_NAME.jar 1>stdout.txt 2>err.txt &\\n```\\n\\n### \ud611\uc5c5\\n\\n\uc77c\ub2e8 \uc6b0\uc2a4\ub791 \uc6b0\ucf54\uac00 \uba3c\uc800 \uc7a0\uc2e4\ub85c \uc640\uc918\uc11c \ub108\ubb34 \uac10\uc0ac\ud588\ub2e4. \\n\ubc31\uc5d4\ub4dc\uac00 \uc544\ub2cc \ub2e4\ub978 \ud06c\ub8e8\ub4e4\uacfc \ud574\ubcf4\ub294 \uccab \ud611\uc5c5\uc774\ub77c \uc57d\uac04 \ub450\uadfc\uac70\ub838\ub2e4. \\n\uc608\uc0c1\uc678\ub85c \ub300\ud654\uac00 \uc798 \ub418\uc5b4\uc11c, \ube60\ub974\uac8c \uba85\uc138\ub97c \uc815\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n**\uc5ec\ub7ec\uac00\uc9c0 \ubc29\ubc95\uc5d0 \ub300\ud55c \uc7a5\ub2e8\uc810\uc744 \uace0\ub824\ud574\ubcf4\uae30**\\n\\n\ubc31\uc5d4\ub4dc\uc640 \ud14c\uc774\ube14 \uba85\uc138\ub098 \ucfe0\ud3f0 \uad6c\ud604\uc5d0 \ub300\ud574\uc11c \uc774\uc57c\uae30\ud560 \ub54c \uc7a5\ub2e8\uc5d0 \ub300\ud574 \ub9ce\uc774 \uace0\ub824\ud558\uc9c0 \ubabb\ud55c \uac83 \uac19\ub2e4. \\n\uc870\uae08 \ub354 \uc2dc\uac04\uc744 \ub9ce\uc774 \ub4e4\uc5ec\uc11c \uc7a5\ub2e8\uc810\uc744 \uace0\ub824\ud588\ub2e4\uba74 \ub354 \uc88b\uc740 \uacb0\uacfc\ubb3c\uc774 \ub098\uc624\uc9c0 \uc54a\uc558\uc744\uae4c? \\n\uc55e\uc73c\ub85c \uc120\ud0dd\uc758 \uc21c\uac04\uc5d0\uc11c \uc870\uae08 \ub354 \uc2dc\uac04\uc744 \ub4e4\uc5ec\ubcf4\ub294 \uac83\ub3c4 \uc88b\uc744 \uac83 \uac19\ub2e4. \\n\\n### \uc0c8\ub85c \ubc30\uc6b4 \ubd80\ubd84\\n\\n**expose headers**\\n\\n\uc6f9 \ud398\uc774\uc9c0\uc5d0\uc11c Location \ud5e4\ub354\ub97c \ubc1b\uc744 \uc218 \uc5c6\ub294 \ubb38\uc81c\uac00 \uc788\uc5c8\ub2e4. \\n\uae30\ubcf8\uc801\uc73c\ub85c [\ud5c8\uc6a9 \ubaa9\ub85d\uc5d0 \uc874\uc7ac\ud558\ub294 \uc751\ub2f5\ud5e4\ub354](https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_response_header)\ub9cc \ubc18\ud658\ud55c\ub2e4\ub294 \uac83\uc744 \ubaa8\ub974\uace0 \uc788\uc5c8\ub2e4. \\n\uc774\ub97c expose headers \uc124\uc815\uc744 \ud1b5\ud574 \ud574\uacb0\ud560 \uc218 \uc788\uc5c8\ub2e4. \\nnginx \uc124\uc815\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc774 \ucd94\uac00\ud574 \uc8fc\uc5c8\ub2e4. \\n\\n```bash\\nadd_header \'Access-Control-Expose-Headers\' \'Location\'\\n```\\n\\n**\uc77d\uae30 \uc804\uc6a9 \ud2b8\ub79c\uc7ad\uc158** \\n\\n\ub2e8\uc21c \uc870\ud68c \uc694\uccad\uc5d0 \ub300\ud55c \uc131\ub2a5\uc744 \ud5a5\uc0c1\uc2dc\ucf1c\uc900\ub2e4\ub294 \uac83\uc774\ub77c\uace0 \uac04\ub2e8\ud788\ub9cc \uc54c\uace0 \uc788\uc5c8\ub2e4. \\n\uc774\ubc88\uc5d0 \ucf54\uba58\ud2b8\uac00 \ub2ec\ub824\uc11c \uc870\uae08 \ub354 \uc790\uc138\ud788 \uacf5\ubd80\ud574 \ubcf4\uae30\ub85c \ud588\ub2e4. \\nTransactional(readOnly = true)\ub97c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \ub2e4\uc74c\uacfc \uac19\uc774 \ub3d9\uc791\ud55c\ub2e4.\\n\\nsetReadOnly(true) \uc124\uc815\uc774 \ub41c Connection\uc73c\ub85c \uc5f0\uacb0\uc744 \uc2dc\ub3c4\ub97c \ud55c\ub2e4. \uc774 \uc124\uc815\uc744 \ud558\ub294 \uacbd\uc6b0 DB\ub9c8\ub2e4 \ub2e4\ub974\uac8c \ub3d9\uc791\ud55c\ub2e4.\\n- h2\uc758 Connection \uad6c\ud604\uccb4\ub294 readOnly \uc124\uc815\uc744 \ubb34\uc2dc\ud558\ub294 \ubc29\ud5a5\uc73c\ub85c \uad6c\ud604\ub418\uc5b4 Transactional \uc801\uc6a9\ub418\uc9c0 \uc54a\ub294\ub2e4. \\n- MySQL 8.0(InnoDB \uc0ac\uc6a9 \uc2dc)\uc758 \uacbd\uc6b0 \uc77d\uae30 \uc804\uc6a9\uc73c\ub85c \uc54c\ub824\uc9c4 \ud2b8\ub79c\uc7ad\uc158\uc758 \uacbd\uc6b0 \ud2b8\ub79c\uc7ad\uc158 ID\ub97c \uc124\uc815\ud558\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0 \uc870\ud68c \uc18d\ub3c4\uac00 \ub354 \ube68\ub77c\uc9c4\ub2e4.\\n\\nORM \ud504\ub808\uc784\uc6cc\ud06c\ub97c \uc0ac\uc6a9\ud55c\ub2e4\uba74 prepareTransactionalConnection\ub97c \ud638\ucd9c\ud55c\ub2e4\uace0 \ud55c\ub2e4. \\n\ucd94\uac00\ub85c \ud604\uc5c5\uc5d0\uc11c\ub294 \uace0\uac00\uc6a9\uc131 \ub0b4\uacb0\ud568\uc131 \ub4f1\uc744 \uc704\ud558\uc5ec \ud074\ub7ec\uc2a4\ud130\ub97c \uad6c\uc131\ud558\uc5ec \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0\uac00 \ub9ce\uace0, \uc774 \uacbd\uc6b0 readOnly \uc124\uc815\uc774 \ub418\uc5b4\uc788\ub2e4\uba74 \uc77d\uae30 \uc804\uc6a9 DB\ub85c \uc9c8\uc758\uac00 \ub4e4\uc5b4\uac00\uc11c \ubd80\ud558 \ubd84\uc0b0\uc758 \ud6a8\uacfc\uac00 \uc788\ub2e4\uace0 \ud55c\ub2e4. \\n\\n**DAO\uc5d0 `@Transactional` \uc801\uc6a9** \\n\\nDAO\uc5d0 \ud2b8\ub79c\uc7ad\uc158\uc744 \ubcf4\uc7a5\ud574 \ubcf4\ub294 \uac74 \uc5b4\ub5bb\uaca0\ub0d0\uace0 \ub9ac\ubdf0\uac00 \ub2ec\ub824\uc11c \uace0\ubbfc\uc744 \ub9ce\uc774 \ud588\ub2e4. \\nService \uacc4\uce35\uc5d0 \uc774\ubbf8 \ud2b8\ub79c\uc7ad\uc158\uc744 \ubcf4\uc7a5\ud574 \uc8fc\uace0 \uc788\uae30\uc5d0 \ud544\uc694 \uc5c6\uc9c0 \uc54a\uc744\uae4c \uc0dd\uac01\ud588\uc5c8\ub2e4. \\nDAO\ub97c \ub2e4\ub978 \uacf3\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub354\ub77c\ub3c4 \ud2b8\ub79c\uc7ad\uc158\uc744 \ubcf4\uc7a5\ud558\uae30 \uc704\ud574(\ud655\uc7a5\uc131 \uace0\ub824) `@Transactional`\uc744 \uc801\uc6a9\ud558\ub294 \uac83\ub3c4 \uad1c\ucc2e\uc740 \uac83 \uac19\ub2e4."},{"id":"tecochat-retrospective-3","metadata":{"permalink":"/tecochat-retrospective-3","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604.mdx","source":"@site/blog/2023-2/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604.mdx","title":"[\ud14c\ucf54\ucc57] 3. \uae30\ub2a5 \uad6c\ud604","description":"\uac1c\uc694","date":"2023-06-01T00:00:00.000Z","formattedDate":"2023\ub144 6\uc6d4 1\uc77c","tags":[{"label":"TecoChat","permalink":"/tags/teco-chat"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":4.005,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ud14c\ucf54\ucc57] 3. \uae30\ub2a5 \uad6c\ud604","slug":"tecochat-retrospective-3","tags":["TecoChat","Retrospective"]},"prevItem":{"title":"\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0","permalink":"/order-retrospective"},"nextItem":{"title":"\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30","permalink":"/composite"}},"content":"### \uac1c\uc694\\n\\n\uc6d0\ub798 \ubaa9\uc801\uc778 `\ud06c\ub8e8\ub4e4\uc758 \ud559\uc2b5\uc5d0 \ub3c4\uc6c0`\uc744 \uc8fc\uae30 \uc704\ud574 \uc5b4\ub5a4 \uae30\ub2a5\uc744 \ucd94\uac00\ud574\uc57c \ud560\uc9c0 \uace0\ubbfc\uc744 \ub9ce\uc774 \ud588\ub2e4. \\n\ub808\ubca8 2\uac00 \uac70\uc758 \ub05d\ub098\uac00\ub294 \uc2dc\uc810, \uadf8\ub3d9\uc548 \ud588\ub358 \uac83\uc744 \uc815\ub9ac\ud574 \ubcf4\ub824\uace0 \ud55c\ub2e4. \\n\\n### \ub098\uc758 \ucc44\ud305 \ud655\uc778\ud558\uace0 \uc774\uc5b4\ud558\ub294 \uae30\ub2a5\\n\\nGPT\uc5d0\ub3c4 \uc788\ub294 \uae30\ub2a5\uc778\ub370, \ub0b4\uac00 \uc774\uc804\uc5d0 \ud588\ub358 \ucc44\ud305\uc744 \uc774\uc5b4\ud560 \uc218 \uc788\ub294 \uae30\ub2a5\uc744 \ucd94\uac00\ud588\ub2e4. \\n\uc608\uc804\uc5d0 \uc5b4\ub5a4 \uc9c8\ubb38\uc744 \ub0a8\uacbc\ub294\uc9c0, \ub610\ud55c \ud574\ub2f9 \ucc44\ud305\uc744 \uc774\uc5b4\uc11c \ud560 \uc218 \uc788\ub2e4. \\n\\n![chat1](./chat1.png)\\n\\n### \uc88b\uc544\uc694\uc640 \ub313\uae00 \uae30\ub2a5\\n\\n\ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc774 \uc9c8\ubb38\ud55c \ub0b4\uc6a9\uc5d0 \ubc18\uc751\ud560 \uc218 \uc788\ub294 \ubb34\uc5b8\uac00\uac00 \uc788\uc5c8\uc73c\uba74 \uc88b\uaca0\ub2e4\ub294 \uc758\uacac\ub4e4\uc774 \ub9ce\uc558\ub2e4. \\n\ub204\uac00 \uc88b\uc544\uc694\ub97c \ub20c\ub800\ub294\uc9c0, \uc5b4\ub5a4 \ucc44\ud305\uc774 \uc88b\uc544\uc694\ub97c \uac00\uc7a5 \ub9ce\uc774 \ubc1b\uc558\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub294 \uae30\ub2a5\uc744 \ucd94\uac00\ud588\ub2e4. \\n\ub610\ud55c \ub313\uae00 \ucd94\uac00 \ubc0f \uc0ad\uc81c \uae30\ub2a5\ub3c4 \ucd94\uac00\ud588\ub2e4.\\n\\n### \ud0a4\uc6cc\ub4dc \ucd94\ucd9c\\n\\n\uc5b4\ub5bb\uac8c \ud0a4\uc6cc\ub4dc \ucd94\ucd9c\uc744 \ud560\uc9c0 \uace0\ubbfc\uc744 \ub9ce\uc774 \ud588\ub294\ub370, \uc77c\ub2e8 GPT\ub97c \uc774\uc6a9\ud574\uc11c \ud0a4\uc6cc\ub4dc\ub97c \ucd94\ucd9c\ud558\uae30\ub85c \ud588\ub2e4. \\n\ud574\ub2f9 \ubd80\ubd84\uc740 \uccab \uc9c8\ubb38\uc5d0 \ub300\ud55c \ud0a4\uc6cc\ub4dc\ub9cc \ucd94\ucd9c\ud558\ub3c4\ub85d \ud588\ub2e4. \\n\ubc31\uc5d4\ub4dc\uc5d0\uc120 \ub9d0\ub791\uc774 \uc774\ubca4\ud2b8 \uc774\uc6a9\ud574\uc11c \uccab \ucc44\ud305 \uc694\uccad\uc774 \uc774\ub8e8\uc5b4\uc9c0\uba74, \ube44\ub3d9\uae30\ub85c \ud0a4\uc6cc\ub4dc\ub97c \ucd94\ucd9c\ud558\ub294 \uc9c8\ubb38\uc744 \ucd94\uac00\ub85c \ub0a0\ub9ac\ub3c4\ub85d \uad6c\ud604\ud558\uc600\ub2e4. \\nCSV \ud615\uc2dd\uc73c\ub85c GPT\uc5d0\uac8c \ub2f5\ubcc0\uc744 \uc785\ub825\ud574\ub2ec\ub77c\uace0 \uc694\uccad\ubc1b\ub294\ub370, \uc774 \ubd80\ubd84\uc774 \ubb38\uc81c(\ud504\ub86c\ud504\ud2b8 \uc5d4\uc9c0\ub2c8\uc5b4\ub9c1 \ubd80\ubd84\uc774 \ubc18\ud658\ub41c\ub2e4.)\uac00 \uc880 \uc788\ub294 \uac83 \uac19\uc544\uc11c \uac1c\uc120\uc774 \ud544\uc694\ud55c \uac83 \uac19\ub2e4. \\n\\n![chat2](./chat2.png)\\n\\n### \ub2e4\ub978 \ud06c\ub8e8\uc758 \ucc44\ud305 \ubcf5\uc0ac\ud574\uc11c \uc774\uc5b4\ud558\ub294 \uae30\ub2a5\\n\\n\ub2e4\ub978 \ud06c\ub8e8\ub4e4\uc758 \ucc44\ud305\uc744 \uc77d\ub2e4\uac00 \uad81\uae08\ud55c \uc810\uc774 \uc788\ub2e4\uba74 \ubcf5\uc0ac\ud574\uc11c \ubc14\ub85c \uc9c8\ubb38\uc744 \ud560 \uc218 \uc788\ub294 \uae30\ub2a5\uc744 \ucd94\uac00\ud588\ub2e4. \\n\ucc44\ud305\uc774 \ubcf5\uc0ac\ub41c \ud6c4 \ubc14\ub85c GPT\uc640 \ub300\ud654\ub97c \ud560 \uc218 \uc788\ub294 \uba54\uc778 \ud654\uba74\uc73c\ub85c \uc774\ub3d9\ud55c\ub2e4. \\n\\n### \uc0ac\uc6a9\uc131 \uace0\ub824\ud558\uae30\\n\\n![chat3](./chat3.png)\\n\\n\uc704 \ud654\uba74\uc740 \ud68c\uc6d0\uac00\uc785 \ucc3d\uc774\ub2e4. \\n\uc0ac\uc2e4 \uac00\uc7a5 \ub9c8\uc74c\uc5d0 \ub4dc\ub294 \ubd80\ubd84\uc774\uace0, \ud68c\uc6d0\uac00\uc785(\ub2c9\ub124\uc784\ub9cc \uc785\ub825\ud558\uc9c0\ub9cc)\ud560 \ub54c \uc775\uba85\uc744 \uc6d0\ud558\ub294 \uc0ac\ub78c\ub4e4\uc758 \uace0\ubbfc\uc744 \ub3c4\uc640\uc8fc\uac8c \ub054 \uc74c\uc2dd, \uacfc\uc77c, \uacfc\uc790 \ub4f1\uc758 \uc694\uc18c\ub4e4\uc744 \uc785\ub825\ud558\ub3c4\ub85d \uc720\ub3c4\ud588\ub2e4!\\n\ucd94\uac00\ub85c GPT\uc758 \ub2f5\ubcc0\uc774 \uc624\uba74 \uc790\ub3d9\uc73c\ub85c \ud654\uba74\uc744 \uc2a4\ud06c\ub864 \ud574\uc8fc\ub294 \uac83\uacfc \uac19\uc774 \uc0ac\uc6a9\uc131\uc744 \uac1c\uc120\ud574 \ubcf4\ub824\uace0 \ub178\ub825\ud588\uc9c0\ub9cc \uc27d\uc9c0 \uc54a\uc558\ub2e4. \\n\uc81c\uc77c \ud558\uace0 \uc2f6\uc740 \uac83\uc740 \uc2e4\uc81c GPT\ub97c \uc0ac\uc6a9\ud558\ub294 \uac83\ucc98\ub7fc stream/text \uac12\uc744 \ucc98\ub9ac\ud558\uace0 \uc2f6\uc740\ub370 \uc774 \ubd80\ubd84\uc740 \ubc29\ud559 \ub54c \uae30\ud68c\uac00 \ub418\uba74 \ub3c4\uc804\ud574 \ubd10\uc57c\uaca0\ub2e4. \\n\\n### \ud5a5\ud6c4 \uacc4\ud68d\\n\\n\uc2e4\uc81c \ud06c\ub8e8\ub4e4\uc774 \uc0ac\uc6a9\ud574 \uc8fc\ub294 \uc11c\ube44\uc2a4\ub97c \uc9c1\uc811 \ub9cc\ub4e4\uc5b4\ubcf4\uba74\uc11c \uc0ac\uc6a9\uc790\uc758 \uc785\uc7a5\uc5d0\uc11c \uace0\ubbfc\ub3c4 \ud558\uac8c \ub418\ub294 \uac83 \uac19\ub2e4. \\n\ud06c\ub8e8\ub4e4\uc774 \uc9c1\uc811 \uc0ac\uc6a9\ud574 \uc8fc\ub2c8\uae4c \ub108\ubb34 \uace0\ub9d9\uace0, \ud55c\ud3b8\uc73c\ub85c\ub294 \uc2e0\uae30\ud558\ub2e4. \\n\uc77c\ub2e8 \ubc29\ud559 \ub54c stream/text \uad00\ub828\ub41c \ubd80\ubd84 \ub3d9\uc791\ub418\ub3c4\ub85d \uad6c\ud604\ud574\ubcf4\ub824\uace0 \ud558\uace0, \uadf8 \uc678\uc758 \ubd80\ubd84\uc740 \uc870\uae08 \ub354 \uace0\ubbfc\ud574\uc57c\ub420 \uac83 \uac19\ub2e4."},{"id":"composite","metadata":{"permalink":"/composite","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30.mdx","source":"@site/blog/2023-2/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30.mdx","title":"\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30","description":"\uc694\uad6c\uc0ac\ud56d","date":"2023-05-26T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 26\uc77c","tags":[{"label":"Pattern","permalink":"/tags/pattern"},{"label":"Composite","permalink":"/tags/composite"}],"readingTime":4.74,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30","slug":"composite","tags":["Pattern","Composite"]},"prevItem":{"title":"[\ud14c\ucf54\ucc57] 3. \uae30\ub2a5 \uad6c\ud604","permalink":"/tecochat-retrospective-3"},"nextItem":{"title":"\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0","permalink":"/subway-retrospective"}},"content":"### \uc694\uad6c\uc0ac\ud56d\\n\\n\uc9c0\ud558\ucca0 \ubbf8\uc158\uc5d0\ub294 \ub2e4\uc74c\uacfc \uac19\uc740 \uc694\uad6c\uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4.\\n\\n- \uac70\ub9ac\ubcc4 \ucd94\uac00 \uc694\uae08 \uc815\ucc45\\n- \ub178\uc120\ubcc4 \ucd94\uac00 \uc694\uae08 \uc815\ucc45\\n- \uc5f0\ub839\ubcc4 \uc694\uae08 \ud560\uc778 \uc815\ucc45\\n\\n### \uc778\ud130\ud398\uc774\uc2a4 \uc0ac\uc6a9\\n\\n\uc694\uae08 \uc815\ucc45\uc740 \ub2e4\uc74c\uacfc \uac19\uc774 \uc778\ud130\ud398\uc774\uc2a4\ub85c \ud45c\ud604\ud560 \uc218 \uc788\ub2e4. \\n\uc694\uae08\uc744 \uacc4\uc0b0\ud558\ub294 \uba54\uc11c\ub4dc\ub294 \ucd5c\ub2e8 \uacbd\ub85c \uacc4\uc0b0\uc758 \uacb0\uacfc, \uc0ac\uc6a9\uc790\uc758 \uc815\ubcf4, \uc694\uae08\uc744 \ubc1b\uc544 \uc694\uae08\uc744 \uacc4\uc0b0\ud55c\ub2e4.\\n\\n```java\\npublic interface FarePolicy {\\n int calculate(Path path, Passenger passenger, int fare);\\n}\\n\\npublic class BaseFarePolicy implements FarePolicy { ... }\\npublic class DistanceFarePolicy implements FarePolicy { ... }\\npublic class AgeDiscountFarePolicy implements FarePolicy { ... }\\n```\\n\\n![composite1](./composite1.png)\\n\\n### \ubaa8\ub4e0 \uc694\uae08 \uc815\ucc45\uc744 \ud3ec\ud568\ud558\ub294 \uc0c8\ub85c\uc6b4 \uc694\uae08 \uc815\ucc45 \ub9cc\ub4e4\uae30\\n\\n\ub098\uba38\uc9c0 \uad6c\ud604\uccb4\ub97c \ubaa8\ub450 \uac00\uc9c0\uace0 \uc788\ub294 \ud558\ub098\uc758 \uad6c\ud604\uccb4\ub97c \ub9cc\ub4e4\uc5c8\ub2e4. \\n\uc774 \ub610\ud55c FarePolicy\ub97c \uad6c\ud604\ud55c \ud615\ud0dc\uac00 \ub418\uace0, \ud544\ub4dc\ub85c\ub294 \ub098\uba38\uc9c0 \uad6c\ud604\uccb4\ub4e4\uc744 \uac00\uc9c0\uace0 \uc788\ub2e4.\\n\\n```java\\npublic class SubwayFarePolicy implements FarePolicy {\\n\\n private final List farePolicies;\\n\\n public SubwayFarePolicy(final List farePolicies) {\\n this.farePolicies = farePolicies;\\n }\\n\\n @Override\\n public int calculate(final Path path, final Passenger passenger, final int fare) {\\n int calculatedFare = fare;\\n for (FarePolicy farePolicy : farePolicies) {\\n calculatedFare = farePolicy.calculate(path, passenger, calculatedFare);\\n }\\n return calculatedFare;\\n }\\n}\\n```\\n\\n\ub530\ub77c\uc11c \uadf8\ub9bc\uc73c\ub85c \ubcf8\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uad6c\uc870\uac00 \ub41c\ub2e4.\\n\\n![composite2](./composite2.png)\\n\\n### \uc815\ucc45\uc758 \uc21c\uc11c\\n\\n\uc9c0\ud558\ucca0 \uc694\uad6c\uc0ac\ud56d\uc740 \uc21c\uc11c\uac00 \uc911\uc694\ud588\ub2e4. \\n\uae08\uc561\uc758 \ucd1d\ud569\uc744 \uad6c\ud558\uace0, \uadf8 \ud6c4\uc5d0 \ud560\uc778 \uc815\ucc45\uc774 \ub4e4\uc5b4\uac00\uc57c\ud588\ub2e4. \\n\ub530\ub77c\uc11c \uc790\uc2dd\ub4e4\uc758 \uc21c\uc11c\ub97c \uad00\ub9ac\ud560 \ub54c \uc8fc\uc758\ub97c \uae30\uc6b8\uc5ec\uc57c \ud588\ub2e4. \\nConfiguration \ud074\ub798\uc2a4\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc774 \uc21c\uc11c\ub97c \uc9c1\uc811 \uc801\uc6a9\uc2dc\ucf30\ub2e4. \\n\\n```java\\n@Configuration\\npublic class FareConfiguration {\\n\\n @Bean\\n public FarePolicy farePolicy() {\\n return new SubwayFarePolicy(List.of(\\n new BaseFarePolicy(),\\n new DistanceFarePolicy(),\\n new AgeDiscountFarePolicy()\\n ));\\n }\\n}\\n```\\n\\n### \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc774\ub780?\\n\\n![composite3](./composite3.png)\\n\\nGOF\uc758 \ub514\uc790\uc778 \ud328\ud134 \ucc45\uc5d0\uc11c\ub294 \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc744 \ub2e4\uc74c\uacfc \uac19\uc774 \uc124\uba85\ud558\uace0 \uc788\ub2e4.\\n\\n> \ubd80\ubd84\uacfc \uc804\uccb4\uc758 \uacc4\uce35\uc744 \ud45c\ud604\ud558\uae30 \uc704\ud574 \uac1d\uccb4\ub4e4\uc744 \ubaa8\uc544 \ud2b8\ub9ac \uad6c\uc870\ub85c \uad6c\uc131\ud569\ub2c8\ub2e4. \\n\uc0ac\uc6a9\uc790\ub85c \ud558\uc5ec\uae08 \uac1c\ubcc4 \uac1d\uccb4\uc640 \ubcf5\ud569 \uac1d\uccb4\ub97c \ubaa8\ub450 \ub3d9\uc77c\ud558\uac8c \ub2e4\ub8f0 \uc218 \uc788\ub3c4\ub85d \ud558\ub294 \ud328\ud134\uc785\ub2c8\ub2e4.\\n> \\n\\n\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc740 \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud55c \uac1c\ubcc4 \uac1d\uccb4\uac00 \uc874\uc7ac\ud558\uace0, \uadf8 \uac1c\ubcc4 \uac1d\uccb4\ub4e4\uc744 \ud3ec\ud568\ud558\ub294 \ud558\ub098\uc758 \uad6c\ud604\uccb4\uac00 \ub530\ub85c \uc874\uc7ac\ud558\ub294 \ud328\ud134\uc774\ub2e4. \\n\uc774 \ub54c \uc0ac\uc6a9\uc790\ub294 \uac1c\ubcc4 \uac1d\uccb4\uc640 \ud569\uc131 \uac1d\uccb4(\uac1c\ubcc4 \uac1d\uccb4\ub4e4\uc744 \ud3ec\ud568\ud558\uace0 \uc788\ub294)\ub97c \ub611\uac19\uc774 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n### \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc758 \uad6c\uc131\uc694\uc18c\\n\\nComponent\\n\\n- \uc9d1\ud569 \uad00\uacc4\uc5d0 \uc815\uc758\ub420 \ubaa8\ub4e0 \uac1d\uccb4\uc5d0 \ub300\ud55c \uc778\ud130\ud398\uc774\uc2a4 \\n- ex) \uc694\uae08 \uc815\ucc45(FarePolicy) \\n\\nLeaf\\n\\n- \uac1c\ubcc4 \uac1d\uccb4, \uac1d\uccb4 \ud569\uc131\uc5d0 \uae30\ubcf8\uc774 \ub418\ub294 \uac1d\uccb4\uc758 \ud589\ub3d9 \\n- ex) \uac70\ub9ac \ubcc4 \uc694\uae08 \uc815\ucc45(DistanceFarePolicy) \\n\\nComposite\\n\\n- \uc5ec\ub7ec \uac1c\uc758 \uac1c\ubc1c \uac1d\uccb4\ub97c \ud3ec\ud568\ud558\ub294 \ud569\uc131 \uac1d\uccb4 \\n- ex) \uc9c0\ud558\ucca0 \uc694\uae08 \uc815\ucc45(SubwayFarePolicy) \\n\\nClient\\n\\n- \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\ub294 \ud074\ub77c\uc774\uc5b8\ud2b8\\n\\n### \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc758 \uc0ac\uc6a9\uacfc \uc8fc\uc694 \ubaa9\ud45c\\n\\n\ubd80\ubd84 - \uc804\uccb4\uc758 \uad00\uacc4\ub97c \ud45c\ud604\ud558\uace0 \uc2f6\uc744 \ub54c \\nClient \uae30\uc900\uc73c\ub85c Composite\uc640 Leaf\uc758 \ucc28\uc774\ub97c \uc54c\uc9c0 \ubabb\ud574\ub3c4 \uc798 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub3c4\ub85d \ud574\uc57c\ub420 \ub54c\\n\\n### \ud328\ud134 \uc0ac\uc6a9\uc2dc \uc8fc\uc758\ud574\uc57c\ud560 \ubd80\ubd84\\n\\n\ud328\ud134\uc740 \uacf5\ud1b5\uc73c\ub85c \uc0ac\uc6a9 \uac00\ub2a5\ud55c \uc5ed\ud560, \ucc45\uc784, \ud611\ub825\uc758 \ud15c\ud50c\ub9bf\uc774\ub2e4. \\n\ubc18\ubcf5\ub418\ub294 \ubb38\uc81c\ub97c \ud6a8\uc728\uc801\uc73c\ub85c \ud574\uacb0\ud560 \uc218 \uc788\uc9c0\ub9cc \ud328\ud134\uc5d0 \ub9e4\ubab0\ub418\uc11c\ub294 \uc548\ub41c\ub2e4. \\n\ud328\ud134\uc744 \ub9f9\ubaa9\uc801\uc73c\ub85c \uc0ac\uc6a9\ud574\uc11c\ub294 \uc548\ub418\uace0, \ud604\uc7ac\uc758 \uc694\uad6c\uc0ac\ud56d\uc5d0 \ub530\ub77c \ud328\ud134\uc744 \uc720\ub3d9\uc801\uc73c\ub85c \uc218\uc815\ud574\uac00\uba74\uc11c \uc801\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\ub2e4. \\n\ud56d\uc0c1 \ud2b8\ub808\uc774\ub4dc\uc624\ud504\ub97c \uc0dd\uac01\ud558\uc790!\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134, GoF\uc758 \ub514\uc790\uc778 \ud328\ud134 \\n\ub514\uc790\uc778 \ud328\ud134\uacfc \ud504\ub808\uc784\uc6cc\ud06c, \uc624\ube0c\uc81d\ud2b8"},{"id":"subway-retrospective","metadata":{"permalink":"/subway-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-25-\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-05-25-\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0","description":"\uc9c0\ud558\ucca0 \ubbf8\uc158","date":"2023-05-25T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 25\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":7.91,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0","slug":"subway-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30","permalink":"/composite"},"nextItem":{"title":"\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5","permalink":"/accidental-duplication"}},"content":"### \uc9c0\ud558\ucca0 \ubbf8\uc158\\n\\n\uc810\uc810 \uc77c\uc815\uc774 \ub9ce\uc544\uc9c0\ub294 \ub290\ub08c\uc774 \ub4e4\uba74\uc11c \ud68c\uace0\uac00 \ub2a6\uc5b4\uc9c4\ub2e4. \\n\uc9c0\ud558\ucca0 \ubbf8\uc158\uc740 \ubc00\ub9ac\ub791 \ud398\uc5b4\ub97c \uc9c4\ud589\ud588\ub2e4. \\n\uac04\ub2e8\ud55c CRUD\ub9cc \uc788\ub358 \uc774\uc804 \ubbf8\uc158\ub4e4\uacfc \ub2ec\ub9ac, \uc870\uae08 \ubcf5\uc7a1\ud55c \ub3c4\uba54\uc778 \uc694\uad6c\uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4. \\n\uc774\ub54c API, \ud14c\uc774\ube14, \ub3c4\uba54\uc778 \uc124\uacc4\ub97c \ud574\uc57c \ud588\ub294\ub370 \uc5b4\ub5a4 \uac83\ubd80\ud130 \ud574\uc57c \ud560\uc9c0 \uace0\ubbfc\uc744 \ub9ce\uc774 \ud588\ub2e4. \\nAPI\uc640 \ud14c\uc774\ube14 \uad6c\uc870\ub97c \uc6b0\ub9ac\uac00 \uc815\ud560 \uc218 \uc788\ub294 \uc0c1\ud669\uc774\uc5c8\uace0, \ub3c4\uba54\uc778 \ub85c\uc9c1\uc774 \ubcf5\uc7a1\ud588\uae30 \ub54c\ubb38\uc5d0 \ub3c4\uba54\uc778\uc744 \uba3c\uc800 \uad6c\ud604\ud588\ub2e4.\\n\\n**\ub178\uc120\uc758 \uad6c\uac04 \ucd94\uac00 \ubc0f \uc0ad\uc81c**\\n\\n\ub178\uc120\uc744 \uc800\uc7a5\ud558\ub294 \ubc29\ubc95\uc5d0 \ub300\ud574\uc11c \ubc00\ub9ac\uc640 \uc774\uc57c\uae30\ub97c \ub098\ub234\ub2e4.\\n\\n1. \uad6c\uac04\uc744 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \uc804\ubd80 \uc81c\uac70\ud558\uace0 \uc804\ubd80 \ucd94\uac00\ud558\ub294 \ubc29\ubc95\\n2. \ubcc0\uacbd\ub41c \uc694\uc18c\ub9cc \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \ubc18\uc601\ud558\ub294 \ubc29\ubc95\\n\\n\ud398\uc5b4 \uc2dc\uac04\uc774 \uc9e7\uc544\uc11c \ub354\uc6b1 \uac04\ub2e8\ud55c 1\ubc88\uc744 \uc120\ud0dd\ud588\uace0, \uc2dc\uac04 \ub0b4 \uc694\uad6c\uc0ac\ud56d\uc744 \ub9cc\uc871\uc2dc\ud0a4\uae30 \uc704\ud574 \ub354 \uac04\ub2e8\ud558\uac8c \uad6c\ud604\ud558\ub294 \ubc29\ubc95\uc744 \uc120\ud0dd\ud558\ub294 \uac83\ub3c4 \uc88b\uc740 \ud2b8\ub808\uc774\ub4dc\uc624\ud504\uc600\ub358 \uac83 \uac19\ub2e4. \\n\ucd94\ud6c4 \ud398\uc5b4\uac00 \ub05d\ub098\uace0 \ub9ac\ubdf0\uc5b4\uc778 \uc11c\ube0c\uc6e8\uc774\uac00 \uc77c\ubd80\ubd84\ub9cc \ubc18\uc601\ud558\ub294 \uac83\uc73c\ub85c \uac1c\uc120\ud574 \ubcf4\ub294 \uac83\ub3c4 \uc88b\uc744 \uac83 \uac19\ub2e4\uace0 \ucf54\uba58\ud2b8\ub97c \ub0a8\uaca8\uc8fc\uc154\uc11c \ucd94\uac00 \ubc0f \uc81c\uac70\ub41c \uc694\uc18c\ub9cc \ubc18\uc601\ud558\ub3c4\ub85d \ubcc0\uacbd\ud588\ub2e4.\\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n\ubbf8\uc158\uc758 \ub09c\uc774\ub3c4\uac00 \uc62c\ub77c\uac04 \ub9cc\ud07c, \ud398\uc5b4 \ud560 \ub550 \ucee8\ub514\uc158 \uad00\ub9ac\ub3c4 \uc798\ud558\ub824\uace0 \ub178\ub825\ud558\uace0 \ubbf8\uc158 \ud560 \ub54c\ub3c4 \uc9d1\uc911\ud574\uc11c \uc798 \ub05d\ub0b8 \uac83 \uac19\ub2e4. \\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\ub97c \uc9c4\ud589\ud558\uba74\uc11c \uc54c\uc544\uc57c \ud558\ub294 \uac8c \ub9ce\uc544\uc9c0\uba74\uc11c \uac00\ub054 \uc870\ubc14\uc2ec\uc744 \uac00\uc9c8 \ub54c\uac00 \uc788\ub294 \uac83 \uac19\uc740\ub370, \uc870\ubc14\uc2ec\uc744 \uacbd\uacc4\ud560 \ud544\uc694\uac00 \uc788\uc744 \uac83 \uac19\ub2e4. \\n\ubd80\uc871\ud55c \ubd80\ubd84\uc740 \uc778\uc815\ud558\uace0, \uc55e\uc73c\ub85c \ub098\uc544\uac00\uc57c\uaca0\ub2e4. \\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654**\\n\\n\uc694\uae08 \uc815\ucc45\uc740 \uae30\ubcf8\uc694\uae08 \uc815\ucc45, \uac70\ub9ac\ubcc4 \uc694\uae08 \uc815\ucc45, \uc5f0\ub839\ubcc4 \ud560\uc778 \uc815\ucc45\uc774 \uc788\uc5c8\ub2e4. \\n\uc694\uae08\uc744 \ub354\ud558\ub294 \ubd80\ubd84\uacfc, \ud560\uc778\ud558\ub294 \ubd80\ubd84\uc774 \uc788\uc5b4\uc11c \uc774 \ub458\uc744 \ubd84\ub9ac\ud560\uae4c \uc0dd\uac01\ud588\uc9c0\ub9cc, \uc774 \uc815\ub3c4 \ud06c\uae30\uc758 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0\uc11c\ub294 \uc624\ud788\ub824 \ubd84\ub9ac\ud558\uc9c0 \uc54a\uace0 \ud558\ub098\ub85c \ud569\uce58\ub294 \uac8c \ub354 \uc88b\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\ub610\ud55c \ubd84\ub9ac\ud558\uc9c0 \uc54a\ub294\ub2e4\uba74 \uc815\ucc45\uc758 \uc21c\uc11c\uac00 \uc911\uc694\ud55c\ub370, \uc5f0\ub839\ubcc4 \ud560\uc778 \uc815\ucc45\uc744 \ub9c8\uc9c0\ub9c9\uc5d0 \ub450\uc5b4\uc57c \ud588\uae30 \ub54c\ubb38\uc5d0 \ucc45\uc784 \uc5f0\uc1c4 \ud328\ud134\ub3c4 \uace0\ub824\ub97c \ud588\uc9c0\ub9cc \uc870\uae08 \ub354 \uac04\uacb0\ud574 \ubcf4\uc774\ub294 \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc744 \uc120\ud0dd\ud588\ub2e4.\\n\\n**\ub3c4\uba54\uc778\uc5d0 \ud2b9\uc815 \uae30\uc220\uc758 \uc758\uc874\uc131\uc744 \ubd84\ub9ac**\\n\\n\ucc98\uc74c\uc5d0 \ub3c4\uba54\uc778 \ud328\ud0a4\uc9c0\uc5d0 jgrapht \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \uc758\uc874\ud558\uace0 \uc788\ub294 \ud074\ub798\uc2a4\ub97c \ub450\uc5b4\uc11c \ub3c4\uba54\uc778 \ud328\ud0a4\uc9c0\uac00 jgrapht\uc640 \uac15\uacb0\ud569\uc774 \ub418\uc5b4\ubc84\ub838\ub2e4. \\n\ub530\ub77c\uc11c \ub3c4\uba54\uc778 \ud328\ud0a4\uc9c0 \ub0b4\uc5d0\ub294 \uacbd\ub85c \uac80\uc0c9\uc5d0 \ub300\ud55c \uc778\ud130\ud398\uc774\uc2a4\ub97c \ub450\uace0, \uc138\ubd80 \uad6c\ud604\uc740 \ub3c4\uba54\uc778 \ud328\ud0a4\uc9c0 \uc678\ubd80\ub85c \ubd84\ub9ac\ud588\ub2e4. \\n\ucd5c\ub300\ud55c \uac04\uacb0\ud558\uac8c \uad6c\ud604\ud55c\ub2e4\uace0 \uc0dd\uac01\uc744 \ud574\ub3c4, \uc774\ub7f0 \ubd80\ubd84\uc740 \uc778\ud130\ud398\uc774\uc2a4\ub97c \ub450\uc5b4 \uacb0\ud569\uc744 \ud53c\ud558\ub294 \uac83\uc774 \uc88b\uc744 \uac83 \uac19\ub2e4.\\n\\n:::note \ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\\n\\n\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc740 \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud55c \uac1c\ubcc4 \uac1d\uccb4\uac00 \uc874\uc7ac\ud558\uace0, \uadf8 \uac1c\ubcc4 \uac1d\uccb4\ub4e4\uc744 \ud3ec\ud568\ud558\ub294 \ud558\ub098\uc758 \uad6c\ud604\uccb4\uac00 \ub530\ub85c \uc874\uc7ac\ud558\ub294 \ud328\ud134\uc774\ub2e4. \\n\uc774\ub54c \uc0ac\uc6a9\uc790\ub294 \uac1c\ubcc4 \uac1d\uccb4\uc640 \ud569\uc131 \uac1d\uccb4(\uac1c\ubcc4 \uac1d\uccb4\ub4e4\uc744 \ud3ec\ud568\ud558\uace0 \uc788\ub294)\ub97c \ub611\uac19\uc774 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n:::\\n\\n**\uc778\uc218 \ud14c\uc2a4\ud2b8 \uc791\uc131**\\n\\n\uc778\uc218 \ud14c\uc2a4\ud2b8\ub294 \uc0ac\uc6a9\uc790 \uc2a4\ud1a0\ub9ac \uc2dc\ub098\ub9ac\uc624 \uae30\ubc18 \ud14c\uc2a4\ud2b8\ub2e4. \\n\ube0c\ub77c\uc6b4\uc774 \ud574\uc8fc\uc2e0 \uac15\uc758 + \uc720\ud29c\ube0c\uc5d0 \uc788\ub294 \ube0c\ub77c\uc6b4\uc758 \uac15\uc758\ub97c \ubcf4\uace0 \uc9c0\ud558\ucca0 \ubbf8\uc158\uc5d0 \uc778\uc218 \ud14c\uc2a4\ud2b8\ub97c \uc801\uc6a9\ud574 \ubcf4\uc558\ub2e4. \\n\uba54\uc11c\ub4dc, \ubcc0\uc218\uba85\uc744 \uc804\ubd80 \ud55c\uae00\ub85c \uc791\uc131\ud588\ub294\ub370 \uc804\uccb4\uc801\uc778 \ud750\ub984\uc744 \uc54c\uae30 \ud3b8\ud558\uace0 \uc77d\uae30\ub3c4 \uc88b\uc558\ub2e4. \\n\uadf8\ub9ac\uace0 \uc778\uc218 \ud14c\uc2a4\ud2b8\uc5d0 \ud544\uc694\ud55c Steps\ub97c \ub9cc\ub4dc\ub294 \uacfc\uc815\uc774 \ub108\ubb34 \uc7ac\ubc0c\uc5c8\ub2e4.\\n\\n\uacb0\uacfc\ub294 \uc544\ub798\uc640 \uac19\ub2e4.\\n\\n```java\\n@Nested\\npublic class \ub178\uc120\uc744_\uc804\uccb4_\uc870\ud68c\ud560_\ub54c {\\n\\n @Test\\n void \uc0c1\ud589\uc885\uc810\uc5ed_\ubd80\ud130_\ud558\ud589\uc885\uc810\uc5ed\uc73c\ub85c_\uc815\ub82c\ub41c_\uacb0\uacfc\ub97c_\ubc18\ud658\ud55c\ub2e4() {\\n // given\\n \ub178\uc120_\uc0dd\uc131_\uc694\uccad(\\"2\ud638\uc120\\", \\"\ucd08\ub85d\\", 0);\\n \ub178\uc120\uc5d0_\uad6c\uac04\uc774_\uc874\uc7ac\ud558\uc9c0_\uc54a\uc744_\ub54c_\ucd08\uae30_\uad6c\uac04_\uc0dd\uc131_\uc694\uccad(\\"2\ud638\uc120\\", \\"\uc7a0\uc2e4\\", \\"\uc7a0\uc2e4\uc0c8\ub0b4\\", 5);\\n \uad6c\uac04_\uc0dd\uc131_\uc694\uccad(\\"2\ud638\uc120\\", \\"\uc7a0\uc2e4\uc0c8\ub0b4\\", \\"\uc885\ud569\uc6b4\ub3d9\uc7a5\\", \uc624\ub978\ucabd, 5);\\n\\n \ub178\uc120_\uc0dd\uc131_\uc694\uccad(\\"9\ud638\uc120\\", \\"\uace0\ub3d9\\", 0);\\n \ub178\uc120\uc5d0_\uad6c\uac04\uc774_\uc874\uc7ac\ud558\uc9c0_\uc54a\uc744_\ub54c_\ucd08\uae30_\uad6c\uac04_\uc0dd\uc131_\uc694\uccad(\\"9\ud638\uc120\\", \\"\ubd09\uc740\uc0ac\\", \\"\uc885\ud569\uc6b4\ub3d9\uc7a5\\", 3);\\n \uad6c\uac04_\uc0dd\uc131_\uc694\uccad(\\"9\ud638\uc120\\", \\"\uc885\ud569\uc6b4\ub3d9\uc7a5\\", \\"\uc0bc\uc804\\", \uc624\ub978\ucabd, 7);\\n\\n // when\\n final var \uc870\ud68c_\uacb0\uacfc = \ub178\uc120_\uc804\uccb4_\uc870\ud68c_\uc694\uccad();\\n\\n // then\\n \uc694\uccad_\uacb0\uacfc\uc758_\uc0c1\ud0dc\ub97c_\uac80\uc99d\ud55c\ub2e4(\uc870\ud68c_\uacb0\uacfc, \uc815\uc0c1_\uc694\uccad);\\n \ub178\uc120_\uc804\uccb4_\uc870\ud68c_\uacb0\uacfc\ub97c_\ud655\uc778\ud55c\ub2e4(\\n \uc870\ud68c_\uacb0\uacfc,\\n \ub178\uc120_\uc815\ubcf4(\\"2\ud638\uc120\\", \\"\ucd08\ub85d\\", 0, \\"\uc7a0\uc2e4\\", \\"\uc7a0\uc2e4\uc0c8\ub0b4\\", \\"\uc885\ud569\uc6b4\ub3d9\uc7a5\\"),\\n \ub178\uc120_\uc815\ubcf4(\\"9\ud638\uc120\\", \\"\uace0\ub3d9\\", 0, \\"\ubd09\uc740\uc0ac\\", \\"\uc885\ud569\uc6b4\ub3d9\uc7a5\\", \\"\uc0bc\uc804\\")\\n );\\n }\\n}\\n```\\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n**\uc758\uacac \uc870\uc728\ud558\uae30**\\n\\n\ubc00\ub9ac\uac00 \ud544\uc694\ud55c \ubd80\ubd84\uc5d0\uc11c \uc758\uacac\uc744 \uc801\uadf9\uc801\uc73c\ub85c \ub0b4\uc918\uc11c \uc9c4\ud589\uc774 \uc218\uc6d4\ud588\ub2e4. \\n\uc758\uc0ac\uc18c\ud1b5\uc774 \ub9e4\uc6b0 \uc798 \ub3fc\uc11c \uc88b\uc558\uace0 \ub355\ubd84\uc5d0 \uc2dc\uac04 \ub0b4\uc5d0 \uc694\uad6c\uc0ac\ud56d\uc744 \ub9cc\uc871\ud574 \ubbf8\uc158\uc744 \uc81c\ucd9c\ud560 \uc218 \uc788\uc5c8\ub358 \uac83 \uac19\ub2e4. \\n\\n**\uaf3c\uaf3c\ud558\uac8c \ucf54\ub529\ud558\uae30**\\n\\n\ubc00\ub9ac\ub294 \ucf54\ub529\uc744 \uc5c4\uccad \uaf3c\uaf3c\ud558\uac8c \ud558\ub294 \uac83 \uac19\ub2e4. \\n\ubcc0\uc218\uba85, \uba54\uc11c\ub4dc\uba85\uc744 \uc911\uc694\ud558\uac8c \uc0dd\uac01\ud588\uace0, \uc88b\uc740 \ubcc0\uc218\uba85\uc744 \uc798 \uc9d3\ub294 \uac83 \uac19\ub2e4. \\n\ub610\ud55c \ucf54\ub529\ud560 \ub54c \ub0b4\uac00 \ud3c9\uc18c\uc5d0 \uc0ac\uc6a9\ud558\ub294 \ucf54\ub529 \ucee8\ubca4\uc158\uc5d0 \ub9de\ucdb0\uc8fc\ub294 \uac83 \uac19\uc544\uc11c \ud398\uc5b4 \ud560 \ub54c \ud3b8\ud588\ub2e4! \\n\\n**\ud3b8\ud55c \ubd84\uc704\uae30**\\n\\n\uc804\uccb4\uc801\uc73c\ub85c \ud398\uc5b4 \ud560 \ub54c \ud3b8\ud558\uac8c \uc9c4\ud589\ud588\ub358 \uac83 \uac19\ub2e4. \\n\uc77c\uc815\ub3c4 \uadf8\ub807\uace0, \ud398\uc5b4 \uc9c4\ud589\ud560 \ub54c\ub3c4 \uadf8\ub807\uace0 \ud070 \ubb38\uc81c\uac00 \uc5c6\uc5c8\ub358 \uac83 \uac19\uc544\uc11c \uc88b\uc558\ub2e4. \\n\ub098\ub294 \uacfc\uc5f0 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc5d0\uac8c \ud3b8\ud55c \uc0ac\ub78c\uc77c\uae4c?"},{"id":"accidental-duplication","metadata":{"permalink":"/accidental-duplication","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5.mdx","source":"@site/blog/2023-2/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5.mdx","title":"\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5","description":"\uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc0c1\ud488 \ucd94\uac00\uc640 \uc0c1\ud488 \uc218\uc815\uc5d0 \ub300\ud55c \uc694\uad6c\uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4.","date":"2023-05-24T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 24\uc77c","tags":[{"label":"DTO","permalink":"/tags/dto"}],"readingTime":7.525,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5","slug":"accidental-duplication","tags":["DTO"]},"prevItem":{"title":"\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0","permalink":"/subway-retrospective"},"nextItem":{"title":"\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0","permalink":"/shopping-cart-retrospective"}},"content":"\uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc0c1\ud488 \ucd94\uac00\uc640 \uc0c1\ud488 \uc218\uc815\uc5d0 \ub300\ud55c \uc694\uad6c\uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4. \\n\uc694\uccad\uc5d0 \ub2f4\uae34 Body\ub97c \ud1b5\ud574 \uc804\ub2ec\ubc1b\uc740 \uac12\uc744 DTO\ub85c \ub9e4\ud551\ud558\uc5ec \ucd94\uac00\uc640 \uc218\uc815\uc744 \ud588\ub2e4.\\n\\n### \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\uc5d0\uc11c\uc758 \uc0c1\ud488 \ucd94\uac00 \ubc0f \uc218\uc815\\n\\n![\uc911\ubcf51](./\uc911\ubcf51.png)\\n\\n\ud074\ub798\uc2a4\uba85\uc744 \uc81c\uc678\ud558\uace0 \ud544\ub4dc\uc640 \uac80\uc99d\ub85c\uc9c1 \uadf8 \uc678 \ubaa8\ub4e0\uac8c \uac19\uc740 DTO\ub97c \ubcf4\uba70 \uc911\ubcf5\uc774\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\ud558\uc9c0\ub9cc \ubc18\ub300\ub85c \uc6a9\ub3c4\uac00 \ub2e4\ub974\uae30 \ub54c\ubb38\uc5d0 \uc911\ubcf5\uc774 \uc544\ub2c8\ub77c\uace0 \uc0dd\uac01\ud558\uae30\ub3c4 \ud588\ub2e4. \\n\uc704 \uacbd\uc6b0\ub294 \uc911\ubcf5\uc77c\uae4c? \uc911\ubcf5\uc774 \uc544\ub2d0\uae4c?\\n\\n\uc774 \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c \ub2e4\uc74c\uacfc \uac19\uc740 \ub9ac\ubdf0\ub97c \ubc1b\uc558\ub2e4.\\n\\n> `ProductSaveRequest`\uc640 `ProductUpdateRequest`\uac00 \uc644\uc804\ud788 \ub3d9\uc77c\ud55c\ub370, \uc7ac\uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc744\uae4c? \ub77c\ub294 \ub9ac\ubdf0\ub97c \ub0a8\uacbc\uc5c8\uc5b4\uc694. \uc0ac\uc2e4 \uc0dd\uc131\uacfc \uc218\uc815\uc740 \uc11c\ub85c \ub2ec\ub77c\uc9c8 \uac1c\uc5f0\uc131\uc774 \ub192\uc544\uc11c \ubbf8\ub9ac \ubd84\ub9ac\ud574\ub193\ub294 \uac8c \ub354 \uc88b\uc740 \ubc29\ubc95\uc774\uae34 \ud55c\ub370, \uadf8\ub798\ub3c4 \uc911\ubcf5\uc740 \uc2eb\uc5b4\uc11c \uc800\ub3c4 \uc694\uc998 \uc774\ub7f0\uc800\ub7f0 \ubc29\ubc95\ub4e4\uc744 \uc2dc\ub3c4\ud574\ubcf4\ub294 \uc911 \uc785\ub2c8\ub2e4. \ud5c8\ube0c\ub294 \uc774 \ubd80\ubd84\uc5d0 \ub300\ud574 \uc5b4\ub5a4 \uc0dd\uac01\uc744 \uac00\uc9c0\uace0 \uc788\uc744\uc9c0 \uad81\uae08\ud558\ub124\uc694 \u314e\u314e\\n> \\n\\n\uc9c8\ubb38\uc5d0 \ub300\ud574 \uc544\ub798\uc640 \uac19\uc774 \ub2f5\ubcc0\uc744 \ud588\ub2e4.\\n\\n> \uc800\uc7a5\uacfc \uc218\uc815\ud560 \ub54c \ud544\uc694\ud55c \ud544\ub4dc\uac12\uc774 \ub3d9\uc77c\ud558\uc5ec \ud604\uc7ac \uad6c\uc870\uc5d0\uc11c\ub294 \ud558\ub098\ub85c \uc0ac\uc6a9\ud574\ub3c4 \ub41c\ub2e4\uace0 \uc0dd\uac01\uc744 \ud558\uc9c0\ub9cc, \ub9d0\uc500\ud574\uc8fc\uc2e0\ub300\ub85c \uc694\uad6c\uc0ac\ud56d\uc774 \ubcc0\uacbd\ub41c\ub2e4\uba74 \ub2ec\ub77c\uc9c8 \uac00\ub2a5\uc131\uc774 \ub192\ub2e4\uace0 \ud310\ub2e8\ud558\uc600\uc2b5\ub2c8\ub2e4!\\n> \\n\\n### \uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5\\n\\n\ub85c\ubc84\ud2b8 \ub9c8\ud2f4\ub2d8\uc774 \uc9d1\ud544\ud558\uc2e0 \ud074\ub9b0 \uc544\ud0a4\ud14d\ucc98\ub294 \uc544\ub798\uc640 \uac19\uc774 \uc911\ubcf5\uc744 \uc5ec\ub7ec\uac00\uc9c0 \uc885\ub958\ub85c \ub098\ub204\uc5b4 \uc124\uba85\ud558\uace0 \uc788\ub2e4.\\n\\n- \uc9c4\uc9dc \uc911\ubcf5: \ud55c \uc778\uc2a4\ud134\uc2a4\uac00 \ubcc0\uacbd\ub418\uba74, \ub3d9\uc77c\ud55c \ubcc0\uacbd\uc744 \uadf8 \uc778\uc2a4\ud134\uc2a4\uc758 \ubaa8\ub4dc \ubcf5\uc0ac\ubcf8\uc5d0 \ubc18\ub4dc\uc2dc \uc801\uc6a9\ud574\uc57c \ud55c\ub2e4.\\n- \uac70\uc9d3\ub41c \uc911\ubcf5, \uc6b0\ubc1c\uc801 \uc911\ubcf5: \uc911\ubcf5\uc73c\ub85c \ubcf4\uc774\ub294 \ub450 \ucf54\ub4dc \uc601\uc5ed\uc774 \uac01\uc790\uc758 \uacbd\ub85c\ub85c \ubc1c\uc804\ud55c\ub2e4\uba74, \uc989 \uc11c\ub85c \ub2e4\ub978 \uc18d\ub3c4\uc640 \ub2e4\ub978 \uc774\uc720\ub85c \ubcc0\uacbd\ub41c\ub2e4\uba74 \uc774 \ub450 \ucf54\ub4dc\ub294 \uc9c4\uc9dc \uc911\ubcf5\uc774 \uc544\ub2c8\ub2e4.\\n\\n\ucd94\uac00\uc640 \uc218\uc815\uc740 \ucd08\uae30\uc5d0\ub294 \uc911\ubcf5\uc73c\ub85c \ubcf4\uc774\uc9c0\ub9cc \ucd08\uae30 \uc0dd\uc131\uc2dc\uc5d0\ub9cc \uae30\uc785\ud558\ub294 \ub370\uc774\ud130\ub4e4\uc774 \ucd94\uac00\ub418\uac70\ub098, \uc2dc\uac04\uc774 \uc9c0\ub098\uba74\uc11c \uc11c\ub85c \ub2ec\ub77c\uc9c8 \uac00\ub2a5\uc131\uc774 \ub192\uc544\uc9c4\ub2e4.\\n\uadf8\ub807\uae30 \ub54c\ubb38\uc5d0 \uc704 \uc0c1\ud669\uc740 \uc6b0\ubc1c\uc801 \uc911\ubcf5\uc73c\ub85c \ubcf4\uc778\ub2e4. \uadf8\ub798\ub3c4 \uc911\ubcf5\uc744 \uc81c\uac70\ud574\ubcfc \uc218 \uc788\uc9c0 \uc54a\uc744\uae4c?\\n\\n### \ud558\ub098\ub85c \uc0ac\uc6a9\ud558\ub294 \uac74 \uc548\uc88b\uc544\ubcf4\uc774\uace0, \uc911\ubcf5\uc740 \uc81c\uac70\ud558\uace0 \uc2f6\uc740 \ub9c8\uc74c\\n\\n\uc9c0\uae08\uc740 \ucd94\uac00, \uc218\uc815 2\uac00\uc9c0 \uacbd\uc6b0 \ubc16\uc5d0 \uc5c6\uc9c0\ub9cc \uc870\uae08 \ub354 \ubcf5\uc7a1\ud55c \uc694\uad6c\uc0ac\ud56d\uc774 \uc8fc\uc5b4\uc838\uc11c 10\uac00\uc9c0 \uacbd\uc6b0\ub85c \uc785\ub825\uc744 \ubc1b\uc73c\uba74 \uc5b4\ub5bb\uac8c \ud574\uc57c\ud560\uae4c? \\n\uc11c\ube44\uc2a4 \uacc4\uce35\uc5d0\uc11c\ub3c4 \uacc4\uce35\uc758 \ubd84\ub9ac\ub97c \uc704\ud574\uc11c \ub2e4\ub978 DTO\ub97c \uc0ac\uc6a9\ud558\uace0 \uc788\ub2e4\uba74 20\uac1c\uc758 DTO\ub97c \ub9cc\ub4e4\uc5b4\uc57c \ud560\uae4c? \\n\ub9ac\ubdf0\uc5b4\uac00 \uc54c\ub824\uc900 \uc758\uc874 \uc5ed\uc804\uc744 \uc774\uc6a9\ud55c \ubc29\ubc95\uc744 \ud1b5\ud574 \uc774\ub97c \ud574\uacb0\ud574\ubcf4\uc790! \\n\\n### \uc911\ubcf5 \uc81c\uac70 \uc804 \ucf54\ub4dc\\n\\n\ud604\uc7ac \ucf54\ub4dc\uc5d0\uc11c\ub294 \uc544\ub798\uc640 \uac19\uc740 \uad6c\uc870\ub85c \ub418\uc5b4\uc788\ub2e4. \\nController\uc640 Service\uc5d0\uc11c \uc800\uc7a5, \uc218\uc815\ud560 \ub54c \uac01\uac01\uc758 DTO\ub97c \uc0ac\uc6a9\ud558\uace0 \uc788\ub2e4.\\n\ud604\uc7ac DTO\ub294 controller, service \ud328\ud0a4\uc9c0 \ub0b4\uc5d0 \uc788\ub294 \uac83\uc774 \uc544\ub2c8\ub77c dto\ub77c\ub294 \ud328\ud0a4\uc9c0\uc5d0 \uc704\uce58\ud558\uace0 \uc788\ub2e4.\\n\\n```java\\n\u251c\u2500\u2500 controller\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductController\\n\u251c\u2500\u2500 service\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductService\\n\u251c\u2500\u2500 dto\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductSaveRequest\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductUpdateRequest\\n```\\n\\n![\uc911\ubcf52](./\uc911\ubcf52.png)\\n\\n### \uc778\ud130\ud398\uc774\uc2a4 \uc791\uc131\ud558\uae30\\n\\n![\uc911\ubcf53](./\uc911\ubcf53.png)\\n\\n\uc11c\ube44\uc2a4 \ub808\uc774\uc5b4\uc5d0\uc11c \ud544\uc694\ub85c \ud558\ub294 \uac12\ub4e4\uc744 \uc778\ud130\ud398\uc774\uc2a4\ub85c \uc815\uc758\ud55c\ub2e4. \\n\ud574\ub2f9 \uc778\ud130\ud398\uc774\uc2a4\ub294 \uc11c\ube44\uc2a4\uc5d0\uc11c \uc0ac\uc6a9\ud558\uae30 \ub54c\ubb38\uc5d0 service \ud328\ud0a4\uc9c0 \ub0b4\ubd80\ub85c \uc62e\uaca8\uc900\ub2e4.\\n\\n```java\\n\u251c\u2500\u2500 controller\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductController\\n\u251c\u2500\u2500 service\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductService\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductSaveRequest\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductUpdateRequest\\n```\\n\\n```java\\npublic interface ProductSaveRequest {\\n\\n String getName();\\n\\n String getImage();\\n\\n Long getPrice();\\n}\\n\\n// ProductService\\npublic Long save(final ProductSaveRequest request) {\\n final Product product = new Product(request.getName(), request.getImage(), request.getPrice());\\n return productDao.saveAndGetId(product);\\n}\\n```\\n\\n### \uad6c\ud604\uccb4 \uc791\uc131\ud558\uae30\\n\\n![\uc911\ubcf54](./\uc911\ubcf54.png)\\n\\n\uc704\uc5d0\uc11c \uc791\uc131\ud55c \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud558\ub294 \ud074\ub798\uc2a4\ub97c \uc791\uc131\ud55c\ub2e4. \\n\uc694\uccad\uc740 ProductRequest \ud074\ub798\uc2a4\ub85c \ubc1b\uace0, \uc11c\ube44\uc2a4\uc5d0 \uc804\ub2ec\ud560 \ub550 \ud574\ub2f9 \uc778\ud130\ud398\uc774\uc2a4\uc758 \uba85\uc138\ub9cc \ub9de\ucd94\uba74 \ubb38\uc81c\uc5c6\uc774 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n```java\\n\u251c\u2500\u2500 controller\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductController\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductRequest\\n\u251c\u2500\u2500 service\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductService\\n\u2502\xa0\xa0 \u251c\u2500\u2500 ProductSaveRequest\\n\u2502\xa0\xa0 \u2514\u2500\u2500 ProductUpdateRequest\\n```\\n\\n```java\\npublic class ProductRequest implements ProductSaveRequest, ProductUpdateRequest {\\n\\n @NotBlank(message = \\"\uc774\ub984\uc740 \uacf5\ubc31\uc77c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.\\")\\n @Size(min = 1, max = 100, message = \\"\uc774\ub984\uc740 \ucd5c\uc18c {min}\uc790 \uc774\uc0c1, {max}\uc790 \uc774\ud558\uc5ec\uc57c \ud569\ub2c8\ub2e4.\\")\\n private final String name;\\n\\n @NotBlank(message = \\"\uc774\ubbf8\uc9c0\ub294 \uacf5\ubc31\uc77c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.\\")\\n private final String image;\\n\\n @Range(message = \\"\uac00\uaca9\uc740 \ucd5c\uc18c {min}\uc6d0 \uc774\uc0c1, {max}\uc6d0 \uc774\ud558\uc5ec\uc57c \ud569\ub2c8\ub2e4.\\")\\n private final long price;\\n\\n public ProductRequest(final String name, final String image, final long price) {\\n this.name = name;\\n this.image = image;\\n this.price = price;\\n }\\n\\n @Override\\n public String getName() {\\n return name;\\n }\\n\\n @Override\\n public String getImage() {\\n return image;\\n }\\n\\n @Override\\n public long getPrice() {\\n return price;\\n }\\n}\\n\\n// ProductController\\n@PostMapping(\\"/products\\")\\npublic ResponseEntity save(@Valid @RequestBody final ProductRequest request) {\\n final Long id = productService.save(request);\\n return ResponseEntity.created(URI.create(\\"/products/\\" + id)).build();\\n}\\n```\\n\\n### \uc815\ub9ac\\n\\n\uc704\uc640 \uac19\uc774 \uad6c\ud604\ud55c\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uc7a5\uc810\uc744 \uc5bb\uc744 \uc218 \uc788\ub2e4. \\n\\n1. Service\uc5d0\uc11c \ubaa8\ub4e0 \ud074\ub77c\uc774\uc5b8\ud2b8 \uc694\uccad\uc5d0 \ub300\ud55c DTO\ub97c \uc54c\uc9c0 \uc54a\uc544\ub3c4 \ub41c\ub2e4.\\n2. \uacf5\ud1b5\uc801\uc73c\ub85c \uc0ac\uc6a9\ud558\ub294 DTO\ub97c \uc81c\uc678\ud558\uace0 DTO \ud328\ud0a4\uc9c0\uc5d0 \ub300\ud55c \uacb0\ud569\ub3c4\uac00 \ub0ae\uc544\uc9c0\uace0, \uac01 \ub808\uc774\uc5b4\uc758 \uc751\uc9d1\ub3c4\uac00 \uc99d\uac00\ud55c\ub2e4.\\n3. \uc694\uccad \uac1d\uccb4\ub9cc \ub2e4\ub974\uace0 \uc11c\ube44\uc2a4\uc5d0\uc11c \ub3d9\uc77c\ud55c \ud589\uc704\ub97c \uc218\ud589\ud558\ub294 \uacbd\uc6b0 \uc911\ubcf5\uc744 \uc81c\uac70\ud560 \uc218 \uc788\ub2e4.\\n\\n\uc704 \ubc29\ubc95\uc744 \uc9c0\uae08 \ubbf8\uc158\uc5d0\uc11c \ubc14\ub85c \uc801\uc6a9\ud560\uae4c \ud558\ub2e4\uac00, \ub098\uc911\uc5d0 \ud544\uc694\ud560 \ub54c \uc801\uc6a9\ud558\uba74 \ub354 \uc88b\uc744 \uac83 \uac19\uc544\uc11c \ubbf8\uc158\uc5d0\ub294 \uc801\uc6a9\ud558\uc9c0 \uc54a\uc558\ub2e4. \\n\uc0c1\ud669\uc5d0 \ub9de\ucdb0 \uc801\uc7ac\uc801\uc18c\uc5d0 \uc758\uc874 \uc5ed\uc804\uc744 \uc774\uc6a9\ud574\ubcf4\ub294 \uac83\ub3c4 \uc88b\uc744 \uac83 \uac19\ub2e4.\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n\ud074\ub9b0 \uc544\ud0a4\ud14d\ucc98 16\uc7a5 \ub3c5\ub9bd\uc131, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4 \\n[https://techblog.woowahan.com/2647/](https://techblog.woowahan.com/2647/) \\n[https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/](https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/)"},{"id":"shopping-cart-retrospective","metadata":{"permalink":"/shopping-cart-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0","description":"\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158","date":"2023-05-12T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 12\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":4.78,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0","slug":"shopping-cart-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5","permalink":"/accidental-duplication"},"nextItem":{"title":"\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0","permalink":"/web-racing-car-retrospective"}},"content":"### \uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\\n\\n\uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\uc740 \ube14\ub799\ucea3\uc774\ub791 \uc9c4\ud589\ud588\ub2e4. \\n\uc694\uad6c\uc0ac\ud56d\uc774 \uc5c4\uccad \ubcf5\uc7a1\ud55c \ubbf8\uc158\uc740 \uc544\ub2c8\uc5c8\uace0, \uc2a4\ud504\ub9c1\uc744 \uc0ac\uc6a9\ud558\uc5ec \uae30\ubcf8\uc801\uc778 CRUD\ub97c \uad6c\ud604\ud558\ub294 \ubbf8\uc158\uc774\uc5c8\ub2e4. \\n2\ub2e8\uacc4\uc5d0\uc11c\ub294 Basic \uc778\uc99d\uc744 \ud1b5\ud574 \uc790\uc2e0\uc758 \uc7a5\ubc14\uad6c\ub2c8\uc5d0\ub9cc \uc0c1\ud488\uc744 \ub2f4\uace0, \uc81c\uac70\ud560 \uc218 \uc788\ub3c4\ub85d \uad6c\ud604\ud558\ub294 \uc694\uad6c\uc0ac\ud56d\uc774 \ucd94\uac00\ub418\uc5c8\ub2e4. \\nInterceptor\ub098 Argument Resolver\uc5d0 \ub300\ud55c \uc774\ud574\ub3c4\uac00 \ub192\uc9c0 \uc54a\uc558\ub294\ub370, \uc774\ubc88 \ubbf8\uc158\uc744 \ud1b5\ud574 \uc870\uae08 \ub354 \uc54c\uc544\uac04 \ub290\ub08c\uc774\ub2e4. \\n\uc774\uc804\uc5d0 \uc2a4\ud504\ub9c1 \uc0ac\uc6a9\ud560 \ub54c\ub294 \uc544\ubb34 \uc0dd\uac01 \uc5c6\uc774 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\ub294 \uacbd\uc6b0\uac00 \ub9ce\uc558\ub294\ub370, \ucf54\ub4dc\ub97c \uc791\uc131\ud560 \ub54c \uadfc\uac70\uac00 \uc0dd\uae30\uace0 \uc788\ub294 \uac83 \uac19\ub2e4. \\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**DTO \uc6b0\ubc1c\uc801 \uc911\ubcf5**\\n\\n\uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc0c1\ud488 \ucd94\uac00\uc640 \uc0c1\ud488 \uc218\uc815\uc5d0 \ub300\ud55c \uc694\uad6c\uc0ac\ud56d\uc774 \uc788\uc5c8\ub2e4. \\n\\n![dto1](./dto1.png)\\n\\n\ud074\ub798\uc2a4\uba85\uc744 \uc81c\uc678\ud558\uace0 \ud544\ub4dc\uc640 \uac80\uc99d \ub85c\uc9c1 \uadf8 \uc678 \ubaa8\ub4e0 \uac8c \uac19\uc740 DTO\ub97c \ubcf4\uba70 \uc911\ubcf5\uc774\ub77c\uace0 \uc0dd\uac01\uc744 \ud588\uace0, \ubc18\ub300\ub85c \uc6a9\ub3c4\uac00 \ub2e4\ub974\uae30 \ub54c\ubb38\uc5d0 \uc911\ubcf5\uc774 \uc544\ub2c8\ub77c\uace0 \uc0dd\uac01\ud558\uae30\ub3c4 \ud588\ub2e4. \\n\ub85c\ubc84\ud2b8 \ub9c8\ud2f4\ub2d8\uc774 \uc9d1\ud544\ud558\uc2e0 \ud074\ub9b0 \uc544\ud0a4\ud14d\ucc98\ub294 \uc544\ub798\uc640 \uac19\uc774 \uc911\ubcf5\uc744 \uc5ec\ub7ec \uac00\uc9c0 \uc885\ub958\ub85c \ub098\ub204\uc5b4 \uc124\uba85\ud558\uace0 \uc788\ub2e4. \\n\\n- \uc9c4\uc9dc \uc911\ubcf5: \ud55c \uc778\uc2a4\ud134\uc2a4\uac00 \ubcc0\uacbd\ub418\uba74, \ub3d9\uc77c\ud55c \ubcc0\uacbd\uc744 \uadf8 \uc778\uc2a4\ud134\uc2a4\uc758 \ubaa8\ub4dc \ubcf5\uc0ac\ubcf8\uc5d0 \ubc18\ub4dc\uc2dc \uc801\uc6a9\ud574\uc57c \ud55c\ub2e4.\\n- \uc6b0\ubc1c\uc801 \uc911\ubcf5: \uc911\ubcf5\uc73c\ub85c \ubcf4\uc774\ub294 \ub450 \ucf54\ub4dc \uc601\uc5ed\uc774 \uac01\uc790\uc758 \uacbd\ub85c\ub85c \ubc1c\uc804\ud55c\ub2e4\uba74, \uc989 \uc11c\ub85c \ub2e4\ub978 \uc18d\ub3c4\uc640 \ub2e4\ub978 \uc774\uc720\ub85c \ubcc0\uacbd\ub41c\ub2e4\uba74 \uc774 \ub450 \ucf54\ub4dc\ub294 \uc9c4\uc9dc \uc911\ubcf5\uc774 \uc544\ub2c8\ub2e4.\\n\\n\ucd94\uac00\uc640 \uc218\uc815\uc740 \ucd08\uae30\uc5d0\ub294 \uc911\ubcf5\uc73c\ub85c \ubcf4\uc774\uc9c0\ub9cc \ucd08\uae30 \uc0dd\uc131 \uc2dc\uc5d0\ub9cc \uae30\uc785\ud558\ub294 \ub370\uc774\ud130\ub4e4\uc774 \ucd94\uac00\ub418\uac70\ub098, \uc2dc\uac04\uc774 \uc9c0\ub098\uba74\uc11c \uc11c\ub85c \ub2ec\ub77c\uc9c8 \uac00\ub2a5\uc131\uc774 \ub192\uc544\uc9c4\ub2e4. \\n\ub530\ub77c\uc11c \ub9ac\ubdf0\uc5b4 \uc6e8\uc9c0\uac00 \uc544\ub798\uc640 \uac19\uc774 \uc758\uc874 \uc5ed\uc804\uc744 \uc774\uc6a9\ud558\ub294 \ubc29\ubc95\ub3c4 \uc788\ub2e4\uace0 \uc54c\ub824\uc8fc\uc168\ub2e4. \\n\\n![dto2](./dto2.png)\\n\\n**Interceptor\uc5d0\uc11c \uc778\uc99d\ud55c \uac12 \uc7ac\uc0ac\uc6a9**\\n\\n\uc0ac\uc2e4 \uc870\ud68c\ub97c \ub450 \ubc88 \ud558\uae30 \uc2eb\uc5b4\uc11c \ub2e4\uc591\ud55c \ubc29\ubc95\uc744 \uc0dd\uac01\ud588\uc5c8\ub294\ub370 \uc774\ubc88 \ubbf8\uc158\uc5d0\uc11c\ub294 ThreadLocal\uc744 \uc0ac\uc6a9\ud588\ub2e4. \\n\uc77c\ub2e8 Tomcat\uc740 \uc694\uccad\ub9c8\ub2e4 \ub2e4\ub978 \uc2a4\ub808\ub4dc\ub97c \uc0ac\uc6a9\ud558\uace0, Interceptor\uc5d0\uc11c \uc870\ud68c\ud574\uc11c \ub9cc\ub4e0 Credential\uc744 ThreadLocal\uc5d0 \ub123\uc5b4\ub450\uc5c8\ub2e4\uac00 ArgumentResolver\uc5d0\uc11c \uaebc\ub0b8 \ub2e4\uc74c ThreadLocal\uc744 clear \ud558\uba74 \ubb38\uc81c\uac00 \uc5c6\uc744 \uac70\ub77c \ud310\ub2e8\ud588\ub2e4. \\n\\n\ub9ac\ubdf0\uc5b4\uc778 \uc6e8\uc9c0\uc5d0\uac8c\ub3c4 \uc5b4\ub5a4 \ubc29\ubc95\uc744 \uc0ac\uc6a9\ud560\uc9c0 \uad81\uae08\uc99d\uc744 \uc791\uc131\ud588\uc5c8\ub2e4. \\n\uc6e8\uc9c0\ub294 email\uc5d0 index\ub97c \uac78\uc5b4\ub450\uace0 dao \uc7ac\uc870\ud68c\ub97c \uc0ac\uc6a9\ud560 \uac83\uc774\ub77c\uace0 \ud588\ub2e4. \\n\uc7ac\uc0ac\uc6a9\ud558\uc9c0 \uc54a\uace0 db\uc5d0 \uc778\ub371\uc2a4\ub97c \uac78 \uc0dd\uac01\uc740 \ud558\uc9c0 \ubabb\ud588\ub294\ub370, \uc81c\uc77c \uc9c1\uad00\uc801\uc774\uace0 \uc88b\uc740 \ubc29\ubc95\uc774\ub77c\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n**\uae30\ub85d**\\n\\n\ube14\ub799\ucea3\uc740 \uae30\ub85d\uc744 \uad49\uc7a5\ud788 \uc798 \ud558\ub294 \ud06c\ub8e8\uc600\ub2e4. \\n\ub178\uc158\uc5d0 \ud398\uc5b4\ub97c \uc9c4\ud589\ud558\uba74\uc11c \ud588\ub358 \ub0b4\uc6a9 + \uace0\ubbfc\ud588\ub358 \ubd80\ubd84 + \ud68c\uace0\ub97c \uaf3c\uaf3c\ud558\uac8c \uae30\ub85d\ud574\uc11c \uacf5\uc720\ud574 \uc8fc\uc5c8\ub2e4. \\n\ucd94\uac00\uc801\uc73c\ub85c \uc774\ubaa8\uc9c0\ub97c \uc801\uadf9\uc801\uc73c\ub85c \uc0ac\uc6a9\ud558\uc5ec \ub354\uc6b1 \uc88b\uc558\ub2e4!\\n\\n**\uc758\uacac \uc77c\uce58\uc2dc\ud0a4\uae30**\\n\\n\ud398\uc5b4 \uc2dc\uac04\uc740 \ud55c\uc815\ub418\uc5b4 \uc788\uace0, \uae30\uac04 \ub0b4 \uc694\uad6c\uc0ac\ud56d\uc744 \ub9cc\uc871\ud574\uc57c \ud55c\ub2e4. \\n\ub530\ub77c\uc11c \uc801\ub2f9\ud788 \ud0c0\ud611\uc744 \ubd10\uc11c \uc758\uacac\uc744 \ube60\ub974\uac8c \uc218\uc6a9\ud574 \ub370\ub4dc\ub77c\uc778\uc744 \ub9de\ucd94\ub294 \uac83\ub3c4 \uc911\uc694\ud558\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\ube14\ub799\ucea3\uc740 \ub0b4 \uc758\uacac\uc744 \uc798 \ub4e4\uc5b4\uc92c\uace0, \ub355\ubd84\uc5d0 \ub9c9\ud788\ub294 \ubd80\ubd84 \uc5c6\uc774 \ube60\ub974\uac8c \ubbf8\uc158\uc744 \uc9c4\ud589\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n\ube68\ub9ac \uce5c\ud574\uc84c\uace0, \uc758\uc0ac\uc18c\ud1b5\uc774 \uc798 \ub3fc\uc11c \uc7ac\ubc0c\uac8c \ucf54\ub529\ud560 \uc218 \uc788\uc5c8\ub2e4!"},{"id":"web-racing-car-retrospective","metadata":{"permalink":"/web-racing-car-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-02-\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-05-02-\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0","description":"\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158","date":"2023-05-02T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 2\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":3.535,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0","slug":"web-racing-car-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0","permalink":"/shopping-cart-retrospective"},"nextItem":{"title":"[\ud14c\ucf54\ucc57] 2. \ubc30\ud3ec","permalink":"/tecochat-retrospective-2"}},"content":"### \uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158\\n\\n\uc0ac\uc774\ub4dc \ud504\ub85c\uc81d\ud2b8\ub97c \ud55c\ub2e4\uace0 \uc2dc\uac04\uc774 \ub9ce\uc774 \uc5c6\uc5b4\uc11c \ud68c\uace0\uac00 \ub2a6\uc5b4\uc84c\ub2e4. \\n\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158\uc5d0\uc11c\ub294 \ube44\ubc84\uc640 \ud398\uc5b4\uac00 \ub9e4\uce6d\ub418\uc5c8\ub2e4. \\n\ub808\ubca8 2\uc5d0\uc11c \uc9c4\ud589\ud558\ub294 \uccab \ubbf8\uc158\uc774\ub77c \ub9ce\uc774 \uae34\uc7a5\ub418\uc5c8\uc9c0\ub9cc, \uadf8\ub798\ub3c4 \ube44\ubc84\ub791 \ucd08\ubc18\uc5d0 \ub9db\uc788\ub294 \uac83\ub3c4 \ub9ce\uc774 \uba39\uc73c\uba74\uc11c \ube68\ub9ac \uce5c\ud574\uc838\uc11c \uc7ac\ubc0c\uac8c \ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n\uc2a4\ud504\ub9c1\uc744 \uc870\uae08 \uc0ac\uc6a9\ud560 \uc904 \uc54c\uc544\uc11c, \ube44\ubc84\ub791 \uac19\uc774 \ud559\uc2b5\ud558\uba74\uc11c \ubbf8\uc158\uc744 \uc9c4\ud589\ud588\ub2e4. \\n\uccab \ubbf8\uc158\uc774\ub77c \uadf8\ub7f0\uc9c0 \ud2b9\ubcc4\ud55c \ubd80\ubd84\uc740 \uc5c6\uc5c8\uace0, \ucd5c\ub300\ud55c \uae54\ub054\ud558\uac8c \uc791\uc131\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. \\n\ub09c\uc774\ub3c4 \ub192\uc740 \ubbf8\uc158\uc774 \uc544\ub2c8\uc5c8\uc9c0\ub9cc \ub9ac\ubdf0\uc5b4\uc778 \ub77c\ube48\uc5d0\uac8c \uce6d\ucc2c\uc744 \ub9ce\uc774 \ubc1b\uc544\uc11c \uae30\ubd84\uc774 \uc88b\uc558\ub2e4. \\n\ub77c\ube48 \uac10\uc0ac\ud569\ub2c8\ub2e4! \\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n\ucee8\ub514\uc158\ub3c4 \uc88b\uc9c0 \uc54a\uace0 \uc5f4\uc815\ub3c4 \uc2dd\uc740 \uac83 \uac19\uc740 \ub290\ub08c\uc774 \ub4e4\uc5c8\ub2e4. \\n\ubbf8\uc158\uc774 \ub2e4\uc18c \uc5ec\uc720\ub86d\ub2e4\uace0 \ub290\uaef4\uc838\uc11c, \uc2dc\uac04\uc5d0 \ub300\ud55c \ubd80\ubd84\ub3c4 \uc798 \uad00\ub9ac\ud558\uc9c0 \ubabb\ud55c \uac83 \uac19\ub2e4. \\n\ubbf8\uc158\uc5d0 \uc798 \uc9d1\uc911\ud558\uc9c0 \ubabb\ud574\uc11c \ud398\uc5b4\uc5d0\uac8c \ub9ce\uc774 \ubbf8\uc548\ud588\uace0, \ub098 \uc790\uc2e0\uc5d0\uac8c \uc544\uc26c\uc6e0\ub358 \ubd80\ubd84\uc774 \ub9ce\uc558\ub2e4. \\n\\n\uc9c0\ub09c\ubc88 \ud68c\uace0\ub97c \ub2e4\uc2dc \ubcf4\ub294\ub370 \uc9d1\uc911\uc744 \uc798 \ubabb\ud55c \uacbd\uc6b0\uac00 \ub9ce\uc740 \uac83 \uac19\ub2e4. \\n\ub3c4\uc804\uc801\uc774\uc9c0 \uc54a\uac70\ub098 \uc2dc\uac04\uc774 \ubd80\uc871\ud558\uc9c0 \uc54a\uc73c\uba74 \uc9d1\uc911\uc744 \uc798 \ubabb\ud558\ub294 \uac83 \uac19\ub2e4. \\n\uba38\ub9bf\uc18d\uc5d0\uc11c \uc2dc\uac04\uc801 \uc5ec\uc720\uac00 \uc788\ub2e4\uace0 \uc0dd\uac01\ud560 \ub54c\uac00 \uac00\uc7a5 \uc704\ud5d8\ud55c \uc21c\uac04\uc778 \uac83 \uac19\ub2e4. \\n\\n\ud568\uaed8 \uc790\ub77c\uae30\uc5d0\uc11c \ub098\uc628 `\ub09c\uc774\ub3c4 \ub192\uc774\uae30`\uac00 \ud544\uc694\ud574\uc9c0\ub294 \uc21c\uac04\uc774\ub2e4. \\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**\uc911\uc694\ub3c4\uac00 \uc788\ub294 \uc5b4\ub178\ud14c\uc774\uc158\ubd80\ud130 \ud074\ub798\uc2a4 \uc774\ub984\uc5d0 \uac00\uae5d\uac8c \uba85\uc2dc\ud558\uae30**\\n\\n```java\\n@SuppressWarnings(\\"NonAsciiCharacters\\")\\n@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)\\n@Transactional\\n@AutoConfigureMockMvc\\n@SpringBootTest\\npublic class RacingGameIntegrationTest {\\n```\\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n**\ube44\ubc84\uc758 \uc131\uaca9** \\n\ube44\ubc84\uac00 \uc131\uaca9\uc774 \uc88b\uc544\uc11c \ud3b8\ud558\uac8c \ud398\uc5b4\ub97c \ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc131\uae09\ud558\uc9c0 \uc54a\uace0 \uc5ec\uc720\ub85c\uc6cc\uc11c \uc88b\uc558\ub2e4. \\n\\n**\ubbf8\uc158\uc5d0 \uc9d1\uc911\ud558\ub294 \ubd80\ubd84** \\n\ub0b4\uac00 \ubbf8\uc158\uc5d0 \uc798 \uc9d1\uc911\ud558\uc9c0 \ubabb\ud588\ub294\ub370\ub3c4 \uac19\uc774 \ud398\uc5b4\ub97c \uc798 \uc9c4\ud589\ud55c \uac83 \uac19\uc544\uc11c \uc88b\uc558\ub2e4. \\n\ube44\ubc84\uac00 \ubbf8\uc158\uc5d0 \uc798 \uc9d1\uc911\ud574\uc11c \uadf8\ub807\uc9c0 \uc54a\uc558\ub098 \uc0dd\uac01\ud588\ub2e4. \\n\uadfc\uc721\ub9e8 \ube44\ubc84\ub77c \uadf8\ub7f0\uc9c0 \uccb4\ub825\uc774 \uc88b\uc544\uc11c \uadf8\ub7f0\uac00? \\n\uc911\uac04\uc5d0 \uc798 \uc548 \uc26c\uace0\ub3c4 \uc9d1\uc911\ud574\uc11c \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\ub294 \uac78 \ubcf4\uace0 \ub300\ub2e8\ud558\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\\n**\ud559\uc2b5\uc5d0 \ub300\ud55c \uc5f4\uc815** \\n\ucd94\uac00\uc801\uc73c\ub85c \uc54c\uace0 \uc2f6\uc740 \ubd80\ubd84\uc744 \ub530\ub85c \ud559\uc2b5\ud558\ub294 \uc5f4\uc815\uc774 \uc88b\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\ube44\ubc84\uc640 \uc2a4\ud504\ub9c1\uc5d0 \ub300\ud574 \uc54c\uc544\uac00\ub294 \uc2dc\uac04\uc744 \ub9ce\uc774 \uac00\uc9c4 \ubd80\ubd84\uc774 \ub9e4\uc6b0 \uc88b\uc558\ub2e4. \\n\ub098\ub3c4 5\uc6d4\ubd80\ud130 \uc870\uae08 \ub354 \ud654\uc774\ud305 \ud574\uc57c\uaca0\ub2e4."},{"id":"tecochat-retrospective-2","metadata":{"permalink":"/tecochat-retrospective-2","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-05-01-\ud14c\ucf54\ucc57 2. \ubc30\ud3ec.mdx","source":"@site/blog/2023-2/2023-05-01-\ud14c\ucf54\ucc57 2. \ubc30\ud3ec.mdx","title":"[\ud14c\ucf54\ucc57] 2. \ubc30\ud3ec","description":"\ud504\ub860\ud2b8\uc5d4\ud2b8","date":"2023-05-01T00:00:00.000Z","formattedDate":"2023\ub144 5\uc6d4 1\uc77c","tags":[{"label":"TecoChat","permalink":"/tags/teco-chat"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":4.67,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ud14c\ucf54\ucc57] 2. \ubc30\ud3ec","slug":"tecochat-retrospective-2","tags":["TecoChat","Retrospective"]},"prevItem":{"title":"\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0","permalink":"/web-racing-car-retrospective"},"nextItem":{"title":"Jenkins\ub85c CI/CD \uc124\uc815","permalink":"/jenkins"}},"content":"### \ud504\ub860\ud2b8\uc5d4\ud2b8\\n\\n\ub2c9\ub124\uc784\uc744 \uc785\ub825\ud558\uc5ec \uac04\ub2e8\ud788 \ub85c\uadf8\uc778\ud558\ub294 \ud654\uba74, \ucc44\ud305 \ubaa9\ub85d\uc744 \ubcf4\uc5ec\uc8fc\ub294 \ud654\uba74\ub3c4 \ub9cc\ub4e4\uc5c8\uace0 \ub2e8\uc77c \ucc44\ud305\uc744 \ud655\uc778\ud560 \uc218 \uc788\ub294 \ud654\uba74\ub3c4 \ub9cc\ub4e4\uc5c8\ub2e4. \\n\ucd94\uac00\ub85c \ucc44\ud305\uc744 \uc774\uc5b4\ub098\uac08 \uc218 \uc788\uac8c \ud558\ub294 \uae30\ub2a5\ub3c4 \ucd94\uac00\ud588\ub2e4. \\n\uc790\uc798\ud558\uac8c \uc2e0\uacbd \uc4f8 \ubd80\ubd84\uc774 \ub9ce\uc544\uc11c, \ud504\ub860\ud2b8\uc5d4\ub4dc \ud558\ub294 \uc0ac\ub78c\ub4e4\uc774 \ub300\ub2e8\ud558\ub2e4\uace0 \uc0dd\uac01\ub418\uc5c8\ub2e4. \\n\uc5ec\uc720\uac00 \ub41c\ub2e4\uba74 \uc790\uc2e0\uc758 \ucc44\ud305\uc744 \ubcfc \uc218 \uc788\ub294 \uae30\ub2a5\uc774\ub098, \ucc44\ud305\uc744 \uc774\uc5b4\uc11c \ud560 \uc218 \uc788\ub294 \uae30\ub2a5, \ub313\uae00 \uae30\ub2a5\ub3c4 \ucd94\uac00\ud560 \uc608\uc815\uc774\ub2e4. \\n\\n### \ubc31\uc5d4\ub4dc\\n\\n\ucd5c\ub300\ud55c \ube68\ub9ac \uc11c\ube44\uc2a4\ub97c \ud06c\ub8e8\ub4e4\uc5d0\uac8c \uc81c\uacf5\ud558\uae30\ub85c \uc815\ud574\uc11c, \ubc31\uc5d4\ub4dc\ub294 \ub9d0\ub791\uc774 \uc77c\ub2e8 \ub2e4 \ub9cc\ub4e4\uace0 \uc788\ub2e4. \\n\ub9d0\ub791\uc774 \ud55c \ubd80\ubd84\uc774 \ub108\ubb34 \ub9ce\uc544\uc11c \ub0b4\uac00 \ubabb \ub530\ub77c\uac00\ub294 \uac83 \uac19\ub2e4. \\n\ub098\uc911\uc5d0 \ubc31\uc5d4\ub4dc \ucf54\ub4dc\ub97c \uc774\ud574\ud558\ub294 \uc2dc\uac04\uc744 \uac00\uc838\uc57c\uaca0\ub2e4. \\n\\n### Http Request Header\\n\\n\uc544\uc9c1 \uc778\uc99d\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \ud558\uc9c0 \uc54a\uc544\uc11c \uc694\uccad \ud5e4\ub354\uc5d0 \uc774\ub984\uc744 \ubcf4\ub0b4\uae30\ub85c \ud588\ub2e4. \\n\ub9d0\ub791\uc774 \ud55c\uae00\uc740 \uc548\ub41c\ub2e4\uace0 \ub9d0\ud574\uc918\uc11c Base64\ub85c \uc778\ucf54\ub529\ud558\uace0, \ubc31\uc5d4\ub4dc\uc5d0\uc11c \ub514\ucf54\ub529 \ud558\uc5ec \uc0ac\uc6a9\ud558\uae30\ub85c \ud588\ub2e4. \\n\uc544\ub798\ub294 pinia\uc5d0 \uc788\ub294 name \uac12\uc744 \uc778\ucf54\ub529 \ud558\ub294 \ucf54\ub4dc\ub2e4. deprecated \ub418\uc5c8\ub2e4\ub294\ub370, \ub2e4\ub978 \ubc29\ubc95\uc744 \uc0ac\uc6a9\ud560 \uc904 \ubab0\ub77c\uc11c \uc77c\ub2e8 \uc774\uac78 \uc0ac\uc6a9\ud588\ub2e4. \\n\\n```ts\\nconst encodedName = () => {\\n const uriComponent = unescape(encodeURIComponent(name.value));\\n return btoa(uriComponent);\\n};\\n```\\n\\n### Elastic Beanstalk\\n\\n\uac00\uc7a5 \ube60\ub974\uac8c \ubc31\uc5d4\ub4dc\ub97c \ubc30\ud3ec\ud560 \uc218 \uc788\ub294 \ubc29\ubc95\uc774 \ubb58\uc9c0 \uace0\ubbfc\ud558\ub2e4\uac00 Elastic Beanstalk\ub97c \uc0ac\uc6a9\ud558\uae30\ub85c \ud588\ub2e4. \\nElastic Beanstalk\ub97c \uc0ac\uc6a9\ud558\uba74 \uc778\ud504\ub77c\uc5d0 \ub300\ud574 \uc798 \uc54c\uc9c0 \ubabb\ud574\ub3c4 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \ube60\ub974\uac8c \ubc30\ud3ec\ud558\uace0 \uad00\ub9ac\ud560 \uc218 \uc788\ub2e4. \\n\ubaa8\ub2c8\ud130\ub9c1, \ub85c\uae45, \ub85c\ub4dc \ubc38\ub7f0\uc2f1 \ub4f1 \ub2e4\uc591\ud55c \uae30\ub2a5\uc744 \uc81c\uacf5\ud55c\ub2e4. \\n\\n### Elastic Beanstalk RDS \uc124\uc815 \ud6c4 \ubd84\ub9ac\\n\\n\ucd08\uae30 \uc124\uc815 \uc2dc RDS\ub97c \uc5f0\uacb0\ud558\uace0 \uc124\uc815 \uc644\ub8cc \ud6c4 \ubd84\ub9ac\ud55c\ub2e4\uba74, Beanstalk \uc778\uc2a4\ud134\uc2a4 -> RDS \uc694\uccad \uc2dc \uc778\ubc14\uc6b4\ub4dc \uc124\uc815\uc744 \uc548 \ud574\ub3c4 \ub41c\ub2e4. \\nRDS \ubd84\ub9ac \uc2dc Beanstalk\uc5d0 \uae30\ubcf8\uc801\uc73c\ub85c \uc124\uc815\ub418\uc5b4 \uc788\ub294 RDS_HOSTNAME, RDS_PORT, RDS_USERNAME, RDS_PASSWORD\uc640 \uac19\uc740 \ud658\uacbd \ubcc0\uc218\uac00 \uac19\uc774 \uc81c\uac70\ub41c\ub2e4. \\n\ucd94\uac00\ub85c Elastic Beanstalk\ub85c RDS\ub97c \uc124\uc815\ud558\uba74 \uae30\ubcf8 \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uba85\uc740 ebdb\ub2e4. \\n\\n### Elastic Beanstalk nginx \uc124\uc815\\n\\n\uc5c5\ub85c\ub4dc\ud558\ub294 zip \ud30c\uc77c \ub0b4\ubd80\uc5d0 `.platform/nginx/conf.d/` \uacbd\ub85c\uc5d0 \uc124\uc815 \ud30c\uc77c\uc744 \ucd94\uac00\ud558\uba74 nginx \uc124\uc815\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\\n### Jenkins\\n\\n\ubc31\uc5d4\ub4dc \ucf54\ub4dc\ub97c \uc77c\uc77c\ud788 \ubc30\ud3ec\ud558\uae30 \ubd88\ud3b8\ud574\uc11c Jenkins\ub97c \uc774\uc6a9\ud558\uc5ec Repository\uc5d0 \ucf54\ub4dc\ub97c push \ud560 \ub54c \uc790\ub3d9\uc73c\ub85c \ubc30\ud3ec\uac00 \ub418\uac8c \uc124\uc815\ud558\uae30\ub85c \ud588\ub2e4. \\n\uc791\ub144\uc5d0 \ud655\uc778\ud588\uc744 \ub550 2022\ub144 12\uc6d4 31\uc77c\uae4c\uc9c0 EC2 ARM \uae30\ubc18 t4g.small\uc774 \ubb34\ub8cc\uc600\ub294\ub370, \ub2e4\uc2dc \ub4e4\uc5b4\uac00 \ubcf4\ub2c8 2023\ub144\uae4c\uc9c0 12\uc6d4 31\uc77c\uae4c\uc9c0 t4g.small\uc744 \ubb34\ub8cc\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\uc5c8\ub2e4. \\nt4g.small\uc740 \ub7a8\uc774 2G\uc778\ub370, \uc608\uc804\uc5d0\ub294 \ubd80\uc871\ud558\uc9c0 \uc54a\uc558\ub2e4\uace0 \uc0dd\uac01\ud588\ub294\ub370 Java 17\uc744 \uc368\uc11c \uadf8\ub7f0\uac00 \ube4c\ub4dc \ud560 \ub54c \ub7a8\uc774 \ub9ce\uc774 \ubd80\uc871\ud55c \uac83 \uac19\uc544\uc11c Swap \uba54\ubaa8\ub9ac 2\uae30\uac00\ub97c \ucd94\uac00\ub85c \uc124\uc815\ud588\ub2e4. \\n\ucd94\uac00\ub85c build.gradle\uc5d0\uc11c \uc544\ub798\uc640 \uac19\uc774 \uc124\uc815\ud55c\ub2e4\uba74 \ud14c\uc2a4\ud2b8 \uc2dc \uc0ac\uc6a9\ud558\ub294 \ub7a8\uc744 \ub298\ub9b4 \uc218 \uc788\ub2e4. \uae30\ubcf8\uac12\uc740 512MB\ub77c\uace0 \ud55c\ub2e4. \\n\\n```groovy\\ntest {\\n maxHeapSize = \\"1024m\\"\\n}\\n```\\n\\n### Jenkins Blue Ocean\\n\\nBlue Ocean\uc740 Jenkins Pipeline\uc744 \uad6c\uc131\ud558\ub294 \ub370\uc5d0 \uc788\uc5b4 \ud3b8\ub9ac\ud558\uac8c \ud574\uc8fc\ub294 \ub3c4\uad6c\ub2e4. \\n\uc2dc\uac01\ud654\ub3c4 \uc798 \ub418\uc5b4\uc788\uace0, \uc124\uc815\ub3c4 \ud3b8\ub9ac\ud55c \uac83 \uac19\ub2e4. \\n\uc624\ub298 \uc801\uc6a9\ud574 \ubcf4\ub2c8 \ub7a8\uc774 \ubd80\uc871\ud558\uc5ec \uc911\uac04\uc5d0 \uc798 \uc548\ub418\uae30\ub3c4 \ud558\uace0 \uadf8\ub798\uc11c \uadf8\ub0e5 \\"Pipeline\ub9cc \uc0ac\uc6a9\ud560 \uac78 \uadf8\ub7ac\ub098?\\" \ub77c\ub294 \uc0dd\uac01\uc774 \ub4e0\ub2e4. \\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n[Elastic Beanstalk, AWS](https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/Welcome.html) \\n[EC2 AWS Graviton, AWS](https://aws.amazon.com/ko/ec2/graviton/) \\n[Default Memory Settings, AWS](https://docs.gradle.org/current/userguide/upgrading_version_4.html#rel5.0:default_memory_settings)"},{"id":"jenkins","metadata":{"permalink":"/jenkins","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815.mdx","source":"@site/blog/2023-2/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815.mdx","title":"Jenkins\ub85c CI/CD \uc124\uc815","description":"\uc124\uc815 \ud658\uacbd","date":"2023-04-30T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 30\uc77c","tags":[{"label":"Jenkins","permalink":"/tags/jenkins"},{"label":"Elastic Beanstalk","permalink":"/tags/elastic-beanstalk"}],"readingTime":7.495,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"Jenkins\ub85c CI/CD \uc124\uc815","slug":"jenkins","tags":["Jenkins","Elastic Beanstalk"]},"prevItem":{"title":"[\ud14c\ucf54\ucc57] 2. \ubc30\ud3ec","permalink":"/tecochat-retrospective-2"},"nextItem":{"title":"[\ud14c\ucf54\ucc57] 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30","permalink":"/tecochat-retrospective-1"}},"content":"### \uc124\uc815 \ud658\uacbd\\n\\n\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc774\ubbf8\uc9c0: Amazon Linux 2023 AMI \\n\uc544\ud0a4\ud14d\uccd0: ARM \\n\uc778\uc2a4\ud134\uc2a4 \uc720\ud615: t4g.small \\n\ud658\uacbd \uad6c\uc131\uc774 \uc644\ub8cc\ub41c Elastic Beanstalk \\n\ub2e8\uc77c Spring Boot \ud504\ub85c\uc81d\ud2b8\uac00 \uc874\uc7ac\ud558\ub294 Github Repository\\n\\n### \\\\[EC2 CLI\\\\] Swap \uba54\ubaa8\ub9ac \uc124\uc815\\n\\nt4g.small\uc774 \ub7a8\uc774 2G\uc778\ub370 \ub7a8\uc774 \ubd80\uc871\ud558\ub2e4\uace0 \ub290\uaef4\uc838\uc11c swap \uba54\ubaa8\ub9ac\ub97c \uc124\uc815\ud588\ub2e4. \\n\uc544\ub798 \uba85\ub839\uc5b4\ub97c \ub530\ub77c swap \uba54\ubaa8\ub9ac\ub97c \uc124\uc815\ud558\uace0 free -h \uba85\ub839\uc5b4\ub97c \ud1b5\ud574 \uc798 \uc124\uc815\ub418\uc5c8\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n``` bash\\n# fallocate \uc774\uc6a9\ud558\uc5ec \uc2a4\uc651 \ud30c\uc77c \uc0dd\uc131\\nsudo fallocate -l 2G /swapfile\\n\\n# \uad8c\ud55c \uc124\uc815\\nsudo chmod 600 /swapfile\\n\\n# \ud30c\uc77c\uc744 Swap \ud3ec\ub9f7\uc73c\ub85c \ubcc0\uacbd \ud6c4 \uc2dc\uc2a4\ud15c\uc5d0 \ub4f1\ub85d\\nsudo mkswap /swapfile\\nsudo swapon /swapfile\\n\\n# Swap \uba54\ubaa8\ub9ac \ubd80\ud305\uc2dc \uc790\ub3d9\uc73c\ub85c \ub9c8\uc6b4\ud2b8\ud558\ub3c4\ub85d \uc801\uc6a9\\n# \ucd5c\ud558\ub2e8\uc5d0 \ub2e4\uc74c \uad6c\ubb38 \uc124\uc815 -> /swapfile swap swap defaults 0 0\\nsudo vim /etc/fstab\\n```\\n\\n\\n### \\\\[EC2 CLI\\\\] jenkins \uc124\uce58\\n\\n```bash\\nsudo wget -O /etc/yum.repos.d/jenkins.repo \\\\\\n https://pkg.jenkins.io/redhat-stable/jenkins.repo\\nsudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key\\nsudo yum upgrade\\nsudo yum install java-17-amazon-corretto-devel\\nsudo yum install jenkins\\nsudo systemctl daemon-reload\\n```\\n\\n[Jenkins \uacf5\uc2dd \ud648\ud398\uc774\uc9c0](https://www.jenkins.io/doc/book/installing/linux/#red-hat-centos) \ub97c \ucc38\uace0\ud558\uc5ec \uc124\uce58\ud558\ub294 \uac8c \uc88b\ub2e4.\\n\\n### \\\\[EC2 CLI\\\\] Jenkins \uc2dc\uc791\\n\\n```bash\\nsudo systemctl enable jenkins\\nsudo systemctl start jenkins\\n```\\n\\nenable\ub85c \uc124\uc815\ud558\uc5ec \ubd80\ud305\uc2dc \uc790\ub3d9\uc2dc\uc791 \ub418\ub3c4\ub85d \uc124\uc815\ud55c\ub2e4.\\n\\n### \\\\[EC2 CLI\\\\] nginx & git \uc124\uce58\\n\\n```bash\\nsudo yum install nginx\\nsudo systemctl enable nginx\\nsudo systemctl start nginx\\n\\nsudo yum install git\\n```\\n\\nnginx\uc640 \ucf54\ub4dc\ub97c \ubd88\ub7ec\uc62c \ub54c \uc0ac\uc6a9\ud560 git\uc744 \uc124\uce58\ud55c\ub2e4.\\n\\n### \\\\[EC2 CLI\\\\] nginx \ub9ac\ubc84\uc2a4 \ud504\ub85d\uc2dc \uc124\uc815\\n\\n\uc544\ub798 \uc124\uc815 \ud30c\uc77c\uc740 \uacf5\uc2dd \ud648\ud398\uc774\uc9c0\uc5d0\uc11c \uc548\ub0b4\ud55c \uae30\ubcf8\uc801\uc778 \uc124\uc815 \ud30c\uc77c\uc774\ub2e4.\\n\\n```bash\\nupstream jenkins {\\n keepalive 32; # keepalive connections\\n server 127.0.0.1:8080; # jenkins ip and port\\n}\\n\\n# Required for Jenkins websocket agents\\nmap $http_upgrade $connection_upgrade {\\n default upgrade;\\n \'\' close;\\n}\\n\\nserver {\\n listen 80; # Listen on port 80 for IPv4 requests\\n\\n server_name jenkins.example.com; # replace \'jenkins.example.com\' with your server domain name\\n\\n # this is the jenkins web root directory\\n # (mentioned in the output of \\"systemctl cat jenkins\\")\\n root /var/run/jenkins/war/;\\n\\n access_log /var/log/nginx/jenkins.access.log;\\n error_log /var/log/nginx/jenkins.error.log;\\n\\n # pass through headers from Jenkins that Nginx considers invalid\\n ignore_invalid_headers off;\\n\\n location ~ \\"^/static/[0-9a-fA-F]{8}\\\\/(.*)$\\" {\\n # rewrite all static files into requests to the root\\n # E.g /static/12345678/css/something.css will become /css/something.css\\n rewrite \\"^/static/[0-9a-fA-F]{8}\\\\/(.*)\\" /$1 last;\\n }\\n\\n location /userContent {\\n # have nginx handle all the static requests to userContent folder\\n # note : This is the $JENKINS_HOME dir\\n root /var/lib/jenkins/;\\n if (!-f $request_filename){\\n # this file does not exist, might be a directory or a /**view** url\\n rewrite (.*) /$1 last;\\n break;\\n }\\n sendfile on;\\n }\\n\\n location / {\\n sendfile off;\\n proxy_pass http://jenkins;\\n proxy_redirect default;\\n proxy_http_version 1.1;\\n\\n # Required for Jenkins websocket agents\\n proxy_set_header Connection $connection_upgrade;\\n proxy_set_header Upgrade $http_upgrade;\\n\\n proxy_set_header Host $host;\\n proxy_set_header X-Real-IP $remote_addr;\\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\\n proxy_set_header X-Forwarded-Proto $scheme;\\n proxy_max_temp_file_size 0;\\n\\n #this is the maximum upload size\\n client_max_body_size 10m;\\n client_body_buffer_size 128k;\\n\\n proxy_connect_timeout 90;\\n proxy_send_timeout 90;\\n proxy_read_timeout 90;\\n proxy_buffering off;\\n proxy_request_buffering off; # Required for HTTP CLI commands\\n proxy_set_header Connection \\"\\"; # Clear for keepalive\\n }\\n\\n}\\n```\\n\\nJenkins\ub294 8080 \ud3ec\ud2b8\ub85c \ub3d9\uc791\ud558\uae30 \ub54c\ubb38\uc5d0 \ub9ac\ubc84\uc2a4 \ud504\ub85d\uc2dc\ub97c \uc124\uc815\ud574\uc900\ub2e4. \\n`/etc/nginx/conf.d`\xa0\uc544\ub798\xa0`default.conf`\xa0\ud30c\uc77c\uc744 \ud558\ub098 \uc0dd\uc131\ud558\uace0 \uc704\uc640 \uac19\uc774 \uc785\ub825\ud558\uace0 \uc800\uc7a5\ud55c\ub2e4. \\nnginx\uc758 \uae30\ubcf8 \uc124\uc815 \ud30c\uc77c\uc5d0 \uc874\uc7ac\ud558\ub294\xa0`include /etc/nginx/conf.d/*.conf;`\xa0\uc124\uc815 \ub54c\ubb38\uc5d0\xa0`.conf`\xa0\ub85c \ub05d\ub09c\ub2e4\uba74 \uc124\uc815\uc774 \uc801\uc6a9\ub41c\ub2e4. \\n\uc124\uc815 \ud6c4\xa0`sudo nginx -t`\ub85c \uc124\uc815\ud30c\uc77c\uc774 \uc815\uc0c1\uc778\uc9c0 \ud655\uc778\ud558\uace0,\xa0`sudo systemctl restart nginx`\xa0\uba85\ub839\uc5b4\ub85c nginx\ub97c \uc7ac\uc2dc\uc791\ud55c\ub2e4. \\n\\n### \\\\[Jenkins\\\\] Jenkins \uc811\uc18d\\n\\nJenkins\ub97c \uc124\uce58\ud55c EC2 \uc778\uc2a4\ud134\uc2a4 \uc778\ubc14\uc6b4\ub4dc \uc124\uc815\uc5d0 80\ubc88 \ud3ec\ud2b8\uac00 \uc5f4\ub824\uc788\ub294\uc9c0 \ud655\uc778\ud55c\ub2e4. \\nEC2\uc758 \uc544\uc774\ud53c \uc8fc\uc18c\ub97c \uc785\ub825\ud558\uace0 \ub4e4\uc5b4\uac00\uba74 \ube44\ubc00\ubc88\ud638\ub97c \uc785\ub825\ud558\ub77c\ub294 \ucc3d\uc774 \ub098\uc628\ub2e4.\\n\\n![jenkins-start](./jenkins-start.png)\\n\\n\ucd08\uae30 \ube44\ubc00\ubc88\ud638\ub97c \uc785\ub825\ud574\uc57c \ud558\ub294\ub370 `sudo cat /var/lib/jenkins/secrets/initialAdminPasswor` \ub97c \uc785\ub825\ud574 \ucd08\uae30 \ube44\ubc00\ubc88\ud638\ub97c \uc5bb\uc744 \uc218 \uc788\ub2e4. \\n\ube44\ubc00\ubc88\ud638\ub97c \uc785\ub825\ud558\uba74 \ud50c\ub7ec\uadf8\uc778 \uc124\uc815 \ucc3d\uc774 \ub098\uc62c\ud150\ub370 `install suggested plugins`\uc744 \ud074\ub9ad\ud558\uc5ec Jenkins\uac00 \ucd94\ucc9c\ud558\ub294 \uae30\ubcf8 \ud50c\ub7ec\uadf8\uc778\ub4e4\uc744 \uc124\uce58\ud558\uba74 \ub41c\ub2e4. \\n\ud50c\ub7ec\uadf8\uc778\uc744 \uc124\uce58\ud558\uba74 \uacc4\uc815 \ubc0f \uc8fc\uc18c \uc124\uc815\uc744 \ud574\uc57c\ud558\ub294\ub370 \uc774\uac74 \ud3b8\ud558\uac8c \uc124\uc815\ud558\uba74 \ub41c\ub2e4. \\n\\n### \\\\[Jenkins\\\\] Jenkins Blue Ocean \uc124\uce58\\n\\nJenkins \uad00\ub9ac \u2192 Plugin Manager\uc5d0\uc11c Blue Ocean\uc744 \uac80\uc0c9\ud574 \uc124\uce58\ud55c\ub2e4.\\n\\n### \\\\[AWS IAM & EC2\\\\] IAM\uc73c\ub85c EC2 \uc778\uc2a4\ud134\uc2a4 \uad8c\ud55c \uc124\uc815\ud558\uae30\\n\\nS3\uc640 Elastic Beanstalk\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\ub294 \uad8c\ud55c\uc744 \ubd80\uc5ec\ud558\ub824\uba74 AmazonS3FullAccess, AdministratorAccess-AWSElasticBeanstalk \ub450 \uac1c\uc758 \uc815\ucc45\uc744 \uac00\uc9c0\uace0 \uc788\ub294 \uc5ed\ud560\uc744 \uc0dd\uc131\ud574\uc57c \ud55c\ub2e4. \\nIAM\uc5d0\uc11c \ub2e4\uc74c\uacfc \uac19\uc774 \uc5ed\ud560\uc744 \ud558\ub098 \uc0c8\ub85c \uc0dd\uc131\ud55c\ub2e4.\\n\\n1. \uc5d4\ud130\ud2f0 \uc120\ud0dd\\n\\n![aws-iam1](./aws-iam1.png)\\n\\n2. \uad8c\ud55c \ucd94\uac00\\n\\n![aws-iam2](./aws-iam2.png)\\n\\n3. \uc774\ub984 \uc9c0\uc815, \uac80\ud1a0 \ubc0f \uc0dd\uc131\\n\\n![aws-iam3](./aws-iam3.png)\\n\\n4. \uc0dd\uc131\ud55c IAM EC2 Jenkins \uc778\uc2a4\ud134\uc2a4\ub97c \uc120\ud0dd\ud558\uace0, \uc791\uc5c5 \u2192 \ubcf4\uc548 \u2192 IAM \uc5ed\ud560 \uc218\uc815\uc744 \ub20c\ub7ec Role \uc124\uc815\\n\\n![aws-iam4](./aws-iam4.png)\\n\\n### \\\\[AWS S3\\\\] Jar \ud30c\uc77c\uc744 \uc5c5\ub85c\ub4dc \ud560 S3 \ubc84\ud0b7 \uc0dd\uc131\\n\\n\ubc84\ud0b7\uc744 \uc0dd\uc131\ud560 \ub54c \ub2e4\uc74c \uc124\uc815\uc744 \uc81c\uc678\ud558\uace0 \ubaa8\ub450 \ucc28\ub2e8 \ud65c\uc131\ud654\ub97c \ud574\uc900\ub2e4.\\n\\n- `\uc0c8 ACL(\uc561\uc138\uc2a4 \uc81c\uc5b4 \ubaa9\ub85d)\uc744 \ud1b5\ud574 \ubd80\uc5ec\ub41c \ubc84\ud0b7 \ubc0f \uac1d\uccb4\uc5d0 \ub300\ud55c \ud37c\ube14\ub9ad \uc561\uc138\uc2a4 \ucc28\ub2e8`\\n\\n![aws-s3](./aws-s3.png)\\n\\n### \\\\[Github\\\\] Blue Ocean\uc5d0\uc11c \ud30c\uc774\ud504\ub77c\uc778 \uc0dd\uc131\uc5d0 \ud544\uc694\ud55c Github Token \uc0dd\uc131\\n\\nrepo, user:email \uad8c\ud55c\uc774 \uc788\ub294 \ud1a0\ud070\uc774 \ud544\uc694\ud558\ub2e4. \\n\\n### \\\\[Jenkins\\\\] \ube14\ub8e8 \uc624\uc158 \uc2dc\uc791\\n\\n![jenkins-blue-ocean1](./jenkins-blue-ocean1.png)\\n\\n\ube14\ub8e8 \uc624\uc158 \uc5f4\uae30\ub85c \ud30c\uc774\ud504\ub77c\uc778\uc744 \uc0dd\uc131\ud55c\ub2e4. \\n\ud1a0\ud070 \uc785\ub825 \u2192 \uc870\uc9c1 \uc120\ud0dd \u2192 CI/CD \uc124\uc815\ud560 Repository \uc120\ud0dd\uc744 \ud558\uba74 \ud30c\uc774\ud504\ub77c\uc778 \ucc3d\uc73c\ub85c \ub118\uc5b4\uac04\ub2e4. \\nJenkinsfile\uc744 \uc9c1\uc811 \uc791\uc131\ud558\uc5ec \uc124\uc815\ud558\uae30 \uc704\ud574 \uac04\ub2e8\ud558\uac8c print \ud558\ub098 \ucd9c\ub825\ud558\ub294 \uac83\uc73c\ub85c \uc124\uc815\ud588\ub2e4. \\n\\n![jenkins-blue-ocean2](./jenkins-blue-ocean2.png)\\n\\n\ud30c\uc774\ud504\ub77c\uc778\uc774 \uc2e4\ud589\ub420 \ud150\ub370 pipeline status\uc5d0\uc11c \uc544\ub798\uc640 \uac19\uc774 \ucd08\ub85d\ubd88\uc774 \ub728\uba74 \ub41c\ub2e4.\\n\\n![jenkins-blue-ocean3](./jenkins-blue-ocean3.png)\\n\\n### \\\\[Github Repsoitory\\\\] Jenkinsfile \uc124\uc815\\n\\n\ube14\ub8e8 \uc624\uc158 \uc2dc\uc791\uc744 \ud1b5\ud574 \uc124\uc815\ud558\uba74 Jenkinsfile\uc774 \ud558\ub098 \ub9cc\ub4e4\uc5b4\uc9c0\uace0, \uc544\ub798\uc640 \uac19\uc774 \uc6d0\ud558\ub294 \ud30c\uc774\ud504\ub77c\uc778\uc744 \uc124\uc815\ud55c\ub2e4.\\n\\n```bash\\npipeline {\\n agent any\\n stages {\\n stage(\'build and test\') {\\n steps {\\n sh \'/gradlew clean build\'\\n }\\n }\\n stage(\'zip\') {\\n steps {\\n sh \'mv ./build/libs/woowachat.jar .\'\\n sh \'zip -r woowachat.zip .platform delivery.jar Procfile\'\\n }\\n }\\n stage(\'upload\') {\\n steps {\\n sh \'aws s3 cp woowachat.zip s3://woowa-chat/woowachat.zip --region ap-northeast-2\'\\n }\\n }\\n stage(\'deploy\') {\\n steps {\\n sh \'aws elasticbeanstalk create-application-version --region ap-northeast-2 --application-name woowachat --version-label ${BUILD_TAG} --source-bundle S3Bucket=\\"woowa-chat\\",S3Key=\\"woowachat.zip\\"\'\\n sh \'aws elasticbeanstalk update-environment --region ap-northeast-2 --environment-name Woowachat-env --version-label ${BUILD_TAG}\'\\n }\\n }\\n }\\n}\\n```\\n\\n### \\\\[Github\\\\] Webhooks \uc124\uc815\\n\\n![github-hook](./github-hook.png)\\n\\npush \uc774\ubca4\ud2b8\uac00 \ubc1c\uc0dd\ud560 \ub54c `http://Jenkins\uc8fc\uc18c/github-webhook/` \ub85c post request\ub97c \ud558\ub3c4\ub85d \uc6f9\ud6c5\uc744 \uc124\uc815\ud55c\ub2e4.\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n[Install Jenkins - CentOS, Jenkins](https://www.jenkins.io/doc/book/installing/linux/#red-hat-centos) \\n[Nginx Reverse Proxy Configuration, Jenkins](https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-nginx/) \\n[Amazon Corretto 17 JDK Install, AWS](https://docs.aws.amazon.com/corretto/latest/corretto-17-ug/amazon-linux-install.html) \\n[Amazon Linux 2023 packages, AWS](https://docs.aws.amazon.com/linux/al2023/release-notes/all-packages-al2023-20230419.html)"},{"id":"tecochat-retrospective-1","metadata":{"permalink":"/tecochat-retrospective-1","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30.mdx","source":"@site/blog/2023-2/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30.mdx","title":"[\ud14c\ucf54\ucc57] 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30","description":"4\uc6d4 21\uc77c \uae08\uc694\uc77c","date":"2023-04-22T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 22\uc77c","tags":[{"label":"TecoChat","permalink":"/tags/teco-chat"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":5.68,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ud14c\ucf54\ucc57] 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30","slug":"tecochat-retrospective-1","tags":["TecoChat","Retrospective"]},"prevItem":{"title":"Jenkins\ub85c CI/CD \uc124\uc815","permalink":"/jenkins"},"nextItem":{"title":"[\ucc45] \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c","permalink":"/book-leadership-and-self-deception"}},"content":"### 4\uc6d4 21\uc77c \uae08\uc694\uc77c\\n\\n\ub808\ubca8 2\ub97c \uc2dc\uc791\ud55c \ub4a4 \ub0b4\uac00 \ud559\uc2b5\uc5d0 \ub300\ud55c \ubc29\ud5a5\uc744 \uc783\uc5b4\ubc84\ub838\ub2e4\ub294 \uc0dd\uac01\uc774 \ub4e4\uc5c8\ub2e4. \\n\ub808\ubca8 3, 4\uc5d0\uc11c \ub098\ub9cc\uc758 \uac15\uc810\uc744 \uac00\uc9c0\uace0 \uc2f6\uc5b4 \uace0\ubbfc\uc744 \ub9ce\uc774 \ud588\ub2e4. \\n\ub2e8\uc21c\ud788 \uc2a4\ud504\ub9c1\uc744 \uae4a\uac8c \uacf5\ubd80\ud558\ub294 \uac74 \ud6a8\uc728\uc774 \ub9ce\uc774 \ub5a8\uc5b4\uc9c4\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uae00\uc4f0\uae30 \uc218\uc0c1\uc73c\ub85c \ubc1b\uc740 \ucfe0\ud3f0\uc744 \uc0ac\uc6a9\ud574 \ube0c\ub77c\uc6b4\uc5d0\uac8c \ucee4\ud53c\ucc57\uc744 \uc2e0\uccad\ud588\uace0, \uc0ac\uc774\ub4dc \ud504\ub85c\uc81d\ud2b8\ub97c \ud574\ubcf4\ub77c\ub294 \ub2f5\uc744 \ubc1b\uc558\ub2e4. \\n\\n\ub098\ub294 \uc544\uc774\ub514\uc5b4\ub97c \ubabb\ub0b4\ub294 \ud3b8\uc778\ub370 \ube0c\ub77c\uc6b4\uc774 \uc544\uc774\ub514\uc5b4\uae4c\uc9c0 \ub358\uc838\uc8fc\uc168\ub2e4. \\n`Chat-GPT \uc11c\ube44\uc2a4\ub97c \ud06c\ub8e8\ub4e4\uc5d0\uac8c \uc81c\uacf5\ud558\uace0, \ud574\ub2f9 \ud06c\ub8e8\ub4e4\uc774 \uc9c8\ubb38\ud55c \ub0b4\uc6a9\uc744 \uacf5\uc720\ud560 \uc218 \uc788\ub294 \uac74 \uc5b4\ub5a4\uc9c0?` \\n\\n\uae30\uc220\uc774 \ubaa9\uc801\uc778 \uc0ac\uc774\ub4dc \ud504\ub85c\uc81d\ud2b8\ub97c \uc9c4\ud589\ud558\uba74 \uc88b\uc744 \uac83 \uac19\ub2e4\ub294 \ub2f5\ubcc0\uc744 \ub4e4\uc5c8\uace0, \ud63c\uc790 \uc544\ub2c8\uba74 \ud398\uc5b4\ud560 \uc218 \uc788\uc744 \uc815\ub3c4\uc758 \uc778\uc6d0\uc73c\ub85c \uc9c4\ud589\ud558\uba74 \uc88b\uaca0\ub2e4\uace0 \ud558\uc168\ub2e4. \\n\ud504\ub860\ud2b8\ub791 \uac04\ub2e8\ud558\uac8c \ubc30\ud3ec\uae4c\uc9c0 \ud574\ubcf8 \uacbd\ud5d8\uc774 \uc788\uc5b4\uc11c \ud63c\uc790\ud574\ub3c4 \ud06c\uac8c \uc5b4\ub835\uc9c0 \uc54a\uc744 \uac83 \uac19\uc544\uc11c \ud63c\uc790 \ud558\uae30\ub85c \ub9c8\uc74c\uc744 \uba39\uc5c8\ub2e4. \\n\\n\uc774\uac74 \ubabb\ucc38\uc9c0\\n\\n### \ub3c4\uba54\uc778 \uad6c\uc785 \uc131\uacf5?\\n\\n\ucee4\ud53c\ucc57\uc774 \ub05d\ub098\uace0 \uc9d1\uc73c\ub85c \ub3cc\uc544\uac00\ub294 \uae38\uc5d0 \ubc14\ub85c \ub3c4\uba54\uc778\uc744 \uad6c\ub9e4\ud558\ub824\uace0 namecheap\uc5d0\uc11c \uc801\ub2f9\ud55c \ub3c4\uba54\uc778\uc774 \uc5c6\uc744\uae4c \uac80\uc0c9\uc744 \uacc4\uc18d\ud588\ub2e4. \\n\ub9c8\uce58 \uc5b4\ub9b4 \ub54c \ud588\ub358 \uac8c\uc784 \ub2c9\ub124\uc784 \uc815\ud558\ub294 \uac83\ucc98\ub7fc \uc2dc\uac04\uc774 \uc624\ub798 \uac78\ub838\ub2e4. \\ndev, io, chat \ub3c4\uba54\uc778\uc774 \ud6c4\ubcf4\uc600\uace0 \uc9d1 \uac00\ub294 \uae38\uc5d0 \uacb0\uc815\ub9cc \ud558\ub2e4\uac00 \uad6c\ub9e4\ud558\uc9c0 \ubabb\ud588\ub2e4.\\n\\n### \ub9d0\ub791\uc758 DM\\n\\n\uc9d1\uc5d0 \uac00\uc11c \ubc25\uc744 \uba39\uace0 \ub9d0\ub791\uc774\ub791 DM \ud558\ub2e4 \ud504\ub85c\uc81d\ud2b8\ub97c \uac19\uc774 \ud558\uc790\ub294 \uc774\uc57c\uae30\uac00 \ub098\uc654\ub2e4. \\n\uc6b0\ud14c\ucf54 \ucd5c\uace0 \uace0\uc218 \ub9d0\ub791\uc758 \uc694\uad6c\ub77c \uc218\ub77d\ud558\uc9c0 \uc54a\uc73c\uba74 \ud6c4\ud3ed\ud48d\uc744 \uac10\ub2f9\ud560 \uc218 \uc5c6\uc5c8\ub2e4. \\n\\n\uc774\ub7f0\uc800\ub7f0 \ub300\ud654\ub97c \ub098\ub204\ub2e4\uac00 \ub09c \ube60\ub974\uac8c \ud504\ub85c\ud1a0\ud0c0\uc785\uc744 \ub9cc\ub4e4\uc5b4 \ubcf4\uace0 \uc2f6\uc5b4\uc11c \ud504\ub860\ud2b8\ub97c \uad6c\ud604\ud55c\ub2e4\uace0 \ud588\uace0, \ub9d0\ub791\uc740 GPT api\ub97c \uc870\uc0ac\ud558\uae30\ub85c \ud588\ub2e4. \\n\ucd94\uac00\ub85c \ub3c4\uba54\uc778\uc5d0 \uad00\ud55c \uc774\uc57c\uae30\ub97c \ud558\ub2e4\uac00 woowachat\uc774 \uc5b8\uae09\ub418\uc5c8\uace0, namecheap\uc5d0\uc11c chat \ub3c4\uba54\uc778\uc744 \uc0ac\uc6a9\ud55c woowa.chat\uc73c\ub85c \uad6c\ub9e4\ud588\ub2e4. \\n\uc774\ud6c4\uc5d0 teco.chat\uc73c\ub85c \ubcc0\uacbd\ud588\ub2e4!\\n\\n### \ub3c4\uba54\uc778 \uc124\uc815 \ubc0f \ubc30\ud3ec\\n\\n\ud1a0\uc694\uc77c\uc5d0 \uad6c\ub9e4\ud55c \ub3c4\uba54\uc778\uc744 CDN, \ubcf4\uc548 \ub4f1 \ub2e4\uc591\ud55c \uae30\ub2a5\uc744 \uc81c\uacf5\ud558\ub294 Cloudflare\uc5d0 \ub3c4\uba54\uc778 \ub4f1\ub85d\uc744 \ud588\ub2e4. \\n\ub098\uc5d0\uac8c \uc775\uc219\ud55c Nuxt3\ub97c \uc0ac\uc6a9\ud558\uae30\ub85c \ud588\uace0, Cloudflare Pages\ub97c \uc774\uc6a9\ud558\uc5ec \ubc30\ud3ec\ud588\ub2e4. \\n\\n### GPT\\n\\n\ubb34\ub8cc \ud06c\ub808\ub527\uc744 \uc0ac\uc6a9\ud558\ub2c8 api limit\uc774 \uc788\uc5b4 \ubd84\ub2f9 3\ubc88\ubc16\uc5d0 \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc5c8\ub2e4. \\n\uc77c\ub2e8 \ubc31\uc5d4\ub4dc\ub97c \uad6c\ucd95\ud558\uae30 \uc804\uc5d0\ub294 \ubb34\ub8cc \ud06c\ub808\ub527\uc744 \uc0ac\uc6a9\ud560 \uc0dd\uac01\uc774\ub2e4. \\n\\n### Sonarcloud\\n\\n\uc815\uc801 \ucf54\ub4dc \ubd84\uc11d \ub3c4\uad6c\ub85c Sonarcloud\ub97c \uc801\uc6a9\ud588\ub2e4. \\nSonarcloud\ub294 SonarQube\uc758 SaaS \ubc84\uc804\uc774\uace0 \uc0ac\uc6a9\uc774 \ub9e4\uc6b0 \ud3b8\ud558\ub2e4. \\n\uc608\uc804\uc5d0 Sonarcloud\ub97c \uc0ac\uc6a9\ud560 \ub550 \ubc84\ud2bc \uba87 \ubc88 \ub204\ub974\uba74 \uc801\uc6a9\ud560 \uc218 \uc788\uc5c8\ub294\ub370, \uc774\ubc88\uc5d0\ub294 \ubc14\ub85c github action\uc744 \uc0ac\uc6a9\ud558\ub77c\ub294 \uc548\ub0b4 \ud398\uc774\uc9c0\ub85c \uc774\ub3d9\ud588\ub2e4. \\nSonarcloud\uac00 \uc790\uccb4\uc801\uc73c\ub85c github repository\uc5d0 push \ud558\uba74 \uc815\uc801 \ubd84\uc11d\uc744 \ud574\uc8fc\ub294 \uae30\ub2a5\uc744 \uc6d0\ud588\uace0, Administration -> Analysis Method\uc5d0 Automatic Analysis\ub97c \uc124\uc815\ud558\ub2c8 \ub418\uc5c8\ub2e4. \\n\ub108\ubb34 \uaf41\uaf41 \uc228\uaca8\uc838\uc788\ub124\\n\\n### Tiptap\\n\\n\ucf54\ub4dc \ud558\uc774\ub77c\uc774\ud305 \uae30\ub2a5\uc744 \ub123\uace0 \uc2f6\uc5b4\uc11c Tiptap\uc744 \uc0ac\uc6a9\ud588\ub2e4. \\nTiptap\uc740 Headless WYSIWYG \uc5d0\ub514\ud130\ub85c \uc0ac\uc6a9\uc790 \uc815\uc758 \uae30\ub2a5\uc5d0 \ud2b9\ud654\ub418\uc5b4\uc788\ub294 \uc5d0\ub514\ud130\ub2e4. \\n\uc544\uc9c1 Tiptap\uc774 \uc81c\uacf5\ud558\ub294 \ubaa8\ub4e0 \uae30\ub2a5\uc744 \uc790\uc5f0\uc2a4\ub7fd\uac8c \uc0ac\uc6a9\ud558\uc9c0\ub294 \ubabb\ud558\uc9c0\ub9cc CodeBlockLowlight \ud50c\ub7ec\uadf8\uc778\uc744 \uc0ac\uc6a9\ud558\uc5ec \ucf54\ub4dc \ube14\ub85d\uc744 \uc608\uc058\uac8c \ucd9c\ub825\ud560 \uc218 \uc788\uc5c8\ub2e4. \\napi \ubc18\ud658\uac12 \uadf8\ub300\ub85c tiptap\uc758 content\uc5d0 \uc124\uc815\ud588\ub354\ub2c8 \ucf54\ub4dc \ube14\ub85d\uc774 \uc124\uc815\ub418\uc9c0 \uc54a\uc544\uc11c \ubc31 \ud2f1 3\uac1c\ub97c `
`\ub85c \ubcc0\ud658\ud588\ub2e4.  \\n\ucd94\uac00\ub85c \ub744\uc5b4\uc4f0\uae30\ub3c4 \uc801\uc6a9\ub418\uc9c0 \uc54a\uc544\uc11c `\\\\n`\ub97c `
`\ud0dc\uadf8\ub85c \ubcc0\ud658\ud588\ub2e4. \\n\ubcc0\ud658\ud558\ub294 \ub85c\uc9c1\uc740 GPT\uc758 \ub3c4\uc6c0\uc744 \ub9ce\uc774 \ubc1b\uc558\ub2e4. \\n\\n```ts\\nconst replaceCodeFences = (input: String) => {\\n const codeFencesRegex = /```([\\\\w-]*)\\\\n([\\\\s\\\\S]*?)\\\\n```/g;\\n return input\\n .replace(codeFencesRegex, (match, p1, p2) => {\\n const languageClass = p1 ? ` class=\\"language-${p1}\\"` : \\"\\";\\n return `
${p2}
`;\\n })\\n .replace(/\\\\n/g, \\"
\\");\\n};\\n```\\n\\nTiptap\uc744 \uc801\uc6a9\ud558\ub2c8 \ub2e4\uc74c\uacfc \uac19\uc774 \uae54\ub054\ud55c \ucf54\ub4dc \ube14\ub85d\uc744 \ubcfc \uc218 \uc788\uc5c8\ub2e4. \\n\\n![tecochat](./teco-chat.png)\\n\\n### \ud3f0\ud2b8 \ubc0f favicon \uc801\uc6a9\\n\\n\ud0c0\uc774\ud2c0\uc740 \ubc30\ub2ec\uc758\ubbfc\uc871 \ub3c4\ud604\uccb4, \ub0b4\uc6a9\uc740 IBM Plex Sans\ub97c \uc0ac\uc6a9\ud588\ub2e4. \\n\ucd94\uac00\ub85c favicon\ub3c4 \uac04\ub2e8\ud558\uac8c \uc801\uc6a9\ud574\uc11c \ub9cc\uc871\uc2a4\ub7ec\uc6e0\ub2e4."},{"id":"book-leadership-and-self-deception","metadata":{"permalink":"/book-leadership-and-self-deception","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-08-\uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c.mdx","source":"@site/blog/2023-2/2023-04-08-\uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c.mdx","title":"[\ucc45] \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c","description":"\ucc45 \uc815\ubcf4","date":"2023-04-08T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 8\uc77c","tags":[{"label":"Book","permalink":"/tags/book"}],"readingTime":5.16,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ucc45] \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c","slug":"book-leadership-and-self-deception","tags":["Book"]},"prevItem":{"title":"[\ud14c\ucf54\ucc57] 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30","permalink":"/tecochat-retrospective-1"},"nextItem":{"title":"InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08","permalink":"/innodb-lock"}},"content":"### \ucc45 \uc815\ubcf4\\n\\n> \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c \\n> \uc544\ube48\uc800\uc5f0\uad6c\uc18c\\n> \\n\\n### \uc790\uae30\uae30\ub9cc\uacfc \uc790\uae30\ubc30\ubc18\\n\\n\ucc45\uc5d0\uc11c\ub294 \uc790\uae30\uae30\ub9cc\uacfc \uc790\uae30\ubc30\ubc18\uc5d0 \ub300\ud55c \ub0b4\uc6a9\uc744 \ub2e4\ub8ec\ub2e4. \\n- \uc790\uae30\uae30\ub9cc: \uc790\uc2e0\uc758 \ubb38\uc81c\ub97c \uc778\uc815\ud558\uc9c0 \uc54a\ub294 \uac83 \\n- \uc790\uae30\ubc30\ubc18: \ub2e4\ub978 \uc0ac\ub78c\uc744 \uc704\ud574 \ubb34\uc5b8\uac00 \ud574\uc57c\ub9cc \ud55c\ub2e4\ub294 \uc0dd\uac01\uc744 \ubc18\ud558\ub294 \ud589\uc704\\n\\n\uc790\uae30\ubc30\ubc18\uc744 \ud55c\ub2e4\uba74 \uc790\uae30\uae30\ub9cc \uc0c1\ud0dc\uac00 \ub41c\ub2e4. \\n\uc790\uae30\uae30\ub9cc \uc0c1\ud0dc\uc5d0 \ube60\uc9c0\ub294 \uac83\uc744 \ucc45\uc5d0\uc11c\ub294 \uc0c1\uc790 \uc548\uc5d0 \ub4e4\uc5b4\uac04\ub2e4\uace0 \ud45c\ud604\ud55c\ub2e4. \\n\\n### \uc77d\uace0 \ub098\uc11c\\n\\n\ucd5c\uadfc\uc5d0 \uc77d\uc740 \ucc45 \uc911 \uac00\uc7a5 \ub9c8\uc74c\uc774 \ubd88\ud3b8\ud588\ub2e4. \\n\uadf8\ub807\uae30\uc5d0 \ub354\ub354\uc6b1 \ub098\uc5d0\uac8c \ud544\uc694\ud55c \ub0b4\uc6a9\uc774 \ub2f4\uaca8\uc788\uc5c8\ub2e4. \\n\\n\uc0b4\uba74\uc11c \ub9ce\uc740 \uc120\ud0dd\uc758 \uc21c\uac04\uc774 \uc874\uc7ac\ud588\uace0, \uadf8 \uc21c\uac04\ub9c8\ub2e4 \uc790\uae30\ubc30\ubc18\uc744 \ud0dd\ud558\ub294 \uacbd\uc6b0\uac00 \ub9ce\uc558\ub2e4. \\n\uc791\uac8c\ub294 \uc9d1\uc548\uc77c\uc744 \ud574\uc57c \ud558\ub294\ub370 \ubab8\uc774 \uc870\uae08 \ud798\ub4e4\ub2e4\uace0 \ud558\uc9c0 \uc54a\uac70\ub098 \\n\ud06c\uac8c\ub294 \uc798\ubabb\uc744 \uc778\uc815\ud574\uc57c \ud558\ub294 \uc0c1\ud669\uc5d0\uc11c \uadf8\ub7ec\uc9c0 \uc54a\uc740 \uacbd\uc6b0\uac00 \uc788\uc5c8\ub2e4. \\n\uc774\ub7f0 \uc0c1\ud669\uc774 \ubc18\ubcf5\ub418\uc5b4 \uacb0\uad6d \uc0c1\uc790 \uc548\uc5d0 \ub098 \uc790\uc2e0\uc744 \uac00\ub450\ub294 \uacbd\uc6b0\uac00 \ub9ce\uc558\ub2e4. \\n\\n\ub354 \ub098\uc740 \uc0b6\uc744 \uc704\ud574 \ub0b4\uac00 \uc0c1\uc790 \uc548\uc5d0 \uc788\ub294\uc9c0 \uc9c0\uc18d\uc801\uc73c\ub85c \ud655\uc778\ud558\uace0, \uc0c1\uc790 \ubc16\uc73c\ub85c \ub098\uac00\ub824\ub294 \uc5f0\uc2b5\uc744 \ud574\uc57c\uaca0\ub2e4. \\n\ub113\uc740 \uc2dc\uc120\uc744 \uac00\uc9c0\uace0, \ud56d\uc0c1 \ub0b4\uac00 \ud2c0\ub9b4 \uc218 \uc788\ub2e4\ub294 \uac83\uc744 \uc0dd\uac01\ud558\uace0 \uc0b4\uc544\uac00\uc790. \\n\\n### \ubc11\uc904 \uce5c \ubb38\uc7a5\ub4e4\\n\\n> \uc6b0\ub9ac\uc758 \uc0dd\uac01\uc740 \uc9c0\uc2dd\ubcf4\ub2e4 \uc791\ub2e4. \\n\uc6b0\ub9ac\uc758 \uc9c0\uc2dd\uc740 \uc0ac\ub791\ubcf4\ub2e4 \uc791\ub2e4. \\n\uc6b0\ub9ac\uc758 \uc0ac\ub791\uc740 \uc874\uc7ac\ubcf4\ub2e4 \uc791\ub2e4. \\n\uadf8\ub9ac\uace0 \uc6b0\ub9ac\uac00 \uc0dd\uac01\ud558\ub294 \ub098\ub294 \uc2e4\uc81c\uc758 \ub098\ubcf4\ub2e4 \uadf8\ub9cc\ud07c \uc791\ub2e4. \\nR. D. \ub7ad \\np.19\\n>\\n\\n> \uc6b0\ub9ac\uac00 \uc678\uc801\uc73c\ub85c \uc5b4\ub5a4 \ud589\ub3d9\uc744 \ud558\ub4e0\uc9c0 \uac04\uc5d0, \uc0ac\ub78c\ub4e4\uc740 \uc6b0\ub9ac \ub9c8\uc74c\uc5d0\uc11c \uadf8\ub4e4\uc744 \uc5b4\ub5bb\uac8c \ub300\ud558\uace0 \uc788\ub294\uc9c0\uc5d0 \ub530\ub77c \uc8fc\ub85c \ubc18\uc751\ud569\ub2c8\ub2e4. \\n\uc6b0\ub9ac\uac00 \uc0ac\ub78c\ub4e4\uc5d0 \ub300\ud574 \uc5b4\ub5bb\uac8c \ub290\ub07c\uac8c \ub418\ub294\uc9c0\ub294 \uc6b0\ub9ac\uac00 \uc0c1\uc790 \uc548\uc5d0 \uc788\ub294\uc9c0 \ud639\uc740 \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294\uc9c0\uc5d0 \ub530\ub77c \ub2ec\ub77c\uc9c0\uac8c \ub429\ub2c8\ub2e4. \\np.66\\n>\\n\\n> \ube44\ub09c\uc740 \uac10\uc815\uc5d0 \uc18d\ud558\uace0 \ub099\uad00\uc740 \uc758\uc9c0\uc5d0 \uc18d\ud55c\ub2e4. \\n\uc778\uac04\uc740 \uac10\uc815\ubcf4\ub2e4 \ub354 \ud070 \uc874\uc7ac\uc774\ub2e4. \\n\uc54c\ub7ad, \ud0c1\ub2db\ud55c \\np.103\\n>\\n\\n> \uc6b0\ub9ac\uac00 \uc790\uc2e0\uc5d0\uac8c\ub9cc \uc9d1\uc911\ud558\uace0 \uc788\ub294 \ud55c, \ud63c\uc790\uc11c \uc77c\ud558\ub294 \uac83 \uc774\uc0c1\uc758 \ucc3d\uc870\uc801\uc778 \uacb0\uacfc\ub098 \ud611\ub825\uc744 \uc774\ub04c\uc5b4 \ub0b8\ub2e4\ub294 \uac83\uc740 \ubd88\uac00\ub2a5\ud569\ub2c8\ub2e4. \\n\uc624\ub298\ub0a0 \uacbd\uc81c \ud658\uacbd\uc5d0\uc11c\ub294 \ud63c\uc790\uc11c\ub294 \uc77c\uc758 \uacb0\uacfc\ub97c \ud0c1\uc6d4\ud558\uac8c \ub9cc\ub4e4\uc5b4 \ub0b4\uae30\uac00 \uc5b4\ub835\uc2b5\ub2c8\ub2e4. \\n\ub0b4\uac00 \uc911\uc2ec\uc774\uc5b4\uc57c \ub41c\ub2e4\ub294 \ud3d0\uc1c4\uc801\uc778 \uc0ac\uace0\ub294 \ud568\uaed8 \uc77c\ud558\ub294 \uc0ac\ub78c\ub4e4\uc758 \uc5f4\uc815\uc744 \ubd88\ub7ec\uc624\uc9c0 \ubabb\ud569\ub2c8\ub2e4. \\np.175\\n> \\n\\n> \uc194\uc9c1\ud568\uc740 \uc6b0\ub9ac\uc758 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \uc5f4\uc1e0\uc785\ub2c8\ub2e4. \\n\uadf8\uac83\uc740 \uc790\uc2e0\uc758 \ud589\ub3d9\uacfc \uad00\ub828\ub41c \uc0ac\ub78c\uc5d0 \ub300\ud574 \uae30\uaebc\uc774 \uc0ac\uacfc\ub97c \ud558\ub294 \uac83\uc785\ub2c8\ub2e4. \\n\uadf8\uac83\ub9cc\uc774 \uc2e4\ud0c0\ub798\ucc98\ub7fc \uc5c9\ud0a8 \uad00\uacc4\uc758 \ubb38\uc81c\ub97c \ud574\uacb0\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc774\uc8e0. \\np.188\\n> \\n\\n> \ub204\uad70\uac00\ub97c \ub098\uc640 \uac19\uc774 \ub3d9\uc77c\ud55c \uac00\uce58\ub97c \uc9c0\ub2cc \ud55c \uc778\uac04\uc73c\ub85c \uc0dd\uac01\ud574\uc11c \uadf8 \uc0ac\ub78c\uc744 \uc704\ud574 \ub0b4\uac00 \uc0c1\uc790 \ubc16\uc5d0 \uacc4\uc18d \uba38\ubb34\ub974\uace0 \uc2f6\uc740 \uc5f4\ub9dd\uc774 \uc0dd\uae38 \ub54c, \ub098\ub294 \uc774\ubbf8 \uadf8 \uc0ac\ub78c\uc5d0 \ub300\ud574 \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub2e4. \\np.214\\n> \\n\\n> \ub300\ubd80\ubd84\uc758 \uc0ac\ub78c\ub4e4\uc774 \uad00\uacc4 \uae30\uc220\uc744 \uac00\uc9c0\uace0 \uadf8\ub4e4\uc774 \uacaa\uace0 \uc788\ub294 \ubb38\uc81c\ub97c \ubc14\ub85c\uc7a1\uc73c\ub824\uace0 \ud558\ub294 \ub178\ub825\uc774 \uacb0\uc2e4\uc744 \uc5bb\uc9c0 \ubabb\ud558\ub294 \uac83\uc740 \uacb0\ucf54 \uadf8\ub7ec\ud55c \uae30\uc220 \ubd80\uc871 \ub54c\ubb38\uc5d0 \uc0dd\uae30\ub294 \uac83\uc774 \uc544\ub2d9\ub2c8\ub2e4. \\n\uadf8\uac83\ub4e4\uc740 \uc790\uae30\ubc30\ubc18 \ub54c\ubb38\uc5d0 \uc0dd\uaca8\ub0a9\ub2c8\ub2e4. \\np.224\\n>\\n\\n> \uc6b0\ub9ac\ub294 \ud568\uaed8 \uc77c\ud558\uace0 \uc6b0\ub9ac\uc640 \ud568\uaed8 \uc0b4\uc544\uac00\ub294 \uc0ac\ub78c\uc774 \uc9c4\uc815\uc73c\ub85c \ub204\uad6c\uc778\uc9c0 \uc54c\uc9c0 \ubabb\ud569\ub2c8\ub2e4. \\n\uc6b0\ub9ac\uac00 \uadf8\ub4e4\uacfc \uc9c4\uc815\uc73c\ub85c \ud568\uaed8 \uc18c\ud1b5\ud558\uae30 \uc804\uae4c\uc9c0\ub294 \uc6b0\ub9ac\ub294 \uadf8\ub4e4\uc758 \uac00\uce58\ub97c \uc798 \ubaa8\ub985\ub2c8\ub2e4. \\n\uc6b0\ub9ac\uc758 \uc704\ub300\ud568\uc774\ub780 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uc758 \uc704\ub300\ud55c \uc810\uc744 \ubc1c\uacac\ud574 \uc8fc\ub294 \uac83\uc5d0 \uc788\uc2b5\ub2c8\ub2e4. \\np.280\\n>"},{"id":"innodb-lock","metadata":{"permalink":"/innodb-lock","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-07-InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx","source":"@site/blog/2023-2/2023-04-07-InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx","title":"InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08","description":"InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08","date":"2023-04-07T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 7\uc77c","tags":[{"label":"DataBase","permalink":"/tags/data-base"},{"label":"Lock","permalink":"/tags/lock"},{"label":"InnoDB","permalink":"/tags/inno-db"}],"readingTime":5.805,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08","slug":"innodb-lock","tags":["DataBase","Lock","InnoDB"]},"prevItem":{"title":"[\ucc45] \uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c","permalink":"/book-leadership-and-self-deception"},"nextItem":{"title":"MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08","permalink":"/mysql-lock"}},"content":"## InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08\\n\\nMySQL\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uc7a0\uae08\uacfc \ubcc4\uac1c\ub85c \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4 \ub0b4\ubd80\uc5d0\uc11c \ub85c\uc6b0 \ub2e8\uc704\uc758 \uc7a0\uae08\uc744 \uc9c0\uc6d0\ud55c\ub2e4. \\n\ubcf4\ud1b5 \uba85\uc2dc\uc801\uc73c\ub85c \uc7a0\uae08\uc744 \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0\ub294 \ub4dc\ubb3c\uace0, \uaca9\ub9ac \uc218\uc900\uc5d0 \ub530\ub77c \ubb35\uc2dc\uc801\uc73c\ub85c \uc7a0\uae08\uc774 \uc0ac\uc6a9\ub41c\ub2e4. \\n\\n\ub3d9\uc2dc\uc131 \uc81c\uc5b4 \ubc29\uc2dd\uc5d0\ub294 \ub099\uad00\uc801\uc778 \ubc29\uc2dd\uacfc \ube44\uad00\uc801\uc778 \ubc29\uc2dd\uc774 \uc788\ub2e4. \\nInnoDB\ub294 \uae30\ubcf8\uc801\uc73c\ub85c MVCC(\ub2e4\uc911 \ubc84\uc804 \ub3d9\uc2dc\uc131 \uc81c\uc5b4)\ub97c \ud1b5\ud574 \ub099\uad00\uc801\uc778 \ubc29\uc2dd\uc744 \uc0ac\uc6a9\ud558\uace0 \ub77d\uc744 \ud1b5\ud574 \ud2b9\uc815 \uc0c1\ud669\uc5d0\uc11c \ube44\uad00\uc801\uc778 \ubc29\uc2dd\uc744 \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n:::note \ub099\uad00\uc801 \ub3d9\uc2dc\uc131 \uc81c\uc5b4(OCC, Optimistic concurrency control)\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc774 \uc11c\ub85c \ucda9\ub3cc\ud558\uc9c0 \uc54a\ub294\ub2e4\uace0 \uac00\uc815\ud558\ub294 \ubc29\uc2dd \\n\\n:::\\n\\n:::note \ube44\uad00\uc801 \ub3d9\uc2dc\uc131 \uc81c\uc5b4(PCC, Pessimistic Concurrency Control)\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc774 \ucda9\ub3cc\ud558\ub294 \uac00\uc815\ud558\uc5d0 \uc7a0\uae08\uc744 \uac70\ub294 \ubc29\uc2dd \\n\uc77c\ubc18\uc801\uc73c\ub85c\xa0Shared Lock, Exclusive Lock\uc744 \ud1b5\ud574 \uc774\ub97c \uad6c\ud604\ud55c\ub2e4.\\n\\n:::\\n\\n### Shared & Exclusive Locks\\n\\nInnoDB\ub294 \ub85c\uc6b0 \ub2e8\uc704\uc758 \uc7a0\uae08\uc744 \uc218\ud589\ud560 \ub54c \uacf5\uc720 \uc7a0\uae08\uacfc \ubc30\ud0c0\uc801 \uc7a0\uae08\uc744 \uc0ac\uc6a9\ud55c\ub2e4. \\n\\n**\uacf5\uc720 \uc7a0\uae08(S, shared lock)**\\n\\n\ub370\uc774\ud130 \uc870\ud68c\ub97c \uc704\ud55c \ub77d, \uc77d\uae30 \uc7a0\uae08(read lock)\uc73c\ub85c\ub3c4 \ubd88\ub9b0\ub2e4. \\n\ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \uc77d\uae30\uac00 \uac00\ub2a5\ud558\uc9c0\ub9cc, \uc4f0\uae30\ub294 \ubd88\uac00\ub2a5\ud558\ub2e4. \\n\uc608) `SELECT * FROM table_name WHERE id = 1 LOCK IN SHARE MODE;`\\n\\n**\ubc30\ud0c0\uc801 \uc7a0\uae08(X, exclusive lock)** \\n\\n\ub370\uc774\ud130 \ubcc0\uacbd\uc744 \uc704\ud55c \ub77d, \uc4f0\uae30 \uc7a0\uae08(write lock)\uc73c\ub85c\ub3c4 \ubd88\ub9b0\ub2e4. \\n\ub77d\uc744 \uac74 \ud2b8\ub79c\uc7ad\uc158\ub9cc\uc774 \ud574\ub2f9 \ub370\uc774\ud130\uc5d0 \uc811\uadfc \uac00\ub2a5\ud558\ub2e4. \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc758 \uacbd\uc6b0 \uc77d\uae30, \uc4f0\uae30\uac00 \ubd88\uac00\ub2a5\ud558\ub2e4. \\n\uc608) `SELECT * FROM table_name WHERE id = 1 FOR UPDATE;`\\n\\n### Intention Locks\\n\\nInnoDB\ub294 \ub85c\uc6b0 \ub2e8\uc704 \uc7a0\uae08\uacfc \ud14c\uc774\ube14 \uc7a0\uae08\uc758 \uacf5\uc874\uc744 \uc704\ud574 \uc778\ud14d\uc158 \uc7a0\uae08\uc744 \uc9c0\uc6d0\ud55c\ub2e4. \\n\ud14c\uc774\ube14\uc5d0 \uc788\ub294 \ub85c\uc6b0\uc5d0 \ub300\ud574\uc11c \ub098\uc911\uc5d0 \uc694\uccad\ub418\ub294 \uac83\uc774 \uc5b4\ub5a4 \ud615\ud0dc\uc758 \uc7a0\uae08\uc778\uc9c0 \uac00\ub9ac\ud0a4\uae30 \uc704\ud574 \uc0ac\uc6a9\ub41c\ub2e4. \\n\uae30\ubcf8\uc801\uc73c\ub85c \ub85c\uc6b0 \ub2e8\uc704 \uc7a0\uae08\uc744 \uc218\ud589\ud558\uae30 \uc804\uc5d0 \uc778\ud150\uc158 \uc7a0\uae08\uc744 \uba3c\uc800 \ud68d\ub4dd\ud55c\ub2e4. \\n\uc778\ud150\uc158 \ub77d\uc740 \uae30\ubcf8\uc801\uc73c\ub85c \ucda9\ub3cc\uc744 \ubc29\uc9c0\ud558\uace0 \ub370\ub4dc\ub77d\uc744 \ubc29\uc9c0\ud558\ub294 \uc5ed\ud560\uc744 \ud55c\ub2e4. \\n\\n**\uc778\ud150\uc158 \uacf5\uc720 \uc7a0\uae08(IS, intention shared lock)**\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc774 \ud14c\uc774\ube14\uc758 \uac1c\ubcc4 \ub85c\uc6b0\uc5d0 \ub300\ud55c \uacf5\uc720 \uc7a0\uae08\uc744 \uc218\ud589\ud558\ub294 \uac83\uc744 \uc758\ubbf8\ud55c\ub2e4.\\n\\n**\uc778\ud150\uc158 \ubc30\ud0c0\uc801 \uc7a0\uae08(IX, intention exclusive lock)** \\n\\n\ud2b8\ub79c\uc7ad\uc158\uc774 \ud14c\uc774\ube14\uc758 \uac1c\ubcc4 \ub85c\uc6b0\uc5d0 \ub300\ud55c \ubc30\ud0c0\uc801 \uc7a0\uae08\uc744 \uc218\ud589\ud558\ub294 \uac83\uc744 \uc758\ubbf8\ud55c\ub2e4.\\n\\n** \uc7a0\uae08\uac04\uc758 \ud638\ud658\uc131 **\\n\\n| | X | IX | S | IS |\\n| --- | --- | --- | --- | --- |\\n| X | Conflict | Conflict | Conflict | Conflict |\\n| IX | Conflict | Compatible | Conflict | Compatible |\\n| S | Conflict | Conflict | Compatible | Compatible |\\n| IS | Conflict | Compatible | Compatible | Compatible |\\n\\n### Record Locks\\n\\n\ub808\ucf54\ub4dc \uc790\uccb4\ub9cc\uc744 \uc7a0\uadf8\ub294 \ub77d\uc774\ub2e4. \\nInnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc740 \ub808\ucf54\ub4dc \uc790\uccb4\uac00 \uc544\ub2c8\ub77c \uc778\ub371\uc2a4\uc758 \ub808\ucf54\ub4dc\ub97c \uc7a0\uadfc\ub2e4. \\n\\n### Gap Locks\\n\\n\ub808\ucf54\ub4dc\uc640 \ubc14\ub85c \uc778\uc811\ud55c \ub808\ucf54\ub4dc \uc0ac\uc774\uc758 \uac04\uaca9\ub9cc\uc744 \uc7a0\uadf8\ub294 \ub77d\uc774\ub2e4. \\n\ub808\ucf54\ub4dc\uc640 \ub808\ucf54\ub4dc \uc0ac\uc774\uc758 \uac04\uaca9\uc5d0 \uc0c8\ub85c\uc6b4 \ub808\ucf54\ub4dc\uac00 \uc0dd\uc131\ub418\ub294 \uac83\uc744 \uc81c\uc5b4\ud558\uace0, \ub125\uc2a4\ud2b8 \ud0a4 \ub77d\uc758 \uc77c\ubd80\ub85c \uc0ac\uc6a9\ub41c\ub2e4. \\n\\n### Next-Key Locks\\n\\n\ub808\ucf54\ub4dc \ub77d\uacfc \uac2d \ub77d\uc744 \ud569\uccd0\ub193\uc740 \ud615\ud0dc\uc758 \uc7a0\uae08\uc73c\ub85c \ub808\ucf54\ub4dc\uc640 \uadf8 \ub808\ucf54\ub4dc \uc55e\uc758 \uac2d \ub77d\uc744 \ud3ec\ud568\ud55c\ub2e4. \\n`REPEATABLE READ` \uaca9\ub9ac \uc218\uc900\uc5d0\uc11c \ud32c\ud140 \ub9ac\ub4dc\ub97c \ubc29\uc9c0\ud558\uae30 \uc704\ud55c \uc7a0\uae08\uc774\ub2e4. \\n\\n### AUTO-INC Locks\\n\\n`AUTO_INCREMENT` \uce7c\ub9bc\uc774 \uc0ac\uc6a9\ub41c \ud14c\uc774\ube14\uc5d0 \ub3d9\uc2dc\uc5d0 \uc5ec\ub7ec \ub808\ucf54\ub4dc\uac00 `INSERT`\ub418\ub294 \uacbd\uc6b0, \uac01 \ub808\ucf54\ub4dc\ub294 \uc911\ubcf5\ub418\uc9c0 \uc54a\uace0 \uc800\uc7a5\ub41c \uc21c\uc11c\ub300\ub85c \uc99d\uac00\ud558\ub294 \uc77c\ub828\ubc88\ud638 \uac12\uc744 \uac00\uc838\uc57c \ud55c\ub2e4. \\nInnoDB \ub294 \ub0b4\ubd80\uc801\uc73c\ub85c AUTO-INC \ub77d\uc774\ub77c\uace0 \ud558\ub294 \ud14c\uc774\ube14 \uc218\uc900\uc758 \uc7a0\uae08\uc744 \uc0ac\uc6a9\ud55c\ub2e4. \\n\ud2b8\ub79c\uc7ad\uc158\uacfc \uad00\uacc4 \uc5c6\uc774 `INSERT`\ub098 `REPLACE` \ubb38\uc7a5\uc5d0\uc11c `AUTO_INCREMENT` \uac12\uc744 \uac00\uc838\uc624\ub294 \uc21c\uac04\ub9cc \ub77d\uc774 \uac78\ub838\ub2e4\uac00 \ud574\uc81c\ub41c\ub2e4.\\n\\n### \uc7a0\uae08 \uc608\uc2dc\\n\\n```sql\\n-- \ub808\ucf54\ub4dc\ub294 id \uae30\uc900 10, 20, 30, 40, 50\uc774 \uc788\ub2e4\uace0 \uac00\uc815\\n-- Record Locks: 10\uc5d0 \ub300\ud574 \ub77d\uc774 \uac78\ub9b0\ub2e4.\\nSELECT * FROM table_name where id = 10 for update;\\n\\n-- Gap Locks: 51\ubd80\ud130 PositiveInfinity\uae4c\uc9c0 \ub77d\uc774 \uac78\ub9b0\ub2e4.\\nSELECT * FROM table_name where id > 100 for update;\\n\\n-- Next-Key Locks: 21\ubd80\ud130 30, 31\ubd80\ud130 40\uc5d0 \ub77d\uc774 \uac78\ub9b0\ub2e4.\\nSELECT * FROM table_name where id BETWEEN 25 AND 35 for update;\\n```\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\nReal My SQL 8.0 - 5\uc7a5 \ud2b8\ub79c\uc7ad\uc158\uacfc \uc7a0\uae08, \ubc31\uc740\ube48, \uc774\uc131\uc6b1 \\n[Optimistic and Pessimistic record locking, IBM](https://www.ibm.com/docs/en/rational-clearquest/9.0.0?topic=clearquest-optimistic-pessimistic-record-locking) \\n[MySQL Innodb Locks, cecil1018](https://cecil1018.wordpress.com/2016/06/18/mysql-innodb-locks/) \\n[MySQL 8.0 InnoDB Locks, MySQL](https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html) \\n[Locks Set by Different SQL Statements in InnoDB, MySQL](https://dev.mysql.com/doc/refman/8.0/en/innodb-locks-set.html)"},{"id":"mysql-lock","metadata":{"permalink":"/mysql-lock","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-06-MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx","source":"@site/blog/2023-2/2023-04-06-MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx","title":"MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08","description":"MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08","date":"2023-04-06T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 6\uc77c","tags":[{"label":"DataBase","permalink":"/tags/data-base"},{"label":"Lock","permalink":"/tags/lock"},{"label":"MySQL","permalink":"/tags/my-sql"}],"readingTime":4.405,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08","slug":"mysql-lock","tags":["DataBase","Lock","MySQL"]},"prevItem":{"title":"InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08","permalink":"/innodb-lock"},"nextItem":{"title":"\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900","permalink":"/transaction-and-isolation"}},"content":"## MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08\\n\\nMySQL\uc5d0\uc11c\uc758 \ub77d\uc740 \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4 \ub808\ubca8\uacfc, MySQL \uc5d4\uc9c4 \ub808\ubca8\ub85c \ub098\ub20c \uc218 \uc788\ub2e4. \\nMySQL \uc5d4\uc9c4 \ub808\ubca8\uc758 \uc7a0\uae08\uc740 \ubaa8\ub4e0 \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc5d0 \uc601\ud5a5\uc744 \ubbf8\uce5c\ub2e4. \\n\\n### \uae00\ub85c\ubc8c \ub77d(Global lock)\\n\\nMySQL\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uc7a0\uae08 \uc911 \uac00\uc7a5 \ub113\uc740 \ubc94\uc704\ub97c \uac00\uc9c0\uace0 \uc788\ub294 \uc7a0\uae08\uc774\ub2e4. \\n - \uc601\ud5a5\uc744 \ubbf8\uce58\ub294 \ubc94\uc704\ub294 \ud574\ub2f9 \uc11c\ubc84 \uc804\uccb4\uc774\ub2e4.\\n - \uc791\uc5c5 \ub300\uc0c1 \ud14c\uc774\ube14, \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uc0c1\uad00 \uc5c6\uc774 \ub3d9\uc77c\ud558\uac8c \uc601\ud5a5\uc744 \ubc1b\ub294\ub2e4.\\n\\n\ud55c \uc138\uc158\uc5d0\uc11c \uae00\ub85c\ubc8c \ub77d\uc744 \ud68d\ub4dd\ud558\uba74 \ud574\uc81c \ub420 \ub54c \uae4c\uc9c0 \uc870\ud68c\ub97c \uc81c\uc678\ud55c \ub300\ubd80\ubd84\uc758 \uba85\ub839\uc774 \ub300\uae30 \uc0c1\ud0dc\uac00 \ub41c\ub2e4. \\n\ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc874\uc7ac\ud558\ub294 MyISAM\uc774\ub098 MEMORY \ud14c\uc774\ube14\uc5d0 \ub300\ud574 \uc77c\uad00\ub41c \ubc31\uc5c5\uc744 \ubc1b\uc544\uc57c\ud560 \ub54c \uc0ac\uc6a9\ud55c\ub2e4. \\nInnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc5d0\uc11c\ub294 \ubc31\uc5c5 \uc2dc \uc870\uae08 \ub354 \uac00\ubcbc\uc6b4 \ubc31\uc5c5 \ub77d\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. \\n\\n```sql\\n-- GLOBAL LOCK\\nFLUSH TABLES WITH READ LOCK;\\n-- UNLOCK\\nUNLOCK TABLES;\\n\\n-- BACKUP LOCK\\nLOCK INSTANCE FOR BACKUP;\\n-- UNLOCK\\nUNLOCK INSTANCE;\\n```\\n\\n:::note MyISAM\\n\\nMySQL 5.5 \ubc84\uc804 \uc774\uc804\uc758 \uae30\ubcf8 \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc774\ub2e4. \\n\ud2b8\ub79c\uc7ad\uc158\uc744 \uc9c0\uc6d0\ud558\uc9c0 \uc54a\uace0, SELECT \uc791\uc5c5 \uc18d\ub3c4\uac00 \ube60\ub974\ub2e4.\\n\\n:::\\n\\n### \ud14c\uc774\ube14 \ub77d(Table lock)\\n\\n\uac1c\ubcc4 \ud14c\uc774\ube14 \ub2e8\uc704\ub85c \uc124\uc815\ub418\ub294 \uc7a0\uae08\uc774\ub2e4. \\n\uba85\uc2dc\uc801 \ub610\ub294 \ubb35\uc2dc\uc801\uc73c\ub85c \ud2b9\uc815 \ud14c\uc774\ube14\uc758 \ub77d\uc744 \ud68d\ub4dd\ud560 \uc218 \uc788\ub2e4. \\n\ubb35\uc2dc\uc801 \ub77d\uc740 MyISAM\uc774\ub098 MEMORY \ud14c\uc774\ube14\uc5d0 \ub370\uc774\ud130\ub97c \ubcc0\uacbd\ud558\ub294 \ucffc\ub9ac\ub97c \uc2e4\ud589\ud558\uba74 \ubc1c\uc0dd\ud55c\ub2e4. \\nInnoDB \ud14c\uc774\ube14\uc5d0\ub294 DML \ucffc\ub9ac\ub294 \ubb34\uc2dc\ub418\uace0 DDL \uc77c \uacbd\uc6b0\uc5d0\ub9cc \ubb35\uc2dc\uc801\uc73c\ub85c \ub77d\uc744 \ud68d\ub4dd\ud55c\ub2e4.\\n\\n```sql\\n-- TABLE LOCK\\nLOCK TABLES table_name [ READ | WRITE ]\\n\\n-- UNLOCK\\nUNLOCK TABLES;\\n```\\n\\n### \ub124\uc784\ub4dc \ub77d(Named lock)\\n\\n\uc784\uc758\uc758 \ubb38\uc790\uc5f4\uc5d0 \ub300\ud55c \uc7a0\uae08\uc744 \uc124\uc815\ud560 \uc218 \uc788\ub294 \uc7a0\uae08\uc73c\ub85c \uc720\uc800 \ub808\ubca8 \ub77d\uc73c\ub85c\ub3c4 \ubd88\ub9b0\ub2e4. \\n\uc5ec\ub7ec \uc2a4\ub808\ub4dc\ub098 \ud504\ub85c\uc138\uc2a4\uac00 \ub3d9\uc77c\ud55c \ub370\uc774\ud130\ub97c \uc218\uc815\ud558\ub824\ub294 \uacbd\uc6b0, \ub3d9\uc2dc\uc5d0 \uc218\uc815\ud558\uc9c0 \ubabb\ud558\ub3c4\ub85d \ubcf4\ud638\ud560 \uc218 \uc788\ub2e4. \\n\\n```sql\\n-- aGVyYg== \ub77c\ub294 \ubb38\uc790\uc5f4\uc5d0 \ub300\ud55c \uc7a0\uae08 \ud68d\ub4dd, \uc774\ubbf8 \uc7a0\uae08\uc744 \uc0ac\uc6a9\uc911\uc778 \uacbd\uc6b0 1\ucd08 \ub3d9\uc548\ub9cc \ub300\uae30\\nSELECT GET_LOCK(\'aGVyYg==\', 1);\\n\\n-- \ubb38\uc790\uc5f4\uc5d0 \ub300\ud55c \uc7a0\uae08\uc774 \uc124\uc815\ub418\uc5b4 \uc788\ub294\uc9c0 \ud655\uc778\ud55c\ub2e4.\\nSELECT IS_FREE_LOCK(\'aGVyYg==\');\\n\\n-- \ubb38\uc790\uc5f4\uc5d0 \ub300\ud55c \uc7a0\uae08\uc744 \ud574\uc81c\ud55c\ub2e4.\\nSELECT RELEASE_LOCK(\'aGVyYg==\');\\n\\n-- \uc704 3\uac1c \ud568\uc218 \ubaa8\ub450 \uc815\uc0c1\uc801\uc73c\ub85c \ub77d\uc744 \ud68d\ub4dd\ud558\uac70\ub098 \ud574\uc81c\ud55c \uacbd\uc6b0\uc5d0 1\uc744, \uc544\ub2c8\uba74 0\uc744 \ubc18\ud658\ud55c\ub2e4.\\n\\n-- \ubaa8\ub4e0 \ubb38\uc790\uc5f4\uc5d0 \ub300\ud55c \uc7a0\uae08\uc744 \ud574\uc81c\ud55c\ub2e4. \ud574\uc81c\ub41c \uc7a0\uae08\uc758 \uac1c\uc218\ub97c \ubc18\ud658\ud55c\ub2e4.\\nSELECT RELEASE_ALL_LOCKS();\\n```\\n\\n### \uba54\ud0c0\ub370\uc774\ud130 \ub77d(Metadata lock)\\n\\n\ub370\uc774\ud130\ubca0\uc774\uc2a4 \uac1d\uccb4\uc758 \uc774\ub984\uc774\ub098 \uad6c\uc870\ub97c \ubcc0\uacbd\ud558\ub294 \uacbd\uc6b0 \ud68d\ub4dd\ud558\ub294 \uc7a0\uae08\uc774\ub2e4. \\n\uba85\uc2dc\uc801\uc73c\ub85c \ud68d\ub4dd \ub610\ub294 \ud574\uc81c \ud560 \uc218 \uc5c6\uc9c0\ub9cc \ud14c\uc774\ube14\uc758 \uc774\ub984\uc744 \ubcc0\uacbd\ud558\ub294 \uacbd\uc6b0 \uc790\ub3d9\uc73c\ub85c \ud68d\ub4dd\ud55c\ub2e4. \\n\ubcf4\ud1b5 \ubc30\uce58 \ud504\ub85c\uadf8\ub7a8\uc5d0\uc11c \uc2e4\uc2dc\uac04\uc73c\ub85c \ud14c\uc774\ube14\uc744 \ubc14\uafd4\uc57c\ud558\ub294 \uacbd\uc6b0\uc5d0 \uc0ac\uc6a9\ub41c\ub2e4.\\n\\n```sql\\n-- \ubc30\uce58 \ud504\ub85c\uadf8\ub7a8\uc5d0\uc11c \ubcc4\ub3c4\uc758 \uc784\uc2dc \ud14c\uc774\ube14\uc5d0 \uc11c\ube44\uc2a4\uc6a9 \ub7ad\ud0b9 \ub370\uc774\ud130 \uc0dd\uc131 \ud6c4 \uae30\uc874 \ud14c\uc774\ube14\uc744 \ubc31\uc5c5\ud558\ub294 \uacbd\uc6b0\\n-- \uc544\ub798 \uad6c\ubb38 \uc2e4\ud589 \uc2dc \uba54\ud0c0\ub370\uc774\ud130 \ub77d\uc744 \uc790\ub3d9\uc73c\ub85c \ud68d\ub4dd\ud55c\ub2e4.\\nRENAME TABLE rank TO rank_backup, rank_new TO rank;\\n```\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\nReal My SQL 8.0 - 5\uc7a5 \ud2b8\ub79c\uc7ad\uc158\uacfc \uc7a0\uae08, \ubc31\uc740\ube48, \uc774\uc131\uc6b1 \\n[MySQL\uc758 User Level Lock\ub97c \ud65c\uc6a9\ud55c\ub2e4\uba74?, gywndi](https://gywn.net/2013/12/mysql-user-level-lock/) \\n[Locking Functions, MySQL 5.7 Reference](https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html#function_release-all-locks) \\n[Locking Functions, MySQL 8.0 Reference](https://dev.mysql.com/doc/refman/8.0/en/locking-functions.html#function_release-all-locks)"},{"id":"transaction-and-isolation","metadata":{"permalink":"/transaction-and-isolation","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-05-\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900.mdx","source":"@site/blog/2023-2/2023-04-05-\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900.mdx","title":"\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900","description":"\ud2b8\ub79c\uc7ad\uc158(Transaction)","date":"2023-04-05T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 5\uc77c","tags":[{"label":"DataBase","permalink":"/tags/data-base"},{"label":"Transaction","permalink":"/tags/transaction"},{"label":"Isolation","permalink":"/tags/isolation"}],"readingTime":9.68,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900","slug":"transaction-and-isolation","tags":["DataBase","Transaction","Isolation"]},"prevItem":{"title":"MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08","permalink":"/mysql-lock"},"nextItem":{"title":"\ud14c\uc2a4\ud2b8 \ub300\uc5ed","permalink":"/test-double"}},"content":"## \ud2b8\ub79c\uc7ad\uc158(Transaction)\\n\\n\ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \ub17c\ub9ac\uc801 \uae30\ub2a5\uc744 \uc218\ud589\ud558\uae30 \uc704\ud55c \uc791\uc5c5\uc758 \ub2e8\uc704\ub97c \ub9d0\ud55c\ub2e4. \\n\ud2b8\ub79c\uc7ad\uc158\uc740 \uc791\uc5c5\uc758 \uc644\uc804\uc131\uacfc \ub370\uc774\ud130\uc758 \uc815\ud569\uc131\uc744 \ubcf4\uc7a5\ud574 \uc900\ub2e4. \\n\ub17c\ub9ac\uc801\uc778 \uc791\uc5c5 \uc14b\uc744 \uc644\ubcbd\ud558\uac8c \ucc98\ub9ac\ud558\uac70\ub098, \uc624\ub958 \uc2dc \uc791\uc5c5\uc758 \uc77c\ubd80\ub9cc \uc801\uc6a9\ub418\ub294 \ud604\uc0c1\uc744 \ub9c9\uc544\uc900\ub2e4. \\n\\n### \ud2b8\ub79c\uc7ad\uc158\uc758 \uc18d\uc131(ACID)\\n\\n\uc6d0\uc790\uc131(Atomicity): \ud2b8\ub79c\uc7ad\uc158 \ub0b4\uc5d0\uc11c \uc2e4\ud589\ub41c \uc791\uc5c5\ub4e4\uc740 \ubaa8\ub450 \uc131\uacf5\ud558\uac70\ub098, \uc2e4\ud328\ud574\uc57c \ud55c\ub2e4. \\n\uc77c\uad00\uc131(Consistency): \ud2b8\ub79c\uc7ad\uc158\uc774 \uc218\ud589\ub418\uae30 \uc804\uacfc \ud6c4\uc5d0 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uac00 \uc77c\uad00\ub41c \uc0c1\ud0dc\ub97c \uc720\uc9c0\ud574\uc57c \ud55c\ub2e4. \\n\uaca9\ub9ac\uc131(Isolation): \uac01\uac01\uc758 \ud2b8\ub79c\uc7ad\uc158\uc740 \ub3c5\ub9bd\uc801\uc774\ub77c \uc11c\ub85c\uc5d0\uac8c \uc601\ud5a5\uc744 \uc8fc\uc9c0 \uc54a\uc544\uc57c \ud55c\ub2e4. \\n\uc9c0\uc18d\uc131(Durability): \ud2b8\ub79c\uc7ad\uc158\uc774 \uc131\uacf5\uc801\uc73c\ub85c \uc644\ub8cc\ub41c\ub2e4\uba74 \uc601\uad6c\uc801\uc73c\ub85c \uacb0\uacfc\uc5d0 \ubc18\uc601\ub418\uc5b4\uc57c \ud55c\ub2e4. \\n\\n### \ud2b8\ub79c\uc7ad\uc158 \uc8fc\uc758\uc0ac\ud56d\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc740 \uaf2d \ud544\uc694\ud55c \ucd5c\uc18c\uc758 \ucf54\ub4dc\uc5d0\ub9cc \uc801\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\ub2e4.(\ud2b8\ub79c\uc7ad\uc158\uc758 \ubc94\uc704\ub97c \ucd5c\uc18c\ud654\ud558\ub77c) \\n\uad6c\ud604\ud574\uc57c \ud558\ub294 \uc5c5\ubb34\uc5d0 \ub530\ub77c \ud2b8\ub79c\uc7ad\uc158\uc744 \ubb36\uac70\ub098 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \uc81c\uc678\ud558\uace0, \ub124\ud2b8\uc6cc\ud06c \uc791\uc5c5\uc774 \uc788\ub294 \uacbd\uc6b0 \ubc18\ub4dc\uc2dc \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ubc30\uc81c\ud574\uc57c \ud55c\ub2e4. \\n\\n:::info \uc65c \ub124\ud2b8\uc6cc\ud06c \uc791\uc5c5\uc774 \uc788\uc744 \ub54c \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ubc30\uc81c\ud574\uc57c \ud560\uae4c? \ud83e\udd14\\n\\n\ub370\uc774\ud130\uc758 \uc77c\uad00\uc131\uacfc \uc548\uc804\uc131\uc744 \ubcf4\uc7a5\ud558\uae30 \uc704\ud574 \ubc30\uc81c\ud574\uc57c \ud55c\ub2e4. \\n\ub124\ud2b8\uc6cc\ud06c \uc791\uc5c5\uc744 \ud2b8\ub79c\uc7ad\uc158 \ub0b4\ubd80\uc5d0 \ud3ec\ud568\ud55c\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud560 \uc218 \uc788\ub2e4. \\n- \ub124\ud2b8\uc6cc\ud06c \uc791\uc5c5\uc774 \uc911\uac04\uc5d0 \uc2e4\ud328\ud560 \uac00\ub2a5\uc131(\uc548\uc804\uc131 X)\\n- \ud1b5\uc2e0\uc73c\ub85c \uc778\ud574 \ub370\uc774\ud130\uac00 \ubcc0\uacbd\ub420 \uc218 \uc788\ub294 \ubd80\ubd84(\uc77c\uad00\uc131 X)\\n\\n:::\\n\\n## \uaca9\ub9ac \uc218\uc900(Isolation level)\\n\\n\uc5ec\ub7ec \ud2b8\ub79c\uc7ad\uc158\uc774 \ub3d9\uc2dc\uc5d0 \ucc98\ub9ac\ub420 \ub54c \ud2b9\uc815 \ud2b8\ub79c\uc7ad\uc158\uc774 \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ub370\uc774\ud130\uc758 \uc870\ud68c \ubc0f \ubcc0\uacbd\uc744 \ud5c8\uc6a9\ud560\uc9c0 \uacb0\uc815\ud558\ub294 \uac83\uc744 \ub9d0\ud55c\ub2e4. \\n\uaca9\ub9ac \uc218\uc900\uc774 \ub192\uc544\uc9c8 \uc218\ub85d \ub3d9\uc2dc \ucc98\ub9ac \uc131\ub2a5\uc774 \ub5a8\uc5b4\uc9c0\ub294 \uac83\uc774 \uc77c\ubc18\uc801\uc774\uc9c0\ub9cc, `SERIALIZABLE`\uc774 \uc544\ub2c8\ub77c\uba74 \ud06c\uac8c \uc131\ub2a5\uc758 \uc800\ud558\uac00 \ubc1c\uc0dd\ud558\uc9c0 \uc54a\ub294\ub2e4. \\n\\n### READ UNCOMMITTED\\n\\n\uac01 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c\uc758 \ubcc0\uacbd \ub0b4\uc6a9\uc774 `COMMIT`\uc774\ub098 `ROLLBACK` \uc5ec\ubd80\uc5d0 \uc0c1\uad00\uc5c6\uc774 \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ubcf4\uc778\ub2e4. \\n\ub354\ud2f0 \ub9ac\ub4dc \ud604\uc0c1\uc774 \ubc1c\uc0dd\ud558\uae30 \ub54c\ubb38\uc5d0 \uc815\ud569\uc131\uc758 \ubb38\uc81c\uac00 \ub9ce\uc740 \uaca9\ub9ac \uc218\uc900\uc774\ub2e4. \\nMySQL \uc0ac\uc6a9\uc2dc \ucd5c\uc18c `READ COMMITTED` \uc774\uc0c1\uc758 \uaca9\ub9ac \uc218\uc900 \uc0ac\uc6a9\uc744 \uad8c\uc7a5\ud55c\ub2e4. \\n\\n```mermaid\\n---\\ntitle: READ UNCOMMITTED\\n---\\nsequenceDiagram\\n Alice->>Database: BEGIN\\n Alice->>Database: INSERT(Alice)\\n Bob->>Database: SELECT\\n Database->>+Bob: Alice\\n Alice->>Database: COMMIT(Alice)\\n```\\n\\n### READ COMMITTED\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ub370\uc774\ud130\ub97c \ubcc0\uacbd\ud558\ub354\ub77c\ub3c4 `COMMIT`\uc774 \uc644\ub8cc\ub41c \ub370\uc774\ud130\ub9cc \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \uc870\ud68c\ud560 \uc218 \uc788\ub2e4. \\n\uc624\ub77c\ud074 DBMS\uc5d0\uc11c \uae30\ubcf8\uc73c\ub85c \uc0ac\uc6a9\ub418\ub294 \uaca9\ub9ac \uc218\uc900\uc774\uba70, \uc628\ub77c\uc778 \uc11c\ube44\uc2a4\uc5d0\uc11c \uac00\uc7a5 \ub9ce\uc774 \uc120\ud0dd\ub418\ub294 \uaca9\ub9ac \uc218\uc900\uc774\ub2e4. \\n`REPEATABLE READ`\uac00 \ubcf4\uc7a5\ub418\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0 `NON-REPEATABLE READ` \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud55c\ub2e4. \\n\\n```mermaid\\n---\\ntitle: READ COMMITTED\\n---\\nsequenceDiagram\\n Alice->>Database: BEGIN\\n Alice->>Database: UPDATE(Alice to Bob)\\n Bob->>Database: SELECT\\n Database->>+Bob: Alice(Undo log)\\n Alice->>Database: COMMIT\\n```\\n\\n### REPEATABLE READ\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc774 \uc2dc\uc791\ub418\uae30 \uc804\uc5d0 `COMMIT`\uc774 \uc644\ub8cc\ub41c \ub0b4\uc6a9\uc5d0 \ub300\ud574\uc11c\ub9cc \uc870\ud68c\ud560 \uc218 \uc788\ub2e4. \\nMySQL\uc758 InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc5d0\uc11c \uae30\ubcf8\uc73c\ub85c \uc0ac\uc6a9\ub418\ub294 \uaca9\ub9ac \uc218\uc900\uc774\ub2e4. \\nMVCC\ub97c \uc774\uc6a9\ud574 \uc5b8\ub450(Undo) \uc601\uc5ed\uc5d0 \ubc31\uc5c5\ub41c \uc774\uc804 \ub370\uc774\ud130\ub97c \uc774\uc6a9\ud574 \ub3d9\uc77c \ud2b8\ub79c\uc7ad\uc158 \ub0b4\uc5d0\uc11c\ub294 \ub3d9\uc77c\ud55c \uacb0\uacfc\ub97c \ubcf4\uc5ec\uc904 \uc218 \uc788\uac8c \ubcf4\uc7a5\ud55c\ub2e4. \\n\ub3d9\uc77c\ud55c \uacb0\uacfc\ub97c \ubcf4\uc7a5\ud558\ub294 \ubc29\ubc95\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n - \ubaa8\ub4e0 InnoDB \ud2b8\ub79c\uc7ad\uc158\uc740 \uc21c\ucc28\uc801\uc73c\ub85c \uc99d\uac00\ud558\ub294 \uace0\uc720\ud55c \ud2b8\ub79c\uc7ad\uc158 \ubc88\ud638\ub97c \uac00\uc9c4\ub2e4.\\n - Undo \uc601\uc5ed\uc5d0 \ubc31\uc5c5\ub41c \ub808\ucf54\ub4dc\uc5d0\ub294 \ubcc0\uacbd\uc744 \ubc1c\uc0dd\uc2dc\ud0a8 \ud2b8\ub79c\uc7ad\uc158\uc758 \ubc88\ud638\uac00 \ud3ec\ud568\ub418\uc5b4\uc788\ub2e4.\\n - Undo \uc601\uc5ed\uc758 \ubc31\uc5c5\ub41c \ub370\uc774\ud130\ub294 \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc774 \ubd88\ud544\uc694\ud558\ub2e4\uace0 \ud310\ub2e8\ud558\ub294 \uacbd\uc6b0 \uc0ad\uc81c\ub41c\ub2e4.\\n - `REPEATABLE READ` \uaca9\ub9ac \uc218\uc900\uc5d0\uc11c\ub294 MVCC\ub97c \ubcf4\uc7a5\ud558\uae30 \uc704\ud574 \uac00\uc7a5 \uc624\ub798\ub41c \ud2b8\ub79c\uc7ad\uc158 \ubc88\ud638\ubcf4\ub2e4 \uc55e\uc120 Undo \uc601\uc5ed\uc758 \ub370\uc774\ud130\ub294 \uc0ad\uc81c\ud558\uc9c0 \uc54a\ub294\ub2e4. \\n\\nInnoDB\uc5d0\uc11c\ub294 \uac2d \ub77d\uacfc \ub125\uc2a4\ud2b8 \ud0a4 \ub77d\uc744 \uc774\uc6a9\ud558\uc5ec \ud32c\ud140 \ub9ac\ub4dc \ud604\uc0c1\uc744 \ubc29\uc9c0\ud55c\ub2e4. \\n\\n```mermaid\\n---\\ntitle: REPEATABLE READ\\n---\\nsequenceDiagram\\n participant Alice\\n participant Database\\n participant Bob\\n Bob->>Database: BEGIN(TRX-ID: 1)\\n Bob->>Database: SELECT\\n Database->>+Bob: Alice\\n Alice->>Database: BEGIN(TRX-ID: 2)\\n Alice->>Database: UPDATE(Alice to Bob)\\n Alice->>Database: COMMIT\\n Bob->>Database: SELECT\\n Database->>+Bob: Alice(Undo log)\\n```\\n\\n:::note \uac2d \ub78d(Gap lock)\uacfc \ub125\uc2a4\ud2b8 \ud0a4 \ub77d(Next-key lock)\\n\\n\uac2d \ub77d: \ub808\ucf54\ub4dc\uc640 \ubc14\ub85c \uc778\uc811\ud55c \ub808\ucf54\ub4dc \uc0ac\uc774\uc758 \uac04\uaca9\ub9cc\uc744 \uc7a0\uadf8\ub294 \ub77d\uc774\ub2e4. \\n\ub125\uc2a4\ud2b8 \ud0a4 \ub77d: \ub808\ucf54\ub4dc \ub77d\uacfc \uac2d \ub77d\uc744 \ud569\uccd0\ub193\uc740 \ud615\ud0dc\uc758 \uc7a0\uae08\uc73c\ub85c \ub808\ucf54\ub4dc\uc640 \uadf8 \ub808\ucf54\ub4dc \uc55e\uc758 \uac2d \ub77d\uc744 \ud3ec\ud568\ud55c\ub2e4.\\n\\n:::\\n\\n:::note MVCC(Multi Version Concurrency Control)\\n\\n\ub3d9\uc2dc\uc131\uc744 \uc81c\uc5b4\ud558\ub294 \ubc29\ubc95 \uc911 \ud558\ub098\ub85c \ud558\ub098\uc758 \ub808\ucf54\ub4dc\uc5d0 \ub300\ud574 \uc5ec\ub7ec \uac1c\uc758 \ubc84\uc804\uc774 \ub3d9\uc2dc\uc5d0 \uad00\ub9ac\ub418\ub294 \uac83\uc774\ub2e4.\\n - PostgreSQL\uc740 \ub2e4\uc911 \ubc84\uc804\uc758 \ub370\uc774\ud130\ub97c \uc800\uc7a5\ud558\ub294 \uac83\uc73c\ub85c MVCC\ub97c \uad6c\ud604\ud55c\ub2e4.\\n - Oracle, InnoDB\ub294 `Undo log`\ub97c \uc774\uc6a9\ud574 \uc774 \uae30\ub2a5\uc744 \uad6c\ud604\ud55c\ub2e4.(\ucd5c\uc2e0 \ubc84\uc804\uc758 \ub370\uc774\ud130\ub9cc DB\uc5d0 \uc800\uc7a5)\\n\\n\uc7a0\uae08\uc744 \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 \uc77d\uad00\ub41c \uc77d\uae30\ub97c \uc81c\uacf5\ud558\ub294 \uac83\uc774 \ubaa9\uc801\uc774\ub2e4.\\n\\n:::\\n\\n### SERIALIZABLE\\n\\n\ud2b8\ub79c\uc7ad\uc158\uc744 \uc21c\ucc28\uc801\uc73c\ub85c \uc9c4\ud589\uc2dc\ud0a4\ub294 \uaca9\ub9ac \uc218\uc900\uc774\uace0 \ub530\ub77c\uc11c \ub3d9\uc2dc \ucc98\ub9ac \uc131\ub2a5\ub3c4 \ub2e4\ub978 \uaca9\ub9ac \uc218\uc900\ubcf4\ub2e4 \ub5a8\uc5b4\uc9c4\ub2e4. \\n\ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \uc77d\uace0 \uc4f0\ub294 \ub808\ucf54\ub4dc\ub97c \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c\ub294 \uc811\uadfc\ud560 \uc218 \uc5c6\uace0 \ub2e8\uc21c\ud55c \uc77d\uae30 \uc791\uc5c5\ub3c4 \uacf5\uc720 \uc7a0\uae08(\uc77d\uae30 \uc7a0\uae08)\uc744 \ud68d\ub4dd\ud574\uc57c\ub9cc \ud55c\ub2e4. \\nInnoDB\uc5d0\uc11c\ub294 \ud32c\ud140 \ub9ac\ub4dc \ud604\uc0c1\uc774 `REPEATABLE READ` \uaca9\ub9ac \uc218\uc900\uc5d0\uc11c \ubc1c\uc0dd\ud558\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0 \uad73\uc774 \uc0ac\uc6a9\ud560 \ud544\uc694\ub294 \uc5c6\ub2e4. \\n\\n## \uaca9\ub9ac \uc218\uc900\uc5d0 \ub530\ub978 \ubd80\uc815\ud569 \ubb38\uc81c\\n\\n\uaca9\ub9ac \uc218\uc900\uc5d0 \ub530\ub77c \ub354\ud2f0 \ub9ac\ub4dc, \ubc18\ubcf5 \uac00\ub2a5\ud558\uc9c0 \uc54a\uc740 \uc870\ud68c, \ud32c\ud140 \ub9ac\ub4dc \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud55c\ub2e4. \\n\\n| \uaca9\ub9ac \uc218\uc900 / \ubd80\uc815\ud569 \ubb38\uc81c | \ub354\ud2f0 \ub9ac\ub4dc | \ubc18\ubcf5 \uac00\ub2a5\ud558\uc9c0 \uc54a\uc740 \uc870\ud68c | \ud32c\ud140 \ub9ac\ub4dc |\\n| --- | --- | --- | --- |\\n| READ UNCOMMITTED | O | O | O |\\n| READ COMMITTED | X | O | O |\\n| REPEATABLE READ | X | X | O(InnoDB\ub294 X) |\\n| SERIALIZABLE | X | X | X |\\n\\n### \ub354\ud2f0 \ub9ac\ub4dc(Dirty read)\\n\\n\uc5b4\ub5a4 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ucc98\ub9ac\ud55c \uc791\uc5c5\uc774 \uc644\ub8cc\ub418\uc9c0 \uc54a\uc558\uc5b4\ub3c4 \ub2e4\ub978 \ud2b8\ub79c\uc7ad\uc158\uc5d0\uc11c \ubcfc \uc218 \uc788\ub294 \ud604\uc0c1 \\n\ud2b8\ub79c\uc7ad\uc158 \uaca9\ub9ac \uc218\uc900\uc774 READ UNCOMMITTED\uc77c \ub54c \ubc1c\uc0dd\ud55c\ub2e4. \\n\uc608) B\uac00 \ub808\ucf54\ub4dc\ub97c \ucd94\uac00\ud558\uace0 \ucee4\ubc0b\uc744 \ud558\uc9c0 \uc54a\uc558\uc9c0\ub9cc, A\uac00 \ud574\ub2f9 \ub808\ucf54\ub4dc\ub97c \uc870\ud68c\ud560 \uc218 \uc788\ub294 \uacbd\uc6b0\\n\\n### \ubc18\ubcf5 \uac00\ub2a5\ud558\uc9c0 \uc54a\uc740 \uc870\ud68c(Non-repeatable read)\\n\\n\ud55c \ud2b8\ub79c\uc7ad\uc158 \ub0b4\uc758 \uac19\uc740 \ud589\uc5d0 \ub450 \ubc88 \uc774\uc0c1 \uc870\ud68c\uac00 \ubc1c\uc0dd\ud588\ub294\ub370, \uadf8 \uac12\uc774 \ub2e4\ub978 \ud604\uc0c1 \\n\uc608) A\uac00 \ub808\ucf54\ub4dc\ub97c \uc5ec\ub7ec \ubc88 \uc870\ud68c\ud558\ub358 \uc911 B\uac00 \ub808\ucf54\ub4dc\ub97c \ubcc0\uacbd\ud558\uc5ec A\uac00 \uc870\ud68c\ud55c \uac12\uc774 \ub2ec\ub77c\uc9c0\ub294 \uacbd\uc6b0 \\n\\n```mermaid\\n---\\ntitle: NON REPEATABLE READ\\n---\\nsequenceDiagram\\n participant Alice\\n participant Database\\n participant Bob\\n Bob->>Database: BEGIN\\n Bob->>Database: SELECT\\n Database->>+Bob: Alice\\n Alice->>Database: BEGIN\\n Alice->>Database: UPDATE(Alice to Bob)\\n Alice->>Database: COMMIT\\n Bob->>Database: SELECT\\n Database->>+Bob: Bob\\n```\\n\\n### \ud32c\ud140 \ub9ac\ub4dc(Phantom read, Phantom row)\\n\\n\ud55c \ud2b8\ub79c\uc7ad\uc158 \ub0b4\uc5d0\uc11c \ub3d9\uc77c\ud55c \ucffc\ub9ac \uc218\ud589\uc2dc, \uc218\ud589 \uacb0\uacfc\uac00 \ub2e4\ub978 \ud604\uc0c1 \\n\uc608) A\uac00 \ub808\ucf54\ub4dc\ub97c \uc870\ud68c\ud558\uace0 B\uac00 \ub808\ucf54\ub4dc\ub97c \ucd94\uac00\ud558\uc5ec A\uac00 \ub2e4\uc2dc \uc870\ud68c\ud560 \ub54c \uc874\uc7ac\ud558\uc9c0 \uc54a\uc740 \ub808\ucf54\ub4dc\uac00 \uc870\ud68c\ub418\ub294 \uacbd\uc6b0 \\n\\n```mermaid\\n---\\ntitle: PHANTOM READ\\n---\\nsequenceDiagram\\n participant Alice\\n participant Database\\n participant Bob\\n Bob->>Database: BEGIN(TRX-ID: 1)\\n Bob->>Database: SELECT COUNT\\n Database->>+Bob: 1\\n Alice->>Database: BEGIN(TRX-ID: 2)\\n Alice->>Database: INSERT(Bob)\\n Alice->>Database: COMMIT\\n Bob->>Database: SELECT COUNT\\n Database->>+Bob: 2\\n```\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\nReal My SQL 8.0 - 5\uc7a5 \ud2b8\ub79c\uc7ad\uc158\uacfc \uc7a0\uae08, \ubc31\uc740\ube48, \uc774\uc131\uc6b1 \\n[Isolation Level, MySQL](https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html)"},{"id":"test-double","metadata":{"permalink":"/test-double","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-04-\ud14c\uc2a4\ud2b8 \ub300\uc5ed.mdx","source":"@site/blog/2023-2/2023-04-04-\ud14c\uc2a4\ud2b8 \ub300\uc5ed.mdx","title":"\ud14c\uc2a4\ud2b8 \ub300\uc5ed","description":"\ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc774\ub780?","date":"2023-04-04T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 4\uc77c","tags":[{"label":"Test","permalink":"/tags/test"},{"label":"Mock","permalink":"/tags/mock"}],"readingTime":4.52,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ud14c\uc2a4\ud2b8 \ub300\uc5ed","slug":"test-double","tags":["Test","Mock"]},"prevItem":{"title":"\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900","permalink":"/transaction-and-isolation"},"nextItem":{"title":"\uc790\ubc14 \ud074\ub798\uc2a4 \ud30c\uc77c \uad6c\uc870","permalink":"/java-class-file"}},"content":"### \ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc774\ub780?\\n\\n\ubaa8\ub4e0 \uc720\ud615\uc758 \ud14c\uc2a4\ud2b8\ub97c \uc704\ud55c \uac00\uc9dc \uc758\uc874\uc131\uc744 \uc758\ubbf8\ud558\uace0, \ud14c\uc2a4\ud2b8\uac00 \uc2e4\ud589\ub420 \ub54c \ub2e4\ub978 \uac1d\uccb4\ub97c \ub300\uc2e0\ud55c\ub2e4. \\nGerard Meszaros\uc758 xUnit Test Patterns\ub77c\ub294 \ucc45\uc5d0\uc11c\ub294 \ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc744 \ub2e4\uc12f \uac00\uc9c0(\ub354\ubbf8, \uc2a4\ud141, \uc2a4\ud30c\uc774, \ubaa9, \ud398\uc774\ud06c)\ub85c \uad6c\ubd84\ud55c\ub2e4.\\n\\n\ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc758 \uae30\ubcf8 \uba54\ucee4\ub2c8\uc998\uc740 \ub2e4\ud615\uc131\uc744 \uc774\uc6a9\ud558\ub294 \ubc29\ubc95\uc774\ub2e4. \\n\uc678\ubd80 \uc11c\ube44\uc2a4\ub97c \uc0ac\uc6a9\ud558\ub294 \ucf54\ub4dc\ub97c \ud14c\uc2a4\ud2b8 \ud558\ub294 \uacbd\uc6b0, \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc815\uc758\ud558\uace0 \uc678\ubd80 \uc11c\ube44\uc2a4 \ub300\uc2e0 \ud14c\uc2a4\ud2b8 \uc6a9\ub3c4\uc758 \uad6c\ud604\uccb4\ub97c \uc0dd\uc131\ud558\ub294 \uac83\uc774\ub2e4.\\n\\n**\ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc758 \ud0c0\uc785 \uacc4\uce35 \uad6c\uc870**\\n\\n```mermaid\\nflowchart LR\\n Mock --\x3e Spy --\x3e Stub --\x3e Dummy --\x3e TestDouble\\n Fake --\x3e TestDouble\\n```\\n\\n### \ub354\ubbf8(Dummy)\\n\\n\uac00\uc7a5 \ub2e8\uc21c\ud558\uace0, \uc6d0\uc2dc\uc801\uc778 \uc720\ud615\uc758 \ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc774\ub2e4. \\n\uae30\ubcf8\uc801\uc73c\ub85c \uc544\ubb34 \uc77c\ub3c4 \ud558\uc9c0 \uc54a\ub294 \uad6c\ud604\uccb4\ub85c \uc778\uc2a4\ud134\uc2a4\ud654\uac00 \ud544\uc694\ud55c \uacbd\uc6b0 \uc0ac\uc6a9\ud55c\ub2e4. \\n\ub9cc\uc57d \uba54\uc11c\ub4dc\uac00 \ubb34\uc5b8\uac00 \ubc18\ud658\uc744 \ud574\uc57c\ud558\ub294 \uacbd\uc6b0 0, null\uacfc \uac19\uc740 \uac12\uc744 \ubc18\ud658\ud55c\ub2e4. \\n\\n### \uc2a4\ud141(Stub)\\n\\n\uc2dc\ub098\ub9ac\uc624\ub9c8\ub2e4 \ub2e4\ub978 \uac12(\ubbf8\ub9ac \uc900\ube44 \ub41c \uacb0\uacfc)\uc744 \ubc18\ud658\ud55c\ub2e4. \\n\uc774\ub97c \ud1b5\ud574 \ud2b9\uc815 \uc870\uac74\uc5d0\uc11c \uba54\uc11c\ub4dc\uac00 \uc608\uc0c1\ud55c\ub300\ub85c \ub3d9\uc791\ud558\ub294\uc9c0 \ud655\uc778\ud560 \uc218 \uc788\ub2e4. \\n\\n### \uc2a4\ud30c\uc774(Spy)\\n\\n\uc2a4\ud141\uacfc \uc720\uc0ac\ud558\uc9c0\ub9cc \ud638\ucd9c \uc5ec\ubd80\ub97c \uae30\ub85d\ud558\uac70\ub098 \ud638\ucd9c\ud560 \ub54c \uc804\ub2ec\ud55c \uc778\uc790\uac12\uc744 \uae30\ub85d\ud560 \uc218 \uc788\ub2e4. \\n\uc608) \uba54\uc77c \uc804\uc1a1 \uae30\ub2a5\uc744 \uac00\uc9c4 \uac1d\uccb4\ub97c \ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc73c\ub85c \uad6c\ud604\ud588\uc744 \ub54c \uba54\uc77c \uc804\uc1a1 \ud69f\uc218\ub97c \uae30\ub85d\ud55c\ub2e4. \\n\\n### \ubaa9, \ubaa8\uc758 \uac1d\uccb4(Mock)\\n\\n\ubaa9\uc740 \ub354\ubbf8, \uc2a4\ud141, \uc2a4\ud30c\uc774\ub97c \ud3ec\ud568\ud55c\ub2e4. \\n\ud638\ucd9c \uc2dc \uc0ac\uc804\uc5d0 \uc815\uc758\ub41c \uacb0\uacfc\ub97c \ubc18\ud658\ud558\uace0, \uc608\uc0c1\uce58 \ubabb\ud55c \ud638\ucd9c\uc774 \uc788\uc744 \uacbd\uc6b0 \uc608\uc678\ub97c \ub358\uc9c8 \uc218 \uc788\ub2e4. \\n\ub610\ud55c \ud638\ucd9c\uc5d0 \ub300\ud55c \uac80\uc99d\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\\n### \uac00\uc9dc(Fake)\\n\\nDOC\uc640 \ub3d9\uc77c\ud55c \uae30\ub2a5\uc744 \uc81c\uacf5\ud558\uc9c0\ub9cc, \ub354\uc6b1 \uac04\ub2e8\ud55c \ubc29\ubc95\uc73c\ub85c \uad6c\ud604\ub41c \uac83\uc774\ub2e4. \\n\uc608) \uc2e4\uc81c \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc640 \uc720\uc0ac\ud558\uac8c \ub3d9\uc791\ud558\ub294 \uac00\uc9dc \uac1d\uccb4\ub97c \ub9cc\ub4e4\uc5b4 \ud14c\uc2a4\ud2b8\ud560 \uc218 \uc788\ub2e4. \\n\\n:::note DOC(depended-on component)\\n\\n\uc758\uc874 \uad6c\uc131 \uc694\uc18c, DOC\ub97c \ud14c\uc2a4\ud2b8 \ub354\ube14\ub85c \ub300\uccb4\ud560 \uc218 \uc788\ub2e4. \\n\ud14c\uc2a4\ud2b8 \ub354\ube14\uc740 DOC\uc640 \ub3d9\uc77c\ud55c API\ub97c \uc81c\uacf5\ud574\uc57c \ud55c\ub2e4. \\n\\n:::\\n\\n### \uc0c1\ud638\uc791\uc6a9\uc5d0 \ub530\ub978 \ubaa9\uacfc \uc2a4\ud141 \uad6c\ubd84\\n\\n\ub2e8\uc704 \ud14c\uc2a4\ud2b8 p.149 \uc5d0\uc11c\ub294 \ud14c\uc2a4\ud2b8 \ub300\uc5ed\uc744 \ud06c\uac8c \ubaa9\uacfc \uc2a4\ud141\uc73c\ub85c \uad6c\ubd84\ud55c\ub2e4. \\n\ubaa9\uc740 SUT\uc640 \uad00\ub828\ub41c \uc0c1\ud638\uc791\uc6a9\uc744 \ubaa8\ubc29\ud558\uace0 \uac80\uc0ac\ud558\ub294 \ubc18\uba74, \uc2a4\ud141\uc740 \ub2e8\uc21c \ubaa8\ubc29\ub9cc \ud55c\ub2e4. \\n\\n| TestDouble | Mock | Stub |\\n| --- | --- | --- |\\n| \ud3ec\ud568 \uc720\ud615 | \ubaa9, \uc2a4\ud30c\uc774 | \uc2a4\ud141, \ub354\ubbf8, \ud398\uc774\ud06c |\\n| \uc6a9\ub3c4 | \uc678\ubd80\ub85c \ub098\uac00\ub294 \uc0c1\ud638\uc791\uc6a9\uc744 \ubaa8\ubc29\ud558\uace0 \uac80\uc0ac\ud558\ub294 \ub370 \uc0ac\uc6a9 | \ub0b4\ubd80\ub85c \ub4e4\uc5b4\uc624\ub294 \uc0c1\ud638\uc791\uc6a9\uc744 \ubaa8\ubc29\ud558\ub294 \ub370 \uc0ac\uc6a9 |\\n| \uc124\uba85 | SUT\uac00 \uc0c1\ud0dc\ub97c \ubcc0\uacbd\ud558\uae30 \uc704\ud55c \uc758\uc874\uc131\uc744 \ud638\ucd9c\ud558\ub294 \uac83\uc5d0 \ud574\ub2f9 | SUT\uac00 \uc785\ub825 \ub370\uc774\ud130\ub97c \uc5bb\uae30 \uc704\ud55c \uc758\uc874\uc131\uc744 \ud638\ucd9c\ud558\ub294 \uac83\uc5d0 \ud574\ub2f9\\n| \uc608\uc2dc | \uc774\uba54\uc77c \ubc1c\uc1a1 | \ub370\uc774\ud130 \uac80\uc0c9 |\\n\\n:::note SUT(system under test)\\n\\n\ud14c\uc2a4\ud2b8 \ub300\uc0c1 \uc2dc\uc2a4\ud15c \\n\ud14c\uc2a4\ud2b8\ub97c \ud558\ub824\ub294 \ub300\uc0c1\\n\\n:::\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc7a5\uc778 \uc815\uc2e0 \uc774\uc57c\uae30 - 3\uc7a5 \uace0\uae09 \ud14c\uc2a4\ud2b8 \uc8fc\ub3c4 \uac1c\ubc1c, \ub85c\ubc84\ud2b8 C. \ub9c8\ud2f4 \\n\ub2e8\uc704 \ud14c\uc2a4\ud2b8 - 5\uc7a5 \ubaa9\uacfc \ud14c\uc2a4\ud2b8 \ucde8\uc57d\uc131, \ube14\ub77c\ub514\ubbf8\ub974 \ucf54\ub9ac\ucf54\ud504 \\n\ud14c\uc2a4\ud2b8 \uc8fc\ub3c4 \uac1c\ubc1c \uc2dc\uc791\ud558\uae30 - 7\uc7a5 \ub300\uc5ed, \ucd5c\ubc94\uade0 \\n[\ud14c\uc2a4\ud2b8 \ub354\ube14, Martin Fowler](https://www.martinfowler.com/bliki/TestDouble.html) \\n[\ud14c\uc2a4\ud2b8 \uad00\ub828 \uc6a9\uc5b4 \uc815\ub9ac, Johngrib](https://johngrib.github.io/wiki/test-terms/) \\n[Test Double, Gerard Meszaros](http://xunitpatterns.com/Test%20Double.html)"},{"id":"java-class-file","metadata":{"permalink":"/java-class-file","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-03-\uc790\ubc14 \ud074\ub798\uc2a4\ud30c\uc77c \uad6c\uc870.mdx","source":"@site/blog/2023-2/2023-04-03-\uc790\ubc14 \ud074\ub798\uc2a4\ud30c\uc77c \uad6c\uc870.mdx","title":"\uc790\ubc14 \ud074\ub798\uc2a4 \ud30c\uc77c \uad6c\uc870","description":"\ud074\ub798\uc2a4 \ud30c\uc77c","date":"2023-04-03T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 3\uc77c","tags":[{"label":"Java","permalink":"/tags/java"},{"label":"Class","permalink":"/tags/class"}],"readingTime":5.63,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc790\ubc14 \ud074\ub798\uc2a4 \ud30c\uc77c \uad6c\uc870","slug":"java-class-file","tags":["Java","Class"]},"prevItem":{"title":"\ud14c\uc2a4\ud2b8 \ub300\uc5ed","permalink":"/test-double"},"nextItem":{"title":"\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30","permalink":"/custom-jdbc-template"}},"content":"### \ud074\ub798\uc2a4 \ud30c\uc77c\\n\\n\uc790\ubc14 \uc18c\uc2a4\ucf54\ub4dc\uac00 \uc2e4\ud589\uc774 \ub418\ub824\uba74 \uc790\ubc14 \ucef4\ud30c\uc77c\ub7ec(javac)\ub97c \ud1b5\ud574 \uc18c\uc2a4\ucf54\ub4dc\ub97c \ud074\ub798\uc2a4\ud30c\uc77c\ub85c \ubcc0\ud658\ud574\uc57c \ud55c\ub2e4. \\n\ucef4\ud30c\uc77c\ub41c \ud074\ub798\uc2a4\ud30c\uc77c\uc740 \uc5b4\ub5a4 \uad6c\uc870\ub85c \ub418\uc5b4\uc788\uc744\uae4c?\\n\\n### \ud074\ub798\uc2a4 \ud30c\uc77c\uc758 \ub370\uc774\ud130 \ud615\uc2dd\\n\\n8\ube44\ud2b8 \ubc14\uc774\ud2b8\uc758 \uc2a4\ud2b8\ub9bc\uc73c\ub85c \uad6c\uc131\ub41c\ub2e4. \\n16\ube44\ud2b8 \ubc0f 32\ube44\ud2b8\uc758 \ub370\uc774\ud130\ub294 \uac01\uac01 2\uac1c, 4\uac1c\uc758 \uc5f0\uc18d\ub41c 8\ube44\ud2b8\ub97c \uc77d\uc5b4\uc11c \uad6c\uc131\ub41c\ub2e4. \\n\uba40\ud2f0\ubc14\uc774\ud2b8\uc758 \uacbd\uc6b0 \ud56d\uc0c1 big endian \uc21c\uc11c\ub85c \uc800\uc7a5\ub41c\ub2e4. \\n\\nu1 \u2192 unsigned 1byte \\nu2 \u2192 unsigned 2byte \\nu4 \u2192 unsigned 4byte \\n\\n### \ud074\ub798\uc2a4 \ud30c\uc77c \uad6c\uc870\\n\\n```\\nClassFile {\\n u4 magic;\\n u2 minor_version;\\n u2 major_version;\\n u2 constant_pool_count;\\n cp_info constant_pool[constant_pool_count-1];\\n u2 access_flags;\\n u2 this_class;\\n u2 super_class;\\n u2 interfaces_count;\\n u2 interfaces[interfaces_count];\\n u2 fields_count;\\n field_info fields[fields_count];\\n u2 methods_count;\\n method_info methods[methods_count];\\n u2 attributes_count;\\n attribute_info attributes[attributes_count];\\n}\\n```\\n\\n### \ub9e4\uc9c1\ub118\ubc84\\n\\n\ubaa8\ub4e0 \ud074\ub798\uc2a4 \ud30c\uc77c\uc740 0xCAFEBABE\ub77c\ub294 \ub9e4\uc9c1\ub118\ubc84\ub85c \uc2dc\uc791\ud55c\ub2e4. \\n\ubcf4\ud1b5 \ub9e4\uc9c1\ub118\ubc84\ub294 \ud30c\uc77c \uc885\ub958\ub97c \uc2dd\ubcc4\ud558\ub294 \uc6a9\ub3c4\ub85c \uc0ac\uc6a9\ub41c\ub2e4. \\n\\n### \ud074\ub798\uc2a4 \ud30c\uc77c \ud3ec\ub9f7 \ubc84\uc804\\n\\n\ud074\ub798\uc2a4 \ud30c\uc77c \ubc84\uc804 \uac12\uc740 \ud074\ub798\uc2a4\ub85c\ub354\uc758 \ud638\ud658\uc131 \ubcf4\uc7a5\uc744 \uc704\ud574 \uaf2d \ud544\uc694\ud55c \uac12\uc774\ub2e4. \\n- Java 17 \ubc84\uc804\uc73c\ub85c \ube4c\ub4dc\ud55c\ub2e4\uba74 class version 61 ex) 00 00 00 3D\\n\\n\ud638\ud658\ub418\uc9c0 \uc54a\ub294 \ubc84\uc804\uc758 \ud074\ub798\uc2a4 \ud30c\uc77c\uc744 \ub85c\ub529\ud558\ub824\uace0 \ud558\ub294 \uacbd\uc6b0 \ub7f0\ud0c0\uc784\uc5d0 `UnsupportedClassVersionError` \uc608\uc678\uac00 \ubc1c\uc0dd\ud55c\ub2e4. \\n\\n**class\xa0file format major versions**\\n\\n| Java SE | Released | Major | Supported majors |\\n| --- | --- | --- | --- |\\n| 8 | March 2014 | 52 | 45 .. 52 |\\n| 9 | September 2017 | 53 | 45 .. 53 |\\n| 10 | March 2018 | 54 | 45 .. 54 |\\n| 11 | September 2018 | 55 | 45 .. 55 |\\n| 12 | March 2019 | 56 | 45 .. 56 |\\n| 13 | September 2019 | 57 | 45 .. 57 |\\n| 14 | March 2020 | 58 | 45 .. 58 |\\n| 15 | September 2020 | 59 | 45 .. 59 |\\n| 16 | March 2021 | 60 | 45 .. 60 |\\n| 17 | September 2021 | 61 | 45 .. 61 |\\n\\n### \uc0c1\uc218 \ud480\\n\\n2\ubc14\uc774\ud2b8\uc758 \uc0c1\uc218\uc758 \uac1c\uc218\uac12\uc774 \uba3c\uc800\uc624\uace0 \uadf8 \ub4a4\ub85c \ucf54\ub4dc\uc5d0 \ub4f1\uc7a5\ud558\ub294 \uc0c1\uc218\uac12\uc774 \ubaa8\uc5ec\uc788\ub2e4. \\n\ud074\ub798\uc2a4\uba85, \uc0c1\uc218\uba85, \uc0c1\uc218 \uac12, \ud544\ub4dc\uba85, \uba54\uc11c\ub4dc\uba85\uacfc \uac19\uc740 \uac12\ub4e4\uc774 \uc874\uc7ac\ud55c\ub2e4. \\nJVM\uc740 \ucf54\ub4dc \uc2e4\ud589 \uc2dc \ub7f0\ud0c0\uc784\uc5d0 \ubc30\uce58\ub41c \uba54\ubaa8\ub9ac\uac00 \uc544\ub2c8\ub77c, \ud574\ub2f9 \uc0c1\uc218 \ud480 \ud14c\uc774\ube14\uc744 \ucc3e\uc544\ubcf4\uace0 \ud544\uc694\ud55c \uac12\uc744 \ucc38\uc870\ud55c\ub2e4.\\n\\n### \uc561\uc138\uc2a4 \ud50c\ub798\uadf8\\n\\n\ud074\ub798\uc2a4, \uc778\ud130\ud398\uc774\uc2a4\uc640 \uac19\uc740 \ud30c\uc77c\uc758 \uc18d\uc131\uc744 \ud45c\uc2dc\ud55c\ub2e4. \\n\uc608\ub97c \ub4e4\uc5b4 public interface\ub85c \uc815\uc758\ub41c \uc778\ud130\ud398\uc774\uc2a4\uc758 \ud50c\ub798\uadf8\ub294 0x0601\uc774\ub2e4. \\n- \uacc4\uc0b0\uc740 \ub2e4\uc74c\uacfc \uac19\uc774 \uc774\ub8e8\uc5b4\uc9c4\ub2e4. `ACC_PUBLIC` xor `ACC_INTERFACE` xor `ACC_ABSTRACT`\\n\\n\uacf5\uc2dd\ubb38\uc11c\uc5d0 \ub4e4\uc5b4\uac00\uba74 \uac01 \ud50c\ub798\uadf8\uc5d0 \ub300\ud55c \uc124\uba85 + \ud50c\ub798\uadf8 \uc124\uc815\uc2dc \ub3d9\uc2dc\uc5d0 \uc124\uc815\ub418\uba74 \uc548\ub418\ub294 \ud50c\ub798\uadf8\uc640 \uac19\uc740 \uc124\uba85\uc774 \uc790\uc138\ud558\uac8c \ub098\uc640\uc788\ub2e4.\\n\\n**Class access and property modifiers**\\n\\n| Flag Name | Value | Interpretation |\\n| --- | --- | --- |\\n| ACC_PUBLIC | 0x0001 | Declared\xa0public; may be accessed from outside its package. |\\n| ACC_FINAL | 0x0010 | Declared\xa0final; no subclasses allowed. |\\n| ACC_SUPER | 0x0020 | Treat superclass methods specially when invoked by the\xa0invokespecial\xa0instruction. |\\n| ACC_INTERFACE | 0x0200 | Is an interface, not a class. |\\n| ACC_ABSTRACT | 0x0400 | Declared\xa0abstract; must not be instantiated. |\\n| ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |\\n| ACC_ANNOTATION | 0x2000 | Declared as an annotation type. |\\n| ACC_ENUM | 0x4000 | Declared as an\xa0enum\xa0type. |\\n| ACC_MODULE | 0x8000 | Is a module, not a class or interface. |\\n\\n### this_class\\n\\n\ud074\ub798\uc2a4\uba85\uacfc \uac19\uc740 \uc774\ub984\uc744 \ud45c\ud604\ud558\ub294 \uac12\uc73c\ub85c, \uc0c1\uc218 \ud480\uc5d0\uc11c \ud074\ub798\uc2a4\uba85\uacfc \uc77c\uce58\ud558\ub294 \ud56d\ubaa9\uc758 \uc778\ub371\uc2a4\ub97c \ucc38\uc870\ud55c\ub2e4. \\n\ud574\ub2f9 \uc778\ub371\uc2a4\uc758 \ud56d\ubaa9\uc740 `CONSTANT_Class_infoclass` \ud615\uc2dd\uc758 \uac12\uc774\uc5b4\uc57c \ud55c\ub2e4. \\n\\n### super_class\\n\\n\uc0c1\uc218 \ud480\uc5d0\uc11c \uc288\ud37c \ud074\ub798\uc2a4\uc758 \uc774\ub984\uacfc \uc77c\uce58\ud558\ub294 \ud56d\ubaa9\uc758 \uc778\ub371\uc2a4\ub97c \ucc38\uc870\ud55c\ub2e4. \\n\uc544\ubb34\uac83\ub3c4 \uc0c1\uc18d\ud558\uc9c0 \uc54a\ub294 \ud074\ub798\uc2a4\uc758 \uacbd\uc6b0 `java.lang.Object`\uc758 \uc778\ub371\uc2a4 \uac12\uc774 \ub4e4\uc5b4\uc788\ub2e4.\\n\\n### interface, field, method\\n\\n\uac01\uac01\uc758 \uac1c\uc218\uc640, \uc815\ubcf4\uc5d0 \ub300\ud55c \uac12\uc774 \ub4e4\uc5b4\uc788\ub2e4. \\ninterface, field, method\ub97c \ud45c\uc2dc\ud558\ub294 \ubc29\ubc95\uc774 \uac01\uac01 \ub2e4\ub974\uace0, \uc811\uadfc\uc790\uc5d0 \ub300\ud55c \ud50c\ub798\uadf8\ub3c4 \uac01\uac01 \ub2e4\ub974\ub2e4.\\n\\n### attributes\\n\\n\ud574\ub2f9 \ud074\ub798\uc2a4 \ud30c\uc77c\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \ucd94\uac00 \uc815\ubcf4\uc758 \ubaa8\uc74c\uc774\ub2e4. \uc608) \uc18c\uc2a4\ud30c\uc77c\uba85 \\n\uc815\ud574\uc9c4 \ud074\ub798\uc2a4 \ud30c\uc77c\uc758 \uad6c\uc870\ub97c \ud655\uc7a5\ud558\ub294 \uc5ed\ud560\uc744 \ud55c\ub2e4. \\n\\n### \ud074\ub798\uc2a4 \ud30c\uc77c \ud655\uc778\ud558\uba74\uc11c \uc0ac\uc6a9\ud55c \ud234\\n\\nIntelliJ plugin - BinEd \\nIntelliJ plugin - jclasslib Bytecode Viewer\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n2\uc7a5 JVM \uc774\uc57c\uae30, \uc790\ubc14 \ucd5c\uc801\ud654 \\n[Class file in Java, File Format](https://docs.fileformat.com/ko/programming/class/) \\n[java se11 Class \ud30c\uc77c \ud615\uc2dd, Oracle](https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html) \\n[java se17 Class \ud30c\uc77c \ud615\uc2dd, Oracle](https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html)"},{"id":"custom-jdbc-template","metadata":{"permalink":"/custom-jdbc-template","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-02-\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30.mdx","source":"@site/blog/2023-2/2023-04-02-\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30.mdx","title":"\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30","description":"\uccb4\uc2a4 \ubbf8\uc158\uc5d0\uc11c\ub294 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \uac12\uc744 \uac00\uc838\uc624\uae30 \uc704\ud574 DAO\ub97c \uc0ac\uc6a9\ud588\ub2e4.","date":"2023-04-02T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 2\uc77c","tags":[{"label":"JDBC","permalink":"/tags/jdbc"},{"label":"Java","permalink":"/tags/java"}],"readingTime":9.025,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30","slug":"custom-jdbc-template","tags":["JDBC","Java"]},"prevItem":{"title":"\uc790\ubc14 \ud074\ub798\uc2a4 \ud30c\uc77c \uad6c\uc870","permalink":"/java-class-file"},"nextItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 1 \ud68c\uace0","permalink":"/woowacourse-level1-retrospective"}},"content":"import Tabs from \\"@theme/Tabs\\";\\nimport TabItem from \\"@theme/TabItem\\";\\n\\n\uccb4\uc2a4 \ubbf8\uc158\uc5d0\uc11c\ub294 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \uac12\uc744 \uac00\uc838\uc624\uae30 \uc704\ud574 DAO\ub97c \uc0ac\uc6a9\ud588\ub2e4. \\n\uc774 \ub54c JDBC\ub97c \uc0ac\uc6a9\ud560 \ub54c \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc758 \ucee4\ub125\uc158\uc744 \uc5bb\uace0, try-with-resource\ub97c \uc0ac\uc6a9\ud558\ub294 \ubd80\ubd84\uc774 \ubc18\ubcf5\ub418\uc5c8\ub2e4. \\n\ud15c\ud50c\ub9bf \ucf5c\ubc31 \ud328\ud134\uc744 \uc774\uc6a9\ud558\uc5ec \ub098\ub9cc\uc758 JdbcTemplate\uc744 \ub9cc\ub4e4\uc5b4\ubcf4\uc558\ub2e4. \\n\\n### \uae30\uc874 \ucf54\ub4dc\\n\\n\\n\\n\\n```java\\npublic class User {\\n private final int id;\\n private final String name;\\n\\n public User(final int id, final String name) {\\n this.id = id;\\n this.name = name;\\n }\\n\\n public int getId() {\\n return id;\\n }\\n\\n public String getName() {\\n return name;\\n }\\n}\\n```\\n\\n\\n\\n\\n\\n```java\\npublic class UserDao {\\n private final ConnectionPool connectionPool;\\n\\n public UserDao(final ConnectionPool connectionPool) {\\n this.connectionPool = connectionPool;\\n }\\n\\n public void insert(final String name) {\\n final Connection connection = connectionPool.getConnection();\\n final String query = \\"INSERT INTO User (name) VALUES (?)\\";\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n preparedStatement.setString(1, name);\\n preparedStatement.executeUpdate();\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n\\n public void delete(final int userId) {\\n final Connection connection = connectionPool.getConnection();\\n final String query = \\"DELETE FROM user WHERE id = ?\\";\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n preparedStatement.setInt(1, userId);\\n preparedStatement.executeUpdate();\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n\\n public User findById(final int userId) {\\n final Connection connection = connectionPool.getConnection();\\n final String query = \\"SELECT * FROM user WHERE id = ?\\";\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n preparedStatement.setInt(1, userId);\\n final ResultSet resultSet = preparedStatement.executeQuery();\\n if (resultSet.next()) {\\n return new User(\\n resultSet.getInt(\\"id\\"),\\n resultSet.getString(\\"name\\")\\n );\\n }\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n return null;\\n }\\n\\n public List findAll() {\\n final Connection connection = connectionPool.getConnection();\\n final String query = \\"SELECT * FROM user\\";\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n final ResultSet resultSet = preparedStatement.executeQuery();\\n final List result = new ArrayList<>();\\n while (resultSet.next()) {\\n result.add(new User(\\n resultSet.getInt(\\"id\\"),\\n resultSet.getString(\\"name\\")\\n ));\\n }\\n return result;\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n}\\n```\\n\\n\\n\\n\\n\\n```java\\npublic class ConnectionPool {\\n private static final String SERVER = \\"localhost:13306\\";\\n private static final String DATABASE = \\"chess\\";\\n private static final String OPTION = \\"?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true\\";\\n private static final String URL = \\"jdbc:mysql://\\" + SERVER + \\"/\\" + DATABASE + OPTION;\\n private static final String USERNAME = \\"root\\";\\n private static final String PASSWORD = \\"root\\";\\n\\n private final AtomicInteger index = new AtomicInteger();\\n private final List connections;\\n\\n public ConnectionPool(final int connectionCount) {\\n connections = generateConnections(connectionCount);\\n }\\n\\n private List generateConnections(final int connectionCount) {\\n return Stream.generate(this::generateConnection)\\n .limit(connectionCount)\\n .collect(toList());\\n }\\n\\n private Connection generateConnection() {\\n try {\\n return DriverManager.getConnection(URL, USERNAME, PASSWORD);\\n } catch (SQLException e) {\\n throw new IllegalStateException(\\"\ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.\\");\\n }\\n }\\n\\n public Connection getConnection() {\\n int currentIndex = index.getAndIncrement();\\n return connections.get(currentIndex % connections.size());\\n }\\n}\\n```\\n\\n\\n\\n\\n### SELECT, DELETE \uc911\ubcf5 \uc81c\uac70\\n\\n\ubcc0\ud558\uc9c0 \uc54a\ub294 \ubd80\ubd84: try-with-resource, preparedStatement\ub97c \uc0ac\uc6a9\ud558\ub294 \ubd80\ubd84, executeUpdate\ub85c \uc2e4\ud589 \ub4f1\ub4f1 \\n\ubcc0\ud558\ub294 \ubd80\ubd84: SQL Query, \ub9e4\uac1c\ubcc0\uc218 \\n\\n\ub2e4\uc74c\uacfc \uac19\uc774 \ucffc\ub9ac\ub97c \uc2e4\ud589\ud558\ub294 \ubd80\ubd84\uc744 \ubd84\ub9ac\ud558\uace0 \uac00\ubcc0\uc778\uc218\ub97c \uc0ac\uc6a9\ud55c\ub2e4\uba74 SELECT\uc640 DELETE\uc758 \uc911\ubcf5\uc744 \uc81c\uac70\ud560 \uc218 \uc788\ub2e4. \\n\\n```java\\npublic void insert(final String name) {\\n final String query = \\"INSERT INTO User (name) VALUES (?)\\";\\n executeUpdate(query, name);\\n}\\n\\npublic void delete(final int userId) {\\n final String query = \\"DELETE FROM user WHERE user_id = ?\\";\\n executeUpdate(query, userId);\\n}\\n\\nprivate void executeUpdate(final String query, final Object... parameters) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n for (int i = 1; i <= parameters.length; i++) {\\n preparedStatement.setObject(i, parameters[i - 1]);\\n }\\n preparedStatement.executeUpdate();\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n}\\n```\\n\\n### \uc870\ud68c \ubd84\ub9ac\ud558\uae30 - 1. \ucf5c\ubc31\uc744 \uc704\ud55c \uc778\ud130\ud398\uc774\uc2a4 \uc815\uc758\\n\\n\uc870\ud68c\ub294 INSERT, DELETE\uc640 \ub2ec\ub9ac \uac12\uc744 \ubc18\ud658\ubc1b\uc544\uc57c \ud558\uae30 \ub54c\ubb38\uc5d0 \ub2e4\ub978 \ubc29\ubc95\uc744 \uc0ac\uc6a9\ud574\uc57c \ud55c\ub2e4. \\n\uc774 \ub54c \ucf5c\ubc31\uc774\ub77c\ub294 \uac83\uc744 \uc0ac\uc6a9\ud558\uc5ec \uc911\ubcf5\uc744 \uc81c\uac70\ud560 \uc218 \uc788\ub2e4. \\n\\n:::note \ucf5c\ubc31(Callback)\\n\\n\ud504\ub85c\uadf8\ub798\ubc0d\uc5d0\uc11c \ucf5c\ubc31\uc740 \ub2e4\ub978 \ucf54\ub4dc\uc758 \uc778\uc218\ub85c \ub118\uaca8\uc8fc\ub294 \uc2e4\ud589 \uac00\ub2a5\ud55c \ucf54\ub4dc\ub97c \ub73b\ud55c\ub2e4. \\n\uc790\ubc14\uc5d0\uc11c\ub294 \ub78c\ub2e4\ub098 \uc775\uba85 \ud074\ub798\uc2a4\ub97c \ub118\uaca8\uc11c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n```mermaid\\nflowchart LR\\n \ud074\ub77c\uc774\uc5b8\ud2b8 -- \ucf5c\ubc31\uc804\ub2ec --\x3e \uba54\uc11c\ub4dc\\n \uba54\uc11c\ub4dc -- \ub0b4\ubd80\ud638\ucd9c --\x3e \uc804\ub2ec\ubc1b\uc740\ucf5c\ubc31\\n```\\n\\n:::\\n\\n\\n\ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \uac12\uc744 \uc870\ud68c\ud558\uace0, \ud574\ub2f9 \uac12\uc744 \uac1d\uccb4\ub85c \ub9e4\ud551\ud558\uc5ec \uac12\uc744 \ubc18\ud658\ud574\uc57c \ud55c\ub2e4. \\nexecuteQuery\ub85c \uc870\ud68c\ud55c \uac12\uc740 ResultSet \uc548\uc5d0 \ub4e4\uc5b4\uac00\uc788\ub2e4. \\n\uc774\ub97c \uc6d0\ud558\ub294 \ud0c0\uc785\uc758 \uac12\uc73c\ub85c \ubcc0\ud658\ud574\uc57c\ud558\ub2c8 \uc77c\ub2e8 \ucf5c\ubc31\uc744 \uc704\ud55c \uc778\ud130\ud398\uc774\uc2a4\ub97c \ub9cc\ub4e4\uc5b4\uc57c \ud55c\ub2e4. \\n\\n```java\\n@FunctionalInterface\\npublic interface RowMapper {\\n User mapRow(final ResultSet resultSet) throws SQLException;\\n}\\n```\\n\\n### \uc870\ud68c \ubd84\ub9ac\ud558\uae30 - 2. \ub2e8\uac74 \uc870\ud68c\\n\\n\uc704\uc5d0\uc11c \uc815\uc758\ud55c RowMapper\ub97c \uba54\uc11c\ub4dc\uc5d0\uc11c \uc5b4\ub5bb\uac8c \uc0ac\uc6a9\ud574\uc57c \ud560\uae4c? \\n\uc544\ub798\uc640 \uac19\uc774 SQL \ucffc\ub9ac, RowMapper, \ud30c\ub77c\ubbf8\ud130\ub97c \ubd84\ub9ac\ud55c \uba54\uc11c\ub4dc\uc5d0 \ub118\uaca8\uc8fc\uace0 \ucffc\ub9ac \uc2e4\ud589 \ud6c4 \ub9e4\ud551\ud55c \uac12\uc744 \ubc18\ud658\ud558\ub3c4\ub85d \ud55c\ub2e4. \\n\\n```java\\npublic User findById(final int userId) {\\n final String query = \\"SELECT * FROM user WHERE id = ?\\";\\n return queryForSingleResult(query, resultSet -> {\\n final int id = resultSet.getInt(\\"id\\");\\n final String name = resultSet.getString(\\"name\\");\\n return new User(id, name);\\n }, userId);\\n}\\n\\nprivate User queryForSingleResult(\\n final String query,\\n final RowMapper rowMapper,\\n final Object... parameters\\n) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query);\\n final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {\\n if (resultSet.next()) {\\n return rowMapper.mapRow(resultSet);\\n }\\n return null;\\n } catch (SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n}\\n\\nprivate ResultSet executeQuery(\\n final PreparedStatement preparedStatement,\\n final Object[] parameters) throws SQLException {\\n for (int i = 1; i <= parameters.length; i++) {\\n preparedStatement.setObject(i, parameters[i - 1]);\\n }\\n return preparedStatement.executeQuery();\\n}\\n```\\n\\n### \uc870\ud68c \ubd84\ub9ac\ud558\uae30 - 3. \ub2e4\uac74 \uc870\ud68c\\n\\n\ub2e8\uac74 \uc870\ud68c\uc640 \uc720\uc0ac\ud558\ub2e4.\\n\\n```java\\npublic List findAll() {\\n final String query = \\"SELECT * FROM user\\";\\n return query(query, resultSet -> {\\n final int id = resultSet.getInt(\\"id\\");\\n final String name = resultSet.getString(\\"name\\");\\n return new User(id, name);\\n });\\n}\\n\\nprivate List query(final String query, final RowMapper rowMapper, final Object... parameters) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query);\\n final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {\\n final List result = new ArrayList<>();\\n while (resultSet.next()) {\\n result.add(rowMapper.mapRow(resultSet));\\n }\\n return result;\\n } catch (SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n}\\n\\nprivate ResultSet executeQuery(\\n final PreparedStatement preparedStatement,\\n final Object[] parameters) throws SQLException {\\n for (int i = 1; i <= parameters.length; i++) {\\n preparedStatement.setObject(i, parameters[i - 1]);\\n }\\n return preparedStatement.executeQuery();\\n}\\n```\\n\\n### \uc81c\ub124\ub9ad \uc0ac\uc6a9\ud558\uae30\\n\\n\uc704\uc758 \ucf54\ub4dc\ub294 User\ub97c \uc870\ud68c\ud560 \ub54c\ub9cc \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. \\n\uc544\ub798\uc640 \uac19\uc774 \uc81c\ub124\ub9ad\uc744 \uc801\uc6a9\ud558\uc5ec \ub2e4\ub978 Dao\uc5d0\uc11c\ub3c4 \uc0ac\uc6a9 \uac00\ub2a5\ud558\ub3c4\ub85d \ubcc0\uacbd\ud560 \uc218 \uc788\ub2e4.\\n\\n```java\\n@FunctionalInterface\\npublic interface RowMapper {\\n T mapRow(final ResultSet resultSet) throws SQLException;\\n}\\n\\nprivate List query(final String query, final RowMapper rowMapper, final Object... parameters) {...}\\nprivate T queryForSingleResult(final String query, final RowMapper rowMapper, final Object... parameters) {...}\\n```\\n\\n### \uba54\uc11c\ub4dc \ubd84\ub9ac\ud55c \ubd80\ubd84 \ud074\ub798\uc2a4\ub85c \ubd84\ub9ac\ud558\uae30 + Optional \uc0ac\uc6a9\ud558\uae30\\n\\n\uba54\uc11c\ub4dc\ub85c \ubd84\ub9ac\ud55c \ubd80\ubd84\uc744 JdbcTemplate\uc774\ub77c\ub294 \ud074\ub798\uc2a4\ub97c \ub9cc\ub4e4\uc5b4 \uc62e\uae34\ub2e4. \\n\ub610\ud55c null\uc744 \ubc18\ud658\ud558\uae30 \ubcf4\ub2e8 Optional\ub85c \uac10\uc2f8\uc11c \ubc18\ud658\ud558\ub3c4\ub85d \ubcc0\uacbd\ud55c\ub2e4. \\n\ucd5c\uc885\uc801\uc73c\ub85c \uc544\ub798\uc640 \uac19\uc740 \ucf54\ub4dc\uac00 \uc644\uc131\ub41c\ub2e4.\\n\\n\\n\\n\\n```java\\npublic class UserDao {\\n private final RowMapper rowMapper = resultSet -> {\\n final int id = resultSet.getInt(\\"id\\");\\n final String name = resultSet.getString(\\"name\\");\\n return new User(id, name);\\n };\\n private final JdbcTemplate jdbcTemplate;\\n\\n public UserDao(final JdbcTemplate jdbcTemplate) {\\n this.jdbcTemplate = jdbcTemplate;\\n }\\n\\n public void insert(final String name) {\\n final String query = \\"INSERT INTO User (name) VALUES (?)\\";\\n jdbcTemplate.executeUpdate(query, name);\\n }\\n\\n public void delete(final int userId) {\\n final String query = \\"DELETE FROM user WHERE user_id = ?\\";\\n jdbcTemplate.executeUpdate(query, userId);\\n }\\n\\n public Optional findById(final int userId) {\\n final String query = \\"SELECT * FROM user WHERE id = ?\\";\\n return jdbcTemplate.queryForSingleResult(query, rowMapper, userId);\\n }\\n\\n public List findAll() {\\n final String query = \\"SELECT * FROM user\\";\\n return jdbcTemplate.query(query, rowMapper);\\n }\\n}\\n```\\n\\n\\n\\n\\n```java\\npublic class JdbcTemplate {\\n private final ConnectionPool connectionPool;\\n\\n public JdbcTemplate(final ConnectionPool connectionPool) {\\n this.connectionPool = connectionPool;\\n }\\n\\n public void executeUpdate(final String query, final Object... parameters) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {\\n for (int i = 1; i <= parameters.length; i++) {\\n preparedStatement.setObject(i, parameters[i - 1]);\\n }\\n preparedStatement.executeUpdate();\\n } catch (final SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n\\n public Optional queryForSingleResult(\\n final String query,\\n final RowMapper rowMapper,\\n final Object... parameters\\n ) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query);\\n final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {\\n if (resultSet.next()) {\\n return Optional.of(rowMapper.mapRow(resultSet));\\n }\\n return Optional.empty();\\n } catch (SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n\\n private ResultSet executeQuery(\\n final PreparedStatement preparedStatement,\\n final Object[] parameters\\n ) throws SQLException {\\n for (int i = 1; i <= parameters.length; i++) {\\n preparedStatement.setObject(i, parameters[i - 1]);\\n }\\n return preparedStatement.executeQuery();\\n }\\n\\n public List query(\\n final String query,\\n final RowMapper rowMapper,\\n final Object... parameters\\n ) {\\n final Connection connection = connectionPool.getConnection();\\n try (final PreparedStatement preparedStatement = connection.prepareStatement(query);\\n final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {\\n final List result = new ArrayList<>();\\n while (resultSet.next()) {\\n result.add(rowMapper.mapRow(resultSet));\\n }\\n return result;\\n } catch (SQLException e) {\\n throw new IllegalArgumentException(e.getMessage());\\n }\\n }\\n}\\n```\\n\\n\\n"},{"id":"woowacourse-level1-retrospective","metadata":{"permalink":"/woowacourse-level1-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-2/2023-04-01-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca81 \ud68c\uace0.mdx","source":"@site/blog/2023-2/2023-04-01-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca81 \ud68c\uace0.mdx","title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 1 \ud68c\uace0","description":"\ub808\ubca8 1\uc774 \ub05d\ub0ac\ub2e4.","date":"2023-04-01T00:00:00.000Z","formattedDate":"2023\ub144 4\uc6d4 1\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":7.48,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 1 \ud68c\uace0","slug":"woowacourse-level1-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30","permalink":"/custom-jdbc-template"},"nextItem":{"title":"\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0","permalink":"/chess-retrospective"}},"content":"\ub808\ubca8 1\uc774 \ub05d\ub0ac\ub2e4. \\n\uc6b0\ud14c\ucf54\ub97c \uc2dc\uc791\ud558\uae30 \uc804 \ub0b4\uac00 \uc815\ud574\ub450\uc5c8\ub358 \ubaa9\ud45c \uc774\uc0c1\uc73c\ub85c \ub2ec\uc131\ud588\uae30 \ub54c\ubb38\uc5d0 \ub9e4\uc6b0 \ub9cc\uc871\uc2a4\ub7fd\ub2e4. \\n\ud63c\uc790 \ub3c5\ud559\uc744 \ud560 \ub550 \uc774 \ubc29\ud5a5\uc73c\ub85c \uacf5\ubd80\ud558\ub294 \uac8c \ub9de\ub294\uc9c0 \uacc4\uc18d \ubc18\ucd94\ud558\ub2e4 \uacb0\uad6d \ubb34\uae30\ub825\ud568\uc5d0 \ube60\uc838\ub4e4\uc5c8\ub2e4. \\n\ud558\uc9c0\ub9cc \uc774\uc81c\ub294 \uac19\uc774 \uacf5\ubd80\ud560 \uc0ac\ub78c\ub3c4 \uc788\uace0, \uc774\uc57c\uae30\ud560 \uc0ac\ub78c\ub3c4 \uc788\uae30 \ub54c\ubb38\uc5d0 \uc990\uae30\ub294 \uc77c\ub9cc \ub0a8\uc740 \uac83 \uac19\ub2e4. \\n\\n### Keep\\n\\n**\ub098\ub9cc\uc758 \ub8e8\ud2f4 \ub9cc\ub4e4\uae30** \\n\\n\uc2a4\uc2a4\ub85c\uac00 \uc678\ubd80\uc758 \uc601\ud5a5\uc744 \ub9ce\uc774 \ubc1b\ub294\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\ucd5c\ub300\ud55c \uafb8\uc900\ud788 \ud560 \uc218 \uc788\ub294 \uc2dc\uac04\uc744 \ub9cc\ub4dc\ub294 \uac83\uc774 \uc911\uc694\ud558\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\\n\ub9e4\uc77c 8\uc2dc\uc5d0 \ub3c4\ucc29\ud558\uc5ec \uc544\uce68\uc5d0 \ud574\uc57c \ud560 \uc77c\uc744 \uc815\ub9ac\ud558\uac70\ub098, \uc6b0\uc120\uc21c\uc704\uc5d0 \ub530\ub77c \ucc98\ub9ac\ud558\uace0 \\n\uc18c\ud654\ub2a5\ub825\uc774 \ubd80\uc871\ud558\uae30 \ub54c\ubb38\uc5d0 \uc810\uc2ec\uc740 \ub3c4\uc2dc\ub77d(\uadf8\ub798\ubd24\uc790 \uacc4\ub7802\uac1c)\uc744 \uc900\ube44\ud558\uace0 \\n\ud56d\uc0c1 \ub611\uac19\uc740 \ucee8\ub514\uc158\uc744 \uc720\uc9c0\ud558\uae30 \uc704\ud574 \ud56d\uc0c1 6\uc2dc\uc5d0 \uc9d1\uc5d0 \uac04\ub2e4. \\n\uc774\uc81c \ubc14\ube60\uc9c8 \ud14c\ub2c8 \uc77c\ucc0d \uc9d1\uc5d0 \uac00\ub294 \uc77c\uc740 \uc5b4\uca54 \uc218 \uc5c6\uc774 \uc904\uc5b4\ub4e4\uaca0\uc9c0\ub9cc\ud83d\ude22 \\n\\n\uc120\ud0dd\ub3c4 \ube44\uc6a9\uc774\ub2e4. \uc55e\uc73c\ub85c \uc758\uc0ac\uacb0\uc815\uc774 \ud544\uc694 \uc5c6\ub294 \ubd80\ubd84\uc744 \ucd5c\ub300\ud55c \ub9ce\uc774 \ub9cc\ub4e4\uc5b4\uc57c\uaca0\ub2e4. \\n\\n**\ud06c\ub8e8\ub4e4\uacfc \uce5c\ud558\uac8c \uc9c0\ub0b4\uae30** \\n\\n10\uba85 \uc815\ub3c4\uc758 \ud06c\ub8e8\uc758 \ub2c9\ub124\uc784\uc744 \uc678\uc6b0\uace0 \uce5c\ud558\uac8c \uc9c0\ub0b8\ub2e4\uba74 \uc131\uacf5\uc801\uc774\ub77c\uace0 \uc0dd\uac01\ud588\uc5c8\ub2e4. \\n\ud558\ub2e4 \ubcf4\ub2c8 \ub354 \ub9ce\uc740 \ud06c\ub8e8\ub4e4\uc758 \ub2c9\ub124\uc784\uc744 \uc678\uc6b4 \uac83 \uac19\ub2e4. \\n\uc55e\uc73c\ub85c\ub3c4 \ud06c\ub8e8\ub4e4\uacfc \uce5c\ud558\uac8c \uc9c0\ub0b4\uace0 \uc544\ubb34 \ub54c\ub098 \ub9d0\uc744 \uac78 \uc218 \uc788\ub294 \ud06c\ub8e8\uac00 \ub298\uc5b4\ub098\uae38 :) \\n\\n**\uae00\uc4f0\uae30** \\n\\n\uae00\uc744 \uc798 \uc4f0\ub294 \ud3b8\uc740 \uc544\ub2c8\uc9c0\ub9cc \uafb8\uc900\ud788 \uc791\uc131\ud558\ub824\uace0 \ub178\ub825\ud588\ub2e4. \\n\ub9e4 \ubbf8\uc158\ub9c8\ub2e4 \ud68c\uace0\ub97c \uc791\uc131\ud558\ub2c8 \uc0dd\uac01\ub3c4 \uc815\ub9ac\ub418\uace0 \uac1c\uc120\uc810\ub3c4 \ucc3e\uc744 \uc218 \uc788\uc5b4\uc11c \uc88b\uc558\ub2e4. \\n\\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\uc5d0\ub294 \ub808\ubca8\ub9c8\ub2e4 \uae00\uc4f0\uae30\ub97c \uc9c4\ud589\ud558\ub294\ub370, \uc6b4\uc774 \uc88b\uac8c \uae00\uc4f0\uae30 \uc0c1\uc744 \ubc1b\uc558\ub2e4. \\n\uc0ac\uc2e4 \uac89\uc73c\ub85c \ub4dc\ub7ec\ub0b4\uc9c0 \uc54a\uc558\uc9c0\ub9cc \uaf2d \ubc1b\uc544\ubcf4\uace0 \uc2f6\uc5c8\ub2e4. \\n\uae00\uc4f0\uae30 \uc870\uc6d0, \ud22c\ud45c\ud574 \uc900 \ud06c\ub8e8\ub4e4\uc5d0\uac8c \ub108\ubb34 \uac10\uc0ac\ud558\ub2e4. \\n\\n**\ucf54\ub4dc \ub9ac\ubdf0 \uc2a4\ud130\ub514** \\n\\n\ub204\ub204, \uc8fc\ub178, \ub2e4\uc990, \ub9d0\ub791, \ubc15\uc2a4\ud130, \uc624\uc789, \uae43\uc9f1\uc640 \ucf54\ub4dc \ub9ac\ubdf0 \uc2a4\ud130\ub514\ub97c \uc9c4\ud589\ud588\ub2e4. \\n\uacfc\uc5f0 \ub3c4\uc6c0\uc774 \ub420\uae4c \uc0dd\uac01\ud588\uc9c0\ub9cc \uacb0\uacfc\uc801\uc73c\ub85c\ub294 \ucf54\ub4dc \ub9ac\ubdf0\ub97c \ud558\uba74\uc11c \uc131\uc7a5\uc744 \ub9ce\uc774 \ud55c \uac83 \uac19\ub2e4. \\n\ud22c\uc790\ud55c \uc2dc\uac04 \ub300\ube44 \uac00\uc131\ube44\uac00 \uc88b\uc740 \ud65c\ub3d9\uc774\uc5c8\ub2e4. \\n\ub204\ub204\uac00 \uc2a4\ud130\ub514\uc7a5\uc778\ub370 \uacfc\uc5f0 \uafb8\uc900\ud788 \uc774\uc5b4\ub098\uac00\ub824\ub098? \\n\\n**\ub808\ubca8 \uc778\ud130\ubdf0**\\n\\n\uc778\ud130\ubdf0\ud560 \ub54c \ub9ce\uc774 \ub5a8\uc9c0 \uc54a\uc544\uc11c \uc88b\uc558\ub2e4. \\n\ub0a8\ub4e4 \uc55e\uc5d0\uc11c \uc774\uc57c\uae30\ub97c \ud558\uac70\ub098, \uba74\uc811\uc744 \ubcf4\uba74 \ud56d\uc0c1 \uc5c4\uccad \ub5a8\uc5b4\uc11c \uac71\uc815\ud588\ub294\ub370 \\n\uae30\uc220\uc801\uc778 \uc9c8\ubb38\uc744 \ubc1b\uc558\uc744 \ub54c \ub5a8\uc9c0 \uc54a\uace0 \uc798 \ub300\ub2f5\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \uc0dd\ud65c\uc744 \ud558\uba74\uc11c \ub2e4\ub978 \ud06c\ub8e8\uac00 \uc9c8\ubb38\ud588\uc744 \ub54c, \ucd5c\ub300\ud55c \uc774\ud574\ud558\uae30 \uc27d\uac8c \uc124\uba85\ud558\ub824\uace0 \ud588\ub358 \uacbd\ud5d8\uc774 \ub3c4\uc6c0\uc774 \ub41c \uac83 \uac19\ub2e4. \\n\uc774\ud6c4 \ub808\ubca8 \uc778\ud130\ubdf0\ub97c \uc9c4\ud589\ud560 \ub54c \ub2e4\uc74c\uacfc \uac19\uc740 \ubd80\ubd84\uc744 \uace0\ub824\ud558\uba74 \ub354 \uc88b\uc744 \uac83 \uac19\ub2e4. \\n- \ub300\ub2f5\ud558\uba74\uc11c \uc9c8\ubb38\uc744 \uacc4\uc18d \uc0dd\uac01\ud558\uba70 \uc78a\uc5b4\ubc84\ub9ac\uc9c0 \ub9d0\uae30 \\n- \ub450\uad04\uc2dd \ud45c\ud604\\n- \uc124\uba85\ud558\ub2e4\uac00 \uc798\ubabb \uc124\uba85\ud55c \uac83 \uac19\uc73c\uba74 \ub2e4 \ub04a\uace0 \ub2e4\uc2dc \uc774\uc57c\uae30\ud574\ub3c4 \ub420\uc9c0 \ubb3c\uc5b4\ubcf4\uae30 \\n- \uc124\uba85\ud560 \uc218 \uc788\uc744\ub9cc\ud07c \uc2dc\uac04 \ucda9\ubd84\ud788 \uac00\uc9c0\uae30\\n- \uc778\ud130\ubdf0\uc5b4\uc758 \uc9c8\ubb38 \uc758\ub3c4\ub97c \uba85\ud655\ud788 \uc774\ud574\ud558\uc9c0 \ubabb\ud588\ub2e4\uba74 \uc758\ub3c4 \ub2e4\uc2dc \ubb3c\uc5b4\ubcf4\uae30\\n- \ub05d\ub9fa\ub294 \ubd80\ubd84 \uc5f0\uc2b5\ud558\uae30(\uc790\uc2e0\uac10 \uc788\uac8c)\\n- \uae30\uc220\uc801\uc778 \uc9d1\ucc29\uac00\uc9c0\uae30\\n- \uae30\uc220\uc801\uc778 \ubd80\ubd84\uc744 \uaf3c\uaf3c\ud788 \uc900\ube44\ud588\uc73c\uba74 \ud611\uc5c5 \uad00\ub828 \uc9c8\ubb38\ub3c4 \uc900\ube44\ud558\uae30\\n\\n### Problem\\n\\n**\ud398\uc5b4\ud504\ub85c\uadf8\ub798\ubc0d** \\n\\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4\ub97c \uc9c4\ud589\ud558\uba74\uc11c \uac00\uc7a5 \uc5b4\ub824\uc6b4 \ud65c\ub3d9 \uc911 \ud558\ub098\ub77c\uace0 \uc0dd\uac01\ud55c\ub2e4. \\n\ud398\uc5b4\ub294 \ub9e4\ubc88 \ubc14\ub00c\uace0, \ubbf8\uc158\uc758 \ubcf5\uc7a1\ub3c4\ub3c4 \uc99d\uac00\ud558\uae30 \ub54c\ubb38\uc778 \uac83 \uac19\ub2e4. \\n\uc18c\ud1b5 \ub2a5\ub825, \uc2dc\uac04\uad00\ub9ac\uac00 \ubd80\uc871\ud588\uace0, \ub9cc\uc871\uc2a4\ub7fd\uc9c0 \uc54a\uc558\ub2e4. \\n\ud558\uc9c0\ub9cc \ud398\uc5b4\ub97c \uc9c4\ud589\ud558\uace0, \ud68c\uace0\ub97c \ud558\ub2e4 \ubcf4\ub2c8 \ub098\ub9cc\uc758 \ub178\ud558\uc6b0\uac00 \uc313\uc774\ub294 \ub290\ub08c\uc774\ub2e4. \\n\ub808\ubca8 2\uc5d0\uc11c\ub294 \ubd80\uc871\ud588\ub358 \ubd80\ubd84\uc744 \uac1c\uc120\ud558\uc5ec \ud568\uaed8\ud558\uace0 \uc2f6\uc740 \ud398\uc5b4\uac00 \ub418\uace0 \uc2f6\ub2e4. \\n\\n**\uc9d1\uc911\ud558\ub294 \uc2dc\uac04\u23f1\ufe0f \ubd80\uc871** \\n\\n\ub808\ubca8 1\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc9d1\uc911\ud558\ub294 \uc2dc\uac04\uc774 \ub9ce\uc774 \ubd80\uc871\ud588\ub2e4. \\n\uc774\ub978 \uc544\uce68\uacfc \uc624\ud6c4\uc5d0 \uac1c\uc778\uc801\uc73c\ub85c \uc9d1\uc911\ud560 \uc218 \uc788\ub294 \uacf5\uac04\uc744 \uc608\uc57d\ud574\uc11c \uc628\uc804\ud788 \ub098\ub9cc\uc758 \uc2dc\uac04\uc744 \uac00\uc838\uc57c\uaca0\ub2e4. \\n\\n### Try\\n\\n**\ud5c8\ube0c\ud83c\udf3f\uc640\uc758 \ud2f0\ud0c0\uc784?** \\n\\n\uc18c\ud504\ud2b8 \uc2a4\ud0ac\uc744 \ub298\ub9b4 \ubc29\ubc95\uc744 \uc0dd\uac01\ud558\ub2e4\uac00 \ub300\ud654\ub97c \ub098\ub204\uc9c0 \ubabb\ud55c \ub2e4\ub978 \ud06c\ub8e8\ub4e4\uacfc \uae5c\uc9dd \ucee4\ud53c\ucc57\uc744 \ud558\uba74 \uc5b4\ub5a8\uae4c \uc0dd\uac01\ud588\ub2e4. \\n\uc608\ub97c \ub4e4\uc5b4 \uc7a1\ub2f4\ubc29\uc5d0 `\uc800\uc640 \ucee4\ud53c\ucc57 \ud558\uc2e4 \ubd84 :)` \ud558\uba74\uc11c \uc62c\ub9b4 \uc218 \uc788\uc744 \uac83 \uac19\ub2e4. \\n\ucc38\uc5ec\ud558\ub294 \uc0ac\ub78c\uc774 \uc788\uc744\uc9c0, \uc548 \uc88b\uac8c \ubcf4\ub294 \uac8c \uc544\ub2d0\uc9c0 \uac71\uc815\ub418\uc9c0\ub9cc \uadf8\ub798\ub3c4 \uc7ac\ubc0c\uc744 \uac83 \uac19\ub2e4. \\n\uc800\ub791 \ud5c8\ube0c\ud2f0 \ud55c\uc794 \ud558\uc2e4\ub798\uc694? \\n\\n**\uae30\uc220\uc801\uc778 \ubd80\ubd84** \\n\\n\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \uc0dd\ud65c\uc744 \ud558\uba74\uc11c \uc18c\ud504\ud2b8 \uc2a4\ud0ac\uc5d0 \uc870\uae08 \ub354 \ubb34\uac8c\ub97c \ub450\ub2e4 \ubcf4\ub2c8 \uc774\ub860\uc801\uc778 \ubd80\ubd84\uc774 \ubd80\uc871\ud560 \uc218 \uc788\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uc2dc\uac04\uc758 \uc5ec\uc720\uac00 \ub420 \ub54c \ucc45\uc744 \uc870\uae08\uc529 \uc77d\uc5b4\uc57c\uaca0\ub2e4. \\n\ube14\ub85c\uadf8\uc5d0 \uae30\uc220\uc801\uc778 \ubd80\ubd84\uc744 \ub9ce\uc774 \uc815\ub9ac\ud558\uc9c0 \uc54a\uc558\ub294\ub370, \uc870\uae08 \ub354 \uae4a\uac8c \uacf5\ubd80\ud558\uace0 \uc815\ub9ac\ud558\ub294 \uc2dc\uac04\ub3c4 \uac00\uc838\uc57c\uaca0\ub2e4. \\n\\n### \ub808\ubca8 1\uc744 \ub9c8\ubb34\ub9ac\ud558\uba70 \\n\\n\uc2dc\uac04\uc774 \ube60\ub974\uac8c \ud758\ub7ec\uac14\ub2e4. \\n\ud0c0\uc778\uc5d0\uac8c \uc88b\uc740 \uc601\ud5a5\uc744 \uc8fc\uae30\uc704\ud574, \ubc29\ud559\ub3d9\uc548 \ub098\ub97c \ucc59\uae30\ub294 \uc2dc\uac04\uc744 \uac00\uc838\uc57c\uaca0\ub2e4. \\n\ub610\ud55c \ud568\uaed8 \uc77c\ud558\uace0 \uc2f6\uc740 \uc0ac\ub78c\uc744 \ubaa9\ud45c\ub85c \uc55e\uc73c\ub85c\ub3c4 \uafb8\uc900\ud788 \uc758\uc2dd\uc801 \ub178\ub825\uc744 \ud574\uc57c\uaca0\ub2e4."},{"id":"chess-retrospective","metadata":{"permalink":"/chess-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-03-31-\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-1/2023-03-31-\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0","description":"\uccb4\uc2a4","date":"2023-03-31T00:00:00.000Z","formattedDate":"2023\ub144 3\uc6d4 31\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":7.63,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0","slug":"chess-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca8 1 \ud68c\uace0","permalink":"/woowacourse-level1-retrospective"},"nextItem":{"title":"\uc77c\ubc18\uc801\uc778 \ucc45\uc784 \ud560\ub2f9\uc744 \uc704\ud55c \ud328\ud134","permalink":"/grasp"}},"content":"### \uccb4\uc2a4\\n\uccb4\uc2a4 \ubbf8\uc158\uc5d0\ub294 \uac00\ube44\uc640 \ud398\uc5b4\uac00 \ub9e4\uce6d\ub418\uc5c8\ub2e4! \\n\uccb4\uc2a4\ub294 \uc774\uc804 \ubbf8\uc158\ub4e4\ubcf4\ub2e4 \ud6e8\uc52c \ubcf5\uc7a1\ud55c \ub3c4\uba54\uc778\uc774\uc5c8\ub2e4. \\n\ud558\uc9c0\ub9cc \uac00\ube44\uc640 \ub098\ub294 \uccb4\uc2a4 \ub3c4\uba54\uc778\uc774 \uc775\uc219\ud574\uc11c \ub354 \ud3b8\ud55c \ub9c8\uc74c\uc73c\ub85c \uc2dc\uc791\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc5b4\ub824\uc6e0\ub358 \ubd80\ubd84\uc740 \uae30\ubb3c\uc758 \uc774\ub3d9, \uc774\ub3d9\uc2dc \uacbd\ub85c\uc5d0 \uae30\ubb3c\uc774 \uc874\uc7ac\ud558\ub294\uc9c0 \ud655\uc778\ud558\ub294 \ubd80\ubd84\uc774\uc5c8\ub2e4. \\n \\n\uac00\ube44\uac00 \uc9d1\uc5d0\uac00\uc11c\ub3c4 \uae30\ubb3c\uc758 \uc774\ub3d9 \uad00\ub828\ud574 \uc0dd\uac01 \uc815\ub9ac\ud55c \uae00\uc744 \ubcf4\ub0b4\uc918\uc11c \ub354\uc6b1 \ube68\ub9ac \uc9c4\ud589\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\ucd5c\uc885\uc801\uc73c\ub85c \uacb0\uc815\ud55c \ubd80\ubd84\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n\\n**\uac01 \uae30\ubb3c\uc758 \uc774\ub3d9 \uac00\ub2a5\uc5ec\ubd80** \\nRank\uc640 File\uc740 \uac01\uac01 \uc704\uce58\uac12\uc744 \uac00\uc9c0\uace0 \uc788\uace0, \uac12\uc758 \ucc28\uc774\ub97c \uc774\uc6a9\ud574\uc11c \uac01 \uae30\ubb3c\uc758 \uc774\ub3d9 \uac00\ub2a5 \uc5ec\ubd80\ub97c \uacc4\uc0b0\ud588\ub2e4. \\n\uc9c1\uc120 \u2192 Rank\uc640 File \ucc28\uc774 \uc911 \ud558\ub098\uac00 0\uc774\uc5b4\uc57c \ud55c\ub2e4. \\n\ub300\uac01\uc120 \u2192 Rank\uc640 File \ucc28\uc774\uc758 \uc808\ub300\uac12\uc774 \uac19\uc544\uc57c \ud55c\ub2e4. ex) abs(-2) == abs(2) \\n\ub098\uc774\ud2b8 \u2192 \ucc28\uc774\uc758 \uc808\ub300\uac12\uc774 \ud558\ub098\ub294 2 \ub098\uba38\uc9c0 \ud558\ub098\ub294 1\uc774\uc5b4\uc57c \ud55c\ub2e4.\\n\\n**\ub3c4\ucc29 \uce78\uc758 \uae30\ubb3c \uc5ec\ubd80** \\n\uc544\uad70 \u2192 \uc774\ub3d9\uc774 \ubd88\uac00\ub2a5\ud558\ub2e4. \\n\uc801\uad70 \u2192 \uc774\ub3d9\uc774 \uac00\ub2a5\ud558\ub2e4. \uc801\uad70\uc744 \uc7a1\ub294\ub2e4. \\n\\n**\uc911\uac04\uc5d0 \uae30\ubb3c \uc874\uc7ac \uc5ec\ubd80** \\n\uc774\ub3d9 \uacbd\ub85c\uc5d0 \uae30\ubb3c\uc774 \uc874\uc7ac\ud558\uba74 \uc548\ub41c\ub2e4. \\n\\n**\ub370\uc774\ud130\ubca0\uc774\uc2a4 \uc0ac\uc6a9** \\n\uccb4\uc2a4 \ubbf8\uc158\uc740 \ud2b9\ubcc4\ud558\uac8c \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc640 \uc5f0\uacb0\ud558\ub294 \ubd80\ubd84\uc774 \uc788\uc5c8\ub2e4. \\n\uccb4\uc2a4 \uac8c\uc784\uc758 \uc0c1\ud0dc\ub97c \ub2e4\uc74c\uc758 \ub450\uac00\uc9c0 \ubc29\ubc95\uc73c\ub85c \uc815\ud560 \uc218 \uc788\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n- \uae30\ubb3c \uc804\uccb4\ub97c \uc800\uc7a5\ud558\ub294 \ubc29\ubc95 \\n- \uae30\ubcf4\ub97c \uc800\uc7a5\ud558\uace0 \uac8c\uc784\uc744 \ubd88\ub7ec\uc640 \uae30\ubcf4\ub300\ub85c \uc774\ub3d9\uc2dc\ud0a4\ub294 \ubc29\ubc95 \\n\\n\uae30\ubb3c\uc774 \uc774\ub3d9\ud560 \ub54c\ub9c8\ub2e4 \uac12\uc744 \uc800\uc7a5\ud558\uace0 \uc2f6\uc5c8\uace0, \uae30\ubcf4\ub97c \uc800\uc7a5\ud558\ub294 \ubc29\ubc95\uc744 \uc120\ud0dd\ud588\ub2e4. \\n\uae30\ubb3c \uc804\uccb4\ub97c \uc800\uc7a5\ud558\uc9c0 \uc54a\uc740 \uc774\uc720\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4. \\n- \ud134\uacfc \uac19\uc740 \ubd80\uac00\uc801\uc778 \uc694\uc18c\ub97c \uc800\uc7a5\ud574\uc57c \ud55c\ub2e4. \\n- \uc774\ub3d9\uc744 \ud560 \ub54c \uae30\ubb3c\uc774 \uc7a1\ud788\ub294 \uacbd\uc6b0 update \ucffc\ub9ac(\uc774\ub3d9 \uae30\ubb3c)\uc640 delete(\uc7a1\ud78c \uae30\ubb3c) 2\uac1c\uc758 \ucffc\ub9ac\ub97c \ub0a0\ub824\uc57c \ud55c\ub2e4. \\n- \ud604\uc7ac \uad6c\uc870\uc5d0\uc11c \ub3c4\uba54\uc778\uc758 \ubcc0\uacbd\uc774 \ud06c\uac8c(\ucd08\uae30 \uc0c1\ud0dc\ub97c \uad6c\uc131\ud558\ub294 \ubd80\ubd84) \uc77c\uc5b4\ub098\uc57c \ud55c\ub2e4. \\n\\n\uc815\ub9ac\ud558\uc790\uba74 \uae30\ubb3c \uc804\uccb4 \uc800\uc7a5\uacfc \uae30\ubcf4 \uc800\uc7a5\uc740 \ub2e4\uc74c\uacfc \ucc28\uc774\uac00 \uc788\ub2e4. \\n\ubcf4\ub4dc\uc800\uc7a5: \ucd08\uae30\uc0c1\ud0dc\uc5d0\uc11c 32\uac1c\uc758 Insert \ucffc\ub9ac(\uae30\ubb3c\uc758 \uc704\uce58) + \uae30\ubb3c \uc774\ub3d9 \uc2dc \uc6c0\uc9c1\uc784 \ubcc0\uacbd(\uc7a1\ud788\ub294 \uacbd\uc6b0 2\uac1c\uc758 \ucffc\ub9ac) \\n\uae30\ubcf4\uc800\uc7a5: \ucd08\uae30\uc0c1\ud0dc \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0\uc11c \uad6c\uc131 + \uc800\uc7a5\ub41c \uae30\ubcf4\ub97c select \ucffc\ub9ac\ub85c \uc870\ud68c\ud574\uc11c \uc0ac\uc6a9(1\ud68c) + insert \ucffc\ub9ac(\uc774\ub3d9 \ub2f9 1\ud68c)\\n\\n\ucd94\uac00\ub85c \uae30\ubcf4\uc800\uc7a5\uc774 \uad6c\ud604\ub3c4 \ub354\uc6b1 \uac04\ub2e8\ud558\ub2e4. \ud83d\udc4d \\n\\n**\ubd80\uac00\uc801\uc778 \ubd80\ubd84**\\n\\n\ub9ac\ubdf0\uc5b4\uc778 \ucc30\ub9ac\ud83c\udf6b\uac00 \ub3d9\uc2dc\uc5d0 \uc5ec\ub7ec \uac8c\uc784\uc774 \uc9c4\ud589\ub41c\ub2e4\uba74 \uc5b4\ub5a8\uc9c0? \uc5d0 \ub300\ud55c \ucf54\uba58\ud2b8\ub97c \ub0a8\uaca8\uc8fc\uc154\uc11c \ub2e4\uc591\ud55c \uc2dc\ub3c4\ub97c \ud574\ubd24\ub2e4. \\n- \ub204\ub204\uc758 \ub3c4\uc6c0\uc73c\ub85c ConnectionPool \uad6c\ud604 \\n- ThreadLocal \uc0ac\uc6a9\ud574\uc11c \uc4f0\ub808\ub4dc \ubcc4 \uc138\uc158 \uad00\ub9ac \\n- \uc2e4\uc81c\ub85c \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ub0b4\uc5d0\uc11c \uccb4\uc2a4 \uac8c\uc784\uc774 \uc9c4\ud589\ub418\ub294 Board\ub97c ConcurrentHashMap\uc73c\ub85c \uc800\uc7a5(\uc0ac\uc2e4 \uc774 \ubd80\ubd84\uc740 \ud604\uc7ac \uad6c\uc870\uc5d0\uc11c \ud544\uc694\uc5c6\uc9c0\ub9cc 2\uba85\uc774 \uc11c\ub85c \uac8c\uc784\ud558\ub294 \uacbd\uc6b0\ub97c \uc0dd\uac01\ud574\uc11c \ub123\uc5b4\ubcf4\uc558\ub2e4.) \\n\\n\ub450 \uba85\uc774 \uc11c\ub85c \uac19\uc740 \ubc29\uc5d0 \uc785\uc7a5\ud558\uc5ec \uac8c\uc784\uc744 \uc9c4\ud589\ud55c\ub2e4\uba74 \ucd9c\ub825\ud558\ub294 \ubd80\ubd84\uc774 \uae4c\ub2e4\ub85c\uc6cc\uc9c8 \uac83 \uac19\ub2e4\uace0 \uc608\uc0c1\ub418\uc5b4(Board\uc5d0 \uc635\uc800\ubc84 \ud328\ud134\uc744 \uc0ac\uc6a9\ud574\uc57c\ub418\ub098?) \ud574\ubcfc \uc5c4\ub450\uac00 \ub098\uc9c0 \uc54a\uc558\ub2e4. \\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n**\uaf3c\uaf3c\ud558\uac8c \ucf54\ub4dc\ub97c \uc791\uc131\ud558\uc9c0 \ubabb\ud55c \ubd80\ubd84** \\nDB \uad00\ub828 \ubd80\ubd84\uc744 \uaf3c\uaf3c\ud558\uac8c \ucf54\ub529\uc744 \ud558\uc9c0 \ubabb\ud588\ub2e4. \\n\ub3c4\uba54\uc778 \ub85c\uc9c1\uc5d0\ub9cc \uc9d1\uc911\ud558\ub2e4\ubcf4\ub2c8 \uc815\uc801 \uc911\uc694\ud55c DB\uc758 \ucf54\ub4dc\uc758 \uc608\uc678\ucc98\ub9ac, \ube48 \uac12\uc744 \ubc18\ud658 \ud558\ub294 \ubd80\ubd84\uc744 \uaf3c\uaf3c\ud558\uac8c \ucc98\ub9ac\ud558\uc9c0 \ubabb\ud588\ub2e4. \\n\ud558\uc9c0\ub9cc \ucc30\ub9ac\uc758 \uaf3c\uaf3c\ud55c \ub9ac\ubdf0\ub85c DB\ubd80\ubd84\uacfc \ub098\ub9cc\uc758 JdbcTemplate\uc744 \uae54\ub054\ud558\uac8c \uad6c\ud604\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n**\uc2dc\uac04\uc5d0 \ub300\ud55c \ubd80\ub2f4\uac10** \\n\ucd08\ubc18\uc5d0\ub294 \uc5ec\uc720\ub86d\uc9c0\ub9cc \uc81c\ucd9c \ub9c8\uac10\uc5d0 \uac00\uae4c\uc6cc\uc9c8 \uc218\ub85d \uc0ac\ub78c\uc774 \uae09\ud574\uc9c0\ub294 \uac83 \uac19\ub2e4. \\n\ub2e4\uc74c \ud398\uc5b4\ud504\ub85c\uadf8\ub798\ubc0d\ud560 \ub550 \uc18d\ub3c4\ub97c \uc870\uc808\ud558\uace0, \ub9c8\uc74c\uc5d0 \uc5ec\uc720\ub97c \uac00\uc838\uc57c\uaca0\ub2e4. \\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**DAO \uc911\ubcf5 \uc81c\uac70**\\n\\n\ud504\ub864\ub85c\uadf8\uc5d0 [\uae00](https://prolog.techcourse.co.kr/studylogs/2947)\uc744 \uc791\uc131\ud588\ub2e4. \\nDAO\ub97c \uc791\uc131\ud558\ub294\ub370 try-catch-resources\uc640 \uc5ec\ub7ec \ucf54\ub4dc\uac00 \uc911\ubcf5\ub418\uc11c \uc81c\uac70\ud558\uace0\uc2f6\uc5c8\ub2e4. \\n\ud15c\ud50c\ub9bf \ucf5c\ubc31 \ud328\ud134\uc73c\ub85c \uae54\ub054\ud558\uac8c \uc911\ubcf5\uc744 \uc81c\uac70\ud560 \uc218 \uc788\uc5c8\ub2e4.\ud83d\udc4d\\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n**\ud398\uc5b4 \uc0dd\uac01\ud558\uae30** \\n\uac00\ube44\ub294 \ub204\uad6c\ubcf4\ub2e4 \ud398\uc5b4\ub97c \uc0dd\uac01\ud558\uace0, \ubc30\ub824\ud574\uc8fc\ub294 \ud398\uc5b4\uc600\ub2e4. \\n\uc911\uac04 \uc911\uac04 \ub2f9 \ub5a8\uc5b4\uc9c8\uae4c\ubd10 \uac71\uc815\ub3c4 \ud574\uc8fc\uace0, \ub098\uc758 \ucee8\ub514\uc158\ub3c4 \ud655\uc778\ud574\uc92c\ub2e4! \\n\\n**\ubbf8\uc158 \ubab0\uc785\ud558\uae30** \\n\ucd5c\uadfc\uc5d0 \ubbf8\uc158\uc5d0 \uc798 \ubab0\uc785\ud558\uc9c0 \ubabb\ud588\ub2e4. \\n\uac00\ube44\ub294 \ud398\uc5b4\ub97c \uc9c4\ud589\ud560 \ub54c \ubbf8\uc158\uc5d0 \ub300\ud55c \ubab0\uc785\ub3c4\uac00 \ub9e4\uc6b0 \uc88b\uc558\ub2e4. \\n\uc9d1\uc5d0\uac00\uc11c\ub3c4 \uccb4\uc2a4 \uc774\ub3d9\uc5d0 \ub300\ud55c \ub85c\uc9c1\uc744 \uc5b4\ub5bb\uac8c \uad6c\ud604\ud560 \uc9c0 \uc0dd\uac01\ud55c \ub4a4 \uaf3c\uaf3c\ud574\uc11c \uc815\ub9ac\ud574\uc11c \ub098\uc5d0\uac8c \ubcf4\ub0b4\uc8fc\uc5c8\ub2e4. \\n\ub355\ubd84\uc5d0 \ub098\ub3c4 \uac00\ube44\uc758 \uc0dd\uac01\uc744 \uc54c \uc218 \uc788\uc5b4\uc11c \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\ub294\ub370 \uac00\uc18d\ub3c4\uac00 \ubd99\uc740 \uac83 \uac19\ub2e4. \\n\ub610\ud55c \ubbf8\uc158\uc744 \uc798 \ub9c8\ubb34\ub9ac\ud558\uace0 \uc2f6\uc740 \ub9c8\uc74c\uc774 \uc804\ub2ec\ub418\uc11c \uadf8\ub7f0\uc9c0 \ub098\ub3c4 \ub369\ub2ec\uc544 \uc5f4\uc2ec\ud788 \ubbf8\uc158\uc744 \ud560 \uc218 \uc788\uc5c8\ub2e4.\ud83d\ude04 \\n\\n**\uc194\uc9c1\ud568** \\n\uba3c\uc800 \ud68c\uace0\ud558\uc790\uace0 \ub9d0 \uac78\uc5b4\uc918\uc11c \uc815\ub9d0 \uace0\ub9c8\uc6e0\ub2e4\uace0 \ud45c\ud604\ud574\uc8fc\ub294 \ubd80\ubd84 \\n\ubaa8\ub974\ub294\uac8c \uc788\uc73c\uba74 \uc194\uc9c1\ud558\uac8c \ub9d0\ud574\uc8fc\ub294 \ubd80\ubd84 \\n\ub098\uc758 \uc758\uacac\uc744 \uc815\ub9ac\ud558\uc9c0 \ubabb\ud55c \uc0c1\ud0dc\ub85c \uc804\ub2ec\ud560 \ub54c \uc774\ud574\uac00 \uc548\ub418\uc5c8\ub2e4\uace0 \uc815\ud655\ud788 \uc804\ub2ec\ud574\uc8fc\ub294 \ubd80\ubd84 \\n\uc194\uc9c1\ud568\uc740 \ud398\uc5b4\ud560 \ub54c \uc911\uc694\ud55c \ubd80\ubd84\uc778 \uac83 \uac19\ub2e4. \\n\\n\ub9c8\uc9c0\ub9c9\uc73c\ub85c \ucc30\ub9ac\ud83c\udf6b \uccb4\uc2a4 \ubbf8\uc158\ub54c \uaf3c\uaf3c\ud558\uac8c \ub9ac\ubdf0 \ub0a8\uaca8\uc8fc\uc154\uc11c \uac10\uc0ac\ud569\ub2c8\ub2e4!"},{"id":"grasp","metadata":{"permalink":"/grasp","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-03-30-GRASP.mdx","source":"@site/blog/2023-1/2023-03-30-GRASP.mdx","title":"\uc77c\ubc18\uc801\uc778 \ucc45\uc784 \ud560\ub2f9\uc744 \uc704\ud55c \ud328\ud134","description":"GRASP(General Responsibility Assignment Software Pattern)","date":"2023-03-30T00:00:00.000Z","formattedDate":"2023\ub144 3\uc6d4 30\uc77c","tags":[{"label":"GRASP","permalink":"/tags/grasp"},{"label":"OOP","permalink":"/tags/oop"}],"readingTime":8.085,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc77c\ubc18\uc801\uc778 \ucc45\uc784 \ud560\ub2f9\uc744 \uc704\ud55c \ud328\ud134","slug":"grasp","tags":["GRASP","OOP"]},"prevItem":{"title":"\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0","permalink":"/chess-retrospective"},"nextItem":{"title":"\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0","permalink":"/blackjack-retrospective"}},"content":"### GRASP(General Responsibility Assignment Software Pattern)\\n\\n\ud06c\ub808\uc774\uadf8 \ub77c\ub9cc\uc758 Applying UML and Patterns\uc774\ub77c\ub294 \ucc45\uc5d0\uc11c \ub098\uc628 \ucc45\uc784 \ud560\ub2f9\uc744 \uc704\ud55c \ud328\ud134\\n\\n\uac01 \ud328\ud134\ub9c8\ub2e4 Solution\uacfc Problem\ub85c \uad6c\uc131\ub418\uc5b4 \uc788\ub2e4.\\n\\n### \uc815\ubcf4 \uc804\ubb38\uac00 \ud328\ud134(Information Expert)\\n\\nQ: \uac1d\uccb4\uc5d0 \ucc45\uc784\uc744 \ud560\ub2f9\ud558\ub294 \uae30\ubcf8 \uc6d0\uce59\uc740 \ubb34\uc5c7\uc778\uac00?\\n\\nA: \ucc45\uc784\uc744 \uc218\ud589\ud558\ub294 \ub370 \ud544\uc694\ud55c \uc815\ubcf4\ub97c \uac00\uc9c4 \ud074\ub798\uc2a4(\uc815\ubcf4 \uc804\ubb38\uac00)\uc5d0\uac8c \ucc45\uc784\uc744 \ud560\ub2f9\ud55c\ub2e4.\\n\\n\uc815\ubcf4\uc640 \ud589\ub3d9\uc744 \uac00\uae4c\uc6b4 \uacf3\uc5d0 \uc704\uce58\uc2dc\ud0a4\uae30 \ub54c\ubb38\uc5d0 \ucea1\uc290\ud654\ub97c \uc720\uc9c0\ud560 \uc218 \uc788\ub2e4.\\n\\n\ud544\uc694\ud55c \uc815\ubcf4\ub97c \uac00\uc9c4 \uac1d\uccb4\ub4e4\ub85c \ucc45\uc784\uc774 \ubd84\uc0b0\ub41c\ub2e4.\\n\\n### \ucc3d\uc870\uc790 \ud328\ud134(Creator)\\n\\nQ: \ub204\uac00 \uac1d\uccb4 A\ub97c \uc0dd\uc131\ud558\ub294\uac00?\\n\\nA: \ub2e4\uc74c\uc758 \uc870\uac74\uc744 \ucd5c\ub300\ud55c \ub9ce\uc774 \ub9cc\uc871\ud558\ub294 \uac1d\uccb4\uc5d0\uac8c \uac1d\uccb4 \uc0dd\uc131 \ucc45\uc784\uc744 \ud560\ub2f9\ud574\uc57c \ud55c\ub2e4.\\n\\n- B\uac00 A \uac1d\uccb4\ub97c \ud3ec\ud568 \ub610\ub294 \ucc38\uc870\ud55c\ub2e4.\\n- B\uac00 A \uac1d\uccb4\ub97c \uae30\ub85d\ud55c\ub2e4.\\n- B\uac00 A \uac1d\uccb4\ub97c \uae34\ubc00\ud558\uac8c \uc0ac\uc6a9\ud55c\ub2e4.\\n- B\uac00 A \uac1d\uccb4\uc758 \ucd08\uae30\uac12\uc744 \uac00\uc9c0\uace0 \uc788\ub2e4.\\n\\n\uc0dd\uc131 \uc608\uc815\uc778 \uac1d\uccb4\uc640 \uc5f0\uad00\ub418\uc5b4 \uc788\ub294 \uac1d\uccb4\uac00 \uc0dd\uc131 \ucc45\uc784\uc744 \uac00\uc9c0\uace0 \uc788\uac8c \ub41c\ub2e4\uba74, \uc774\ubbf8 \ud574\ub2f9 \uac1d\uccb4\uc640 \uacb0\ud569\ub418\uc5b4\uc788\ub2e4\uace0 \uc0dd\uac01\ud560 \uc218 \uc788\ub2e4. \ub530\ub77c\uc11c \uc804\uccb4\uc801\uc778 \uacb0\ud569\ub3c4\ub97c \ub0ae\uac8c \uc720\uc9c0\ud560 \uc218 \uc788\ub2e4.\\n\\n### \ub0ae\uc740 \uacb0\ud569\ub3c4 \ud328\ud134(Low Coupling)\\n\\nQ: \uc758\uc874\uc131\uc744 \ub0ae\ucd94\uace0 \ubcc0\ud654\uc758 \uc601\ud5a5\uc744 \uc904\uc774\uba70 \uc7ac\uc0ac\uc6a9\uc131\uc744 \uc99d\uac00\uc2dc\ud0a4\ub294 \ubc29\ubc95\uc740?\\n\\nA: \uc804\uccb4\uc801\uc778 \uacb0\ud569\uc774 \ub0ae\uac8c \uc720\uc9c0\ub418\ub3c4\ub85d \ucc45\uc784\uc744 \ud560\ub2f9\ud574\uc57c \ud55c\ub2e4.\\n\\n> \uacb0\ud569\ub3c4(Coupling)\\n\uac1d\uccb4 \uc0ac\uc774\uc758 \uc758\uc874\uc131\uc774 \uacfc\ud55c \uacbd\uc6b0 \uacb0\ud569\ub3c4\uac00 \ub192\ub2e4\uace0 \ub9d0\ud55c\ub2e4.\\n- \uc624\ube0c\uc81d\ud2b8 p.17\\n> \\n\\n\uacb0\ud569\ub3c4\ub97c \ub0ae\ucd98\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uc774\uc810\uc774 \uc788\ub2e4.\\n\\n- \ub2e4\ub978 \uad6c\uc131 \uc694\uc18c\uc758 \ubcc0\ud654\uc5d0 \uc601\ud5a5\uc744 \ubc1b\uc9c0 \uc54a\ub294\ub2e4.\\n- \uc7ac\uc0ac\uc6a9\uc774 \ud3b8\ub9ac\ud574\uc9c4\ub2e4.\\n- \ud574\ub2f9 \ud074\ub798\uc2a4\uc5d0 \ub300\ud55c \uc774\ud574\uac00 \uc26c\uc6cc\uc9c4\ub2e4. (\uc758\uc874\ud558\ub294 \ud074\ub798\uc2a4\uac00 \uc801\uae30 \ub54c\ubb38\uc5d0)\\n\\n### \ub192\uc740 \uc751\uc9d1\ub3c4 \ud328\ud134(High Cohesion)\\n\\nQ. \uac1d\uccb4\ub97c \uad00\ub9ac\ud558\uae30 \uc27d\uac8c \ud558\ub824\uba74 \uc5b4\ub5bb\uac8c \ud574\uc57c \ud560\uae4c?\\n\\nA. \ub192\uc740 \uc751\uc9d1\ub3c4\ub97c \uc720\uc9c0\ud560 \uc218 \uc788\uac8c \ucc45\uc784\uc744 \ud560\ub2f9\ud574\uc57c \ud55c\ub2e4.\\n\\n> \uc751\uc9d1\ub3c4(Cohesion)\\n\uc5f0\uad00\ub41c \uc791\uc5c5\ub9cc\uc744 \uc218\ud589\ud558\uace0 \uc5f0\uad00\uc131 \uc5c6\ub294 \uc791\uc5c5\uc740 \ub2e4\ub978 \uac1d\uccb4\uc5d0\uac8c \uc704\uc784\ud558\ub294 \uac1d\uccb4\ub97c \uac00\ub9ac\ucf1c \uc751\uc9d1\ub3c4\uac00 \ub192\ub2e4\uace0 \ub9d0\ud55c\ub2e4.\\n- \uc624\ube0c\uc81d\ud2b8 p.26\\n> \\n\\n\ubcc0\uacbd\uc758 \uc774\uc720\uc5d0 \ub530\ub77c \ud074\ub798\uc2a4\ub97c \ubd84\ub9ac\ud55c\ub2e4\uba74 \uc751\uc9d1\ub3c4\ub97c \ub192\uc77c \uc218 \uc788\uace0, \uc751\uc9d1\ub3c4\uac00 \ub192\uc544\uc9c4\ub2e4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uc774\uc810\uc774 \uc788\ub2e4.\\n\\n- \ud574\ub2f9 \ud074\ub798\uc2a4\uc5d0 \ub300\ud55c \uc774\ud574\uac00 \uc26c\uc6cc\uc9c4\ub2e4. (\ud560\ub2f9\ub41c \ucc45\uc784\ub9cc\uc744 \uc218\ud589\ud558\uace0 \uc788\uae30 \ub54c\ubb38\uc5d0)\\n- \uc720\uc9c0\ubcf4\uc218\uac00 \uc26c\uc6cc\uc9c4\ub2e4.\\n- \ub0ae\uc740 \uacb0\ud569\ub3c4 \ub610\ud55c \uc9c0\uc6d0\ud55c\ub2e4.\\n- \uc751\uc9d1\ub3c4\uac00 \ub192\uc740 \ud074\ub798\uc2a4\ub294 \ud2b9\uc815\ud55c \ubaa9\uc801\uc5d0 \uc0ac\uc6a9\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \uc7ac\uc0ac\uc6a9\ud558\uae30 \uc88b\ub2e4.\\n\\n### \ucee8\ud2b8\ub864\ub7ec \ud328\ud134(Controller)\\n\\nQ. \uc0ac\uc6a9\uc790\uc758 \uc694\uccad\uc744 \ucc98\ub9ac\ud558\ub294 \uac83\uc740 \ub204\uac00 \ub2f4\ub2f9\ud574\uc57c \ud558\ub294\uac00?\\n\\nA. \uc0ac\uc6a9\uc790\uc758 \uc694\uccad\uc744 \ucc98\ub9ac\ud558\ub294 Controller \uac1d\uccb4\ub97c \ub9cc\ub4e4\uc5b4\uc11c \uc0ac\uc6a9\ud574\uc57c \ud55c\ub2e4.\\n\\n\uc5b4\ub5a4 \uc11c\ube0c\uc2dc\uc2a4\ud15c\uc774 \uc874\uc7ac\ud55c\ub2e4\uace0 \uac00\uc815\ud560 \ub54c\\n\\n- \uc9c1\uc811\uc801\uc73c\ub85c \uac1d\uccb4\uc5d0 \uc811\uadfc\ud558\uc5ec \ud504\ub85c\uadf8\ub7a8\uc744 \uc0ac\uc6a9\ud55c\ub2e4\uba74 \uacb0\ud569\ub3c4\uac00 \uc0c1\uc2b9\ud55c\ub2e4.\\n- \uc11c\ube0c \uc2dc\uc2a4\ud15c\uc5d0 \ub4e4\uc5b4\uc624\ub294 \uc694\uccad\uc744 \ucc98\ub9ac\ud574\uc8fc\ub294 \ucee8\ud2b8\ub864\ub7ec\uac00 \uc788\ub2e4\uba74 \uc0ac\uc6a9\ud558\ub294 \uc785\uc7a5\uc5d0\uc11c\ub294 \ud574\ub2f9 \ucee8\ud2b8\ub864\ub7ec\ub9cc \uc54c\uba74 \ub41c\ub2e4.\\n- \ub9cc\uc57d \uc11c\ube0c \uc2dc\uc2a4\ud15c\uc758 \ubcc0\uacbd\uc774 \uc0dd\uacbc\uc744 \ub54c \uc678\ubd80\uc5d0 \ubbf8\uce58\ub294 \uc601\ud5a5\ub3c4 \uc904\uc5b4\ub4e0\ub2e4.\\n\\n### \ub2e4\ud615\uc131 \ud328\ud134(Polymorphism)\\n\\nQ. \uac1d\uccb4\uc758 \ud0c0\uc785\uc5d0 \ub530\ub77c \ud589\ub3d9\uc774 \ubc14\ub010\ub2e4\uba74 \ucc45\uc784\uc744 \uc5b4\ub5bb\uac8c \ud560\ub2f9\ud574\uc57c \ud560\uae4c?\\n\\nA. OOP\uac00 \uc9c0\uc6d0\ud558\ub294 \ub2e4\ud615\uc131\uc744 \uc801\uadf9\uc801\uc73c\ub85c \ud65c\uc6a9\ud55c\ub2e4. (\uc778\ud130\ud398\uc774\uc2a4\ub97c \ub450\uace0 \ud589\ub3d9\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \uad6c\ud604)\\n\\n\uac1d\uccb4\uc758 \uc885\ub958\uc5d0 \ub530\ub77c \ubd84\uae30\ud558\ub294 \uc870\uac74\ubb38\uc774 \uc544\ub2cc \ub2e4\ud615\uc131\uc744 \uc0ac\uc6a9\ud558\ub294 \uac83\uc774 \uc88b\uc740 \ubc29\ubc95\uc774\ub2e4.\\n\\n\uc0c8\ub85c\uc6b4 \ud0c0\uc785\uc774 \ucd94\uac00\ub418\uc5c8\uc744 \ub54c \uc870\uac74\ubb38\uc744 \uc0ac\uc6a9\ud55c\ub2e4\uba74 \uae30\uc874\uc758 \uc870\uac74\ubb38\uc744 \uc218\uc815\ud574\uc57c \ud558\uc9c0\ub9cc \ub2e4\ud615\uc131\uc744 \ud65c\uc6a9\ud558\uba74 \uc27d\uac8c \ud655\uc7a5\ud560 \uc218 \uc788\ub2e4.\\n\\n### \ubcc0\uacbd \ubcf4\ud638 \ud328\ud134(Protected Variations)\\n\\nQ. \uc5b4\ub5bb\uac8c \ud558\uba74 \ubcc0\uacbd\uc774 \ub2e4\ub978 \uc694\uc18c\uc5d0 \uc601\ud5a5\uc744 \ubbf8\uce58\uc9c0 \uc54a\ub3c4\ub85d \ubc29\uc9c0\ud560 \uc218 \uc788\uc744\uae4c?\\n\\nA. \ubcc0\ud654\uac00 \uc608\uc0c1\ub418\ub294 \uc9c0\uc810\uc744 \uc2dd\ubcc4\ud558\uace0, \uc8fc\uc704\uc5d0 \uc548\uc815\ub41c \uc778\ud130\ud398\uc774\uc2a4\ub97c \ud615\uc131\ud558\ub3c4\ub85d \ucc45\uc784\uc744 \ud560\ub2f9\ud574\uc57c \ud55c\ub2e4.\\n\\n### \uac04\uc811 \ucc38\uc870 \ud328\ud134(Indirection)\\n\\nQ. \ub450 \uac1d\uccb4 \uc0ac\uc774\uc758 \uc9c1\uc811\uc801\uc778 \uc5f0\uacb0\uc744 \ud53c\ud558\uace0 \uc2f6\ub2e4\uba74 \uc5b4\ub5bb\uac8c \ud574\uc57c \ud560\uae4c?\\n\\nA. \ub450 \uac1d\uccb4 \uc0ac\uc774\uc5d0 \ub610 \ub2e4\ub978 \uac1d\uccb4\ub97c \ub450\uc5b4 \uc9c1\uc811\uc801\uc778 \uc5f0\uacb0\uc744 \ud53c\ud560 \uc218 \uc788\ub2e4.\\n\\n\uc911\uc7ac\uc790 \ud328\ud134\uc744 \uc0ac\uc6a9\ud558\uc5ec \ub450 \uac1d\uccb4 \uc0ac\uc774\uc5d0 \ub610 \ud558\ub098\uc758 \uac1d\uccb4\ub97c \ucd94\uac00\ud558\uc5ec \ubcf5\uc7a1\ud55c \uad00\uacc4\ub97c \ub2e8\uc21c\ud654\ud560 \uc218 \uc788\ub2e4.\\n\\n\uc911\uac04\uc5d0 \uc778\ud130\ud398\uc774\uc2a4\ub97c \ub454\ub2e4\uba74 \ubcc0\uacbd \ubcf4\ud638 \ud328\ud134(Protected Variations)\uc5d0 \ud574\ub2f9\ub41c\ub2e4.\\n\\n### \uc21c\uc218\ud55c \uac00\uacf5\ubb3c \ud328\ud134(Pure Fabrication)\\n\\nQ. \ucc45\uc784\uc744 \ud560\ub2f9\ud55c \ub3c4\uba54\uc778 \uac1d\uccb4\uac00 Low Coupling, High Cohesion, \uc7ac\uc0ac\uc6a9\uc131 \ub4f1\uc758 \ubaa9\uc801\uc744 \uc704\ubc18\ud55c\ub2e4\uba74 \uc5b4\ub5bb\uac8c \ud574\uc57c \ud560\uae4c?\\n\\nA. \ub3c4\uba54\uc778 \uac1c\ub150\uc744 \ud3ec\ud568\ud558\uc9c0 \uc54a\ub294 \ud074\ub798\uc2a4\ub97c \ud558\ub098 \ub9cc\ub4e4\uace0 \ub9e4\uc6b0 \uc751\uc9d1\ub41c \ucc45\uc784\uc744 \ud560\ub2f9\ud560 \uc218 \uc788\ub2e4.\\n\\n\ud589\ub3d9\uc744 \ucd94\uac00\ud560 \ub54c, \ud574\ub2f9 \ucc45\uc784\uc744 \uc218\ud589\ud560 \ub3c4\uba54\uc778 \uac1c\ub150\uc774 \uc874\uc7ac\ud558\uc9c0 \uc54a\ub294\ub2e4\uba74 \ub3c4\uba54\uc778\uacfc \ubb34\uad00\ud55c \uc778\uacf5\uc801\uc778 \uac1d\uccb4\ub97c \ub9cc\ub4e0\ub2e4\uc74c \ud574\ub2f9 \uac1d\uccb4\uc5d0\uac8c \ucc45\uc784\uc744 \ud560\ub2f9\ud55c\ub2e4.\\n\\n\uac1d\uccb4\uac00 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud574\uc57c \ud560 \uac12\uc744 \uac00\uc9c0\uace0 \uc788\ub2e4\uace0, \uc815\ubcf4 \uc804\ubb38\uac00 \ud328\ud134\uc744 \uc801\uc6a9\ud558\uc5ec \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud558\ub77c\ub294 \ucc45\uc784\uc744 \uac00\uc9c0\ub77c\uace0 \ud558\uc9c0 \uc54a\ub294\ub2e4.\\n\\n\uc608) \uc0c1\uc810\uacfc \uace0\uac1d \ud074\ub798\uc2a4\uac00 \uc788\uace0 \uc11c\ub85c \ub2e4\ub978 \ud1b5\ud654\ub97c \uc0ac\uc6a9\ud558\uace0 \uc788\ub2e4\uace0 \uac00\uc815\\n\\n- \uc11c\ub85c \ub2e4\ub978 \ud1b5\ud654\ub97c \uc0ac\uc6a9\ud558\uace0 \uc788\uae30 \ub54c\ubb38\uc5d0 \uac70\ub798\ub97c \ud558\ub824\uba74 \ud658\uc804\uc744 \ud574\uc57c\ud55c\ub2e4.\\n- \ub450 \ud074\ub798\uc2a4 \ub2e4 \ud658\uc804\uc5d0 \ub300\ud55c \ucc45\uc784\uc744 \ubd80\uc5ec\ud558\uae30 \uc560\ub9e4\ud558\ub2e4\uba74 \ud658\uc804\uc744 \ucc45\uc784\ud558\ub294 \ud074\ub798\uc2a4\ub97c \ucd94\uac00\ud558\uace0 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n\uc624\ube0c\uc81d\ud2b8 5\uc7a5. \ucc45\uc784 \ud560\ub2f9\ud558\uae30, [\uc870\uc601\ud638](http://aeternum.egloos.com/)\\n\\nApplying UML and Patterns Chapter 16, Chapter 21 GRASP, Craig Larman\\n\\n[GRASP, \ud55c\ube5b \ub124\ud2b8\uc6cc\ud06c](https://www.hanbit.co.kr/network/category/category_view.html?cms_code=CMS8586826397)"},{"id":"blackjack-retrospective","metadata":{"permalink":"/blackjack-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-03-14-\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-1/2023-03-14-\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0.mdx","title":"\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0","description":"\ube14\ub799\uc7ad","date":"2023-03-14T00:00:00.000Z","formattedDate":"2023\ub144 3\uc6d4 14\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":5.105,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0","slug":"blackjack-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc77c\ubc18\uc801\uc778 \ucc45\uc784 \ud560\ub2f9\uc744 \uc704\ud55c \ud328\ud134","permalink":"/grasp"},"nextItem":{"title":"\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0","permalink":"/ladder-retrospective"}},"content":"### \ube14\ub799\uc7ad\\n\\n\ube14\ub799\uc7ad \ubbf8\uc158\uc5d0\uc11c\ub294 \ud6c4\ucd94\uc640 \ud398\uc5b4(\uc870\ubbf8\ub8cc \ub4c0\uc624?)\uac00 \ub9e4\uce6d\ub418\uc5c8\ub2e4. \\n\uc774\ubc88\uc5d0\ub294 \uc2e4\uc218\ud558\uc9c0 \uc54a\uace0, \ubc14\ub85c \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uc9c0 \uc54a\uace0 \uce5c\ud574\uc9c0\uae30 \ubd80\ud130 \uc2dc\uc791\ud588\ub2e4. \\n\\n\ube14\ub799\uc7ad\uc740 \uad6c\ud604\ud574\uc57c \ub420 \ub0b4\uc6a9\uc774 \ub9ce\uc544 \uc2dc\uac04\uc774 \ub9ce\uc774 \ubd80\uc871\ud560 \uac83 \uac19\uc558\uc9c0\ub9cc \\n\ud6c4\ucd94\uc640 \ud568\uaed8 \uc804\ub7b5\uc801(\uc0bc\uc77c\uc808\uc5d0 \ubbf8\uc158 \uc774\uc57c\uae30 \ub098\ub204\uae30)\uc73c\ub85c \ubbf8\uc158\uc744 \uc9c4\ud589\ud574 \uc2dc\uac04 \ub0b4\uc5d0 \uc81c\ucd9c\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n\ubbf8\uc158\uc744 \ub05d\ub098\uace0 \ud68c\uace0\ub97c \ud588\uc744 \ub54c \ud6c4\ucd94\uac00 \uace0\ubbfc\uac70\ub9ac\ub97c \ud558\ub098 \ub0b4\uc92c\ub2e4. \\n\\"\ud398\uc5b4\ub97c \uc9c4\ud589\ud560 \ub54c \uc555\ubc15\uac10\uc744 \ub290\ub07c\ub294 \ud398\uc5b4\uac00 \uc788\ub2e4\uba74 \ud5c8\ube0c\uac00 \ud574\uc904 \uc218 \uc788\ub294\uac8c \ubb50\uac00 \uc788\uc744\uae4c?\\" \\n\\n\uacf0\uacf0\ud788 \uc0dd\uac01\ud574\ubd24\uc9c0\ub9cc \uc27d\uac8c \ub2f5\uc744 \ub0b4\ub9b4 \uc218 \uc5c6\uc5c8\ub2e4. \\n\uc911\uac04 \uc911\uac04 \ud68c\uace0\ub97c \ud558\uace0, \ub098\uc758 \uc18c\ud504\ud2b8\uc2a4\ud0ac\uc744 \ub192\ud788\ub294\uac8c \ub2f5\uc77c\uae4c? \\n\ubd80\ub2f4\uac10\uc744 \ub290\ub07c\uc9c0 \uc54a\uace0 \uac19\uc774 \uc77c\ud558\uace0 \uc2f6\uc740 \uc0ac\ub78c\uc774 \ub420 \uc218 \uc788\ub3c4\ub85d \uacc4\uc18d \uc0dd\uac01\ud574\ubd10\uc57c\uaca0\ub2e4. \\n\\n\uc774 \ubd80\ubd84\uc5d0 \ub300\ud574 \uc0dd\uac01\uc774 \ub9ce\uc544\uc838\uc11c \uc804 \ub9ac\ubdf0\uc5b4\uc778 \ud130\ud2c0\ud83d\udc22\uacfc\ub3c4 \ub300\ud654\ub97c \ub098\ub204\uc5c8\ub2e4. \\n\ud130\ud2c0\uc740 \uc81c\uc5b4\ud560 \uc218 \uc5c6\ub294 \ubd80\ubd84\ubcf4\ub2e4 \uc81c\uc5b4\ud560 \uc218 \uc788\ub294 \ubd80\ubd84(\uad81\uadf9\uc801\uc778 \ubaa9\ud45c\uc778 \uc88b\uc740 \ucf54\ub4dc\ub97c \uc791\uc131\ud558\ub294 \uac83)\uc5d0 \uc9d1\uc911\ud574\ubcf4\ub77c\uace0 \ud558\uc168\ub2e4. \\n\\n\uc88b\uc740 \ucf54\ub4dc, \uc88b\uc740 \ud398\uc5b4\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \uc77c\ub2e8 \uc9c0\uc18d\uc801\uc73c\ub85c \uc0dd\uac01\ud574\ubd10\uc57c\uaca0\ub2e4.\\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n**\ud398\uc5b4 \uc2e0\uacbd\uc4f0\uae30** \\n\uc774\ubc88 \ud398\uc5b4\ud560 \ub54c \uc801\uadf9\uc801\uc73c\ub85c \uc758\uacac\uc744 \ub0b4\ubcf4\ub3c4\ub85d \ud588\ub2e4. \uadf8\ub807\uae30\uc5d0 \ub108\ubb34 \uc758\uacac\uc744 \uac15\ud558\uac8c \ubc00\uc5b4\ubd99\uc778 \ub290\ub08c\uc774 \ub4e4\uc5b4\uc11c \ubbf8\uc548\ud588\ub2e4. \\n\ud6c4\ucd94\uac00 \uc555\ubc15\uc744 \ub290\uaf08\uc744 \uc218\ub3c4 \uc788\uc744 \uac83 \uac19\ub2e4\ub294 \uc0dd\uac01\uc774 \ub4e0\ub2e4. \\n\uc911\uac04 \uc911\uac04 \uc791\uc740 \ud68c\uace0\ub97c \uc9c4\ud589\ud574\ubcf4\ub294 \uac83\uc774 \uc88b\uc744\uae4c?\\n\\n**\uccb4\ub825 \uad00\ub9ac** \\n\uc694\uc998 \uc798 \ubabb\uba39\ub294 \uac83 \uac19\ub2e4. \\n\uc55e\uc73c\ub85c \uc0b4 \ub0a0\uc774 \ub9ce\uc740\ub370 \uc798 \ucc59\uaca8\uba39\uace0, \ud798\ub0b4\uc57c\uaca0\ub2e4.\\n\\n**\uc911\uac04 \uc911\uac04 \ub3cc\uc544\ubcf4\uae30** \\n\uc774\ubc88 \ubbf8\uc158\uacfc \uad00\ub828\ub41c \ub0b4\uc6a9\uc740 \uc544\ub2c8\uc9c0\ub9cc \uc6b0\ud14c\ucf54\ub97c \uc798 \ud65c\uc6a9 \ud558\uace0 \uc788\ub294\uc9c0 \uc0dd\uac01\uc744 \ud574\ubd10\uc57c\uaca0\ub2e4. \\n\ub0b4\uac00 \uc6b0\ud14c\ucf54\uc5d0 \uc9c0\uc6d0\ud55c \uc774\uc720\ub97c \ud56d\uc0c1 \uc78a\uc9c0 \uc54a\uc544\uc57c\uaca0\ub2e4. \\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**\uc0c1\ud0dc \ud328\ud134** \\n\uac1d\uccb4\uc758 \ub0b4\ubd80 \uc0c1\ud0dc\uc5d0 \ub530\ub77c \uc2a4\uc2a4\ub85c \ud589\ub3d9\uc744 \ubcc0\uacbd\ud558\ub3c4\ub85d \ud558\ub294 \ud328\ud134\uc73c\ub85c if/else/switch\uc640 \uac19\uc740 \uc870\uac74\ubb38\uc744 \ud6a8\uacfc\uc801\uc73c\ub85c \uc81c\uac70\ud560 \uc218 \uc788\ub2e4. \\n\ube14\ub799\uc7ad \ubbf8\uc158\uc744 \uc9c4\ud589\ud558\uba74\uc11c \uc0c1\ud0dc \ud328\ud134\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \ucc98\uc74c \uc801\uc6a9\ud574\ubcf4\uc558\ub2e4. \\n\ucc98\uc74c \uc801\uc6a9\ud558\uae30 \uc804\uc5d0\ub294 \ubcc4\ub85c\ub77c\uace0 \uc0dd\uac01\ud588\ub294\ub370, \uc0dd\uac01\ubcf4\ub2e4 \uad1c\ucc2e\uc740 \uac83 \uac19\ub2e4. \\n\\n**\uc77c\uad00\uc131, \uac00\ub3c5\uc131, \ucd94\uc0c1\ud654** \\n\uc774\ubc88 \ub9ac\ubdf0\uc5b4\ub294 \uac80\ud504\ud83c\udf6b \uc600\ub2e4! \\n\uac80\ud504\uc758 \ub9ac\ubdf0\ub294 \uac04\uacb0\ud568\uc5d0 \uad00\ub828\ub41c \ub0b4\uc6a9\uc774 \ub9ce\uc558\ub2e4. \\n\uc77c\uad00\uc131\uc774 \uc788\ub294 \ucf54\ub4dc, \uac00\ub3c5\uc131\uc774 \uc88b\uc740 \ucf54\ub4dc, \ucd94\uc0c1\ud654\uac00 \uc798 \ub418\uc5b4\uc788\ub294 \ucf54\ub4dc \\n\uc77d\uae30 \uc88b\uace0, \uac04\uacb0\ud55c \ubc29\ud5a5\uc73c\ub85c \ucf54\ub4dc\ub97c \uc791\uc131\ud558\ub294 \ubc29\ubc95\uc744 \ubc30\uc6b4 \uac83 \uac19\ub2e4. \\n\ucf54\ub4dc\ub97c \ubc14\ub77c\ubcf4\ub294 \uc2dc\uc810\uc774 \ud558\ub098 \ub298\uc5b4\ub09c \uae30\ubd84\uc774\ub2e4!(\uc55e\uc73c\ub85c \uc801\uc6a9\ud558\ub294 \uac83\uc740 \ub098\uc758 \ubaab\uc774\uc9c0\ub9cc) \\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n**\uc0dd\uac01 \uc815\ub9ac** \\n\uc911\uac04 \uc911\uac04 \ud604\uc7ac \uc0c1\ud669\uc5d0 \ub300\ud574 \uadf8\ub9bc\uc744 \uadf8\ub9ac\uac70\ub098, \uae00\uc744 \uc801\uc73c\uba74\uc11c \uc815\ub9ac\ud55c\ub2e4. \\n\ud398\uc5b4\uc640 \ub3d9\uc77c\ud55c \ubd80\ubd84\uc744 \uc774\ud574\ud558\uace0 \uc788\ub294\uc9c0 \ud655\uc778\ud55c\ub2e4. \\n\uc9c4\ud589\ud558\ub294\ub370 \ub9e4\uc6b0 \ub3c4\uc6c0\uc774 \ub418\uc5c8\ub358 \uac83 \uac19\ub2e4. \\n\ub098\ub3c4 \ub2e4\uc74c \ud398\uc5b4\ub54c\ubd80\ud130 \ud39c\uc774\ub791 \uc885\uc774\ub97c \uc900\ube44\ud574\uc57c\uaca0\ub2e4.\\n\\n**\uac00\uac10\uc5c6\uc774 \uc758\uacac\uc744 \ub9d0\ud574\uc8fc\ub294 \ubd80\ubd84** \\n\uc9c4\ud589 \uc0c1\ud669\uc5d0 \ub300\ud55c \ubd80\ubd84, \uc9c4\ud589 \uc18d\ub3c4, \uc9c0\uae08 \uc790\uc2e0\uc774 \uc774\ud574\ud558\uace0 \uc788\ub294 \ubd80\ubd84\uc744 \ub9d0\ud574\uc918\uc11c \ud3b8\ud588\ub2e4. \\n\ud68c\uace0\ub54c\ub3c4 \uc11c\ub85c \uc194\uc9c1\ud558\uac8c \uc758\uacac\uc744 \uc8fc\uace0 \ubc1b\uc544\uc11c \uc88b\uc558\ub2e4. \\n\\n**\ub3c4\uba54\uc778 \uc5b8\uc5b4\uc5d0 \uc2e0\uacbd\uc4f0\ub294 \ubd80\ubd84** \\n\ud074\ub798\uc2a4\uba85, \ubcc0\uc218\uba85\uacfc \uac19\uc740 \uc5b8\uc5b4\ub97c \uc138\uc2ec\ud558\uac8c \uc2e0\uacbd\uc4f4\ub2e4. \\n\uc694\uad6c\uc0ac\ud56d \uc815\ub9ac\ub3c4 \uae54\ub054\ud558\uac8c \uc798\ud558\ub294 \uac83 \uac19\ub2e4. \\n\\n\ud6c4\ucd94 \ucd5c\uace0 \ud83d\udc4d"},{"id":"ladder-retrospective","metadata":{"permalink":"/ladder-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-02-26-\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-1/2023-02-26-\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0","description":"\uc0ac\ub2e4\ub9ac \ud0c0\uae30","date":"2023-02-26T00:00:00.000Z","formattedDate":"2023\ub144 2\uc6d4 26\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":10.22,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0","slug":"ladder-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0","permalink":"/blackjack-retrospective"},"nextItem":{"title":"\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0","permalink":"/racing-car-retrospective"}},"content":"### \uc0ac\ub2e4\ub9ac \ud0c0\uae30\\n\\n\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158\uc5d0\uc11c\ub294 \uc6b0\uac00\uc640 \ud398\uc5b4\uac00 \ub9e4\uce6d\ub418\uc5c8\ub2e4. \\n\uc774\uc804 \ubbf8\uc158\uacfc \ub2ec\ub9ac TDD\ub85c \uc9c4\ud589\ud558\ub294 \uac83\uc774 \ud544\uc218\uc600\uae30 \ub54c\ubb38\uc5d0 \uc775\uc219\ud558\uc9c0 \uc54a\uc558\uc9c0\ub9cc, \uc6b0\uac00\uc640 \ubbf8\uc158\uc5d0 \uad00\ud55c \uc18c\ud1b5\uc774 \uc798 \ub418\uc5b4\uc11c \ud070 \ubb38\uc81c \uc5c6\uc774 \ubbf8\uc158\uc744 \ub9c8\ubb34\ub9ac\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\\n\uc6b0\uac00\uc640 \uc774\uc57c\uae30\uac00 \uc798 \ud1b5\ud574\uc11c \uadf8\ub7f0\uc9c0 1\ub2e8\uacc4\ub294 \ud06c\uac8c \uc5b4\ub835\uc9c0 \uc54a\uac8c \uc9c4\ud589\ud560 \uc218 \uc788\uc5c8\ub294\ub370, 2\ub2e8\uacc4\uc5d0\uc11c \ub9ce\uc774 \uace0\uc804\ud55c \uac83 \uac19\ub2e4.\\n\\n2\ub2e8\uacc4\uc5d0\uc11c\ub294 2\uac00\uc9c0 \ubc29\ubc95\uc73c\ub85c \uad6c\ud604\ud574\ubd24\ub2e4.\\n\\n1. LadderGame\uc5d0\uc11c Position \uae30\uc900\uc73c\ub85c \uc0ac\ub2e4\ub9ac \uac8c\uc784\uc744 \uc9c4\ud589\ud558\ub294 \ubc29\ubc95\\n2. Player\uc5d0\uac8c Ladder\ub97c \ub118\uaca8\uc11c Ladder\uc5d0\uac8c Position\uc744 \ub118\uaca8\uc8fc\uba70 \uba54\uc2dc\uc9c0\ub97c \ubcf4\ub0b4\ub294 \ubc29\ubc95\\n\\n### Position \uae30\uc900\uc73c\ub85c \uc0ac\ub2e4\ub9ac \uac8c\uc784\uc744 \uc9c4\ud589\ud558\ub294 \ubc29\ubc95\\n\\n\uc0ac\uc2e4\uc0c1 index\ub97c Ladder\uc5d0\uac8c \ub118\uaca8\uc8fc\uace0, \ud574\ub2f9 index\uc5d0 \ub300\ud55c \uacb0\uacfc\ub97c \ubc1b\ub294 \ubc29\ubc95\uacfc \uc720\uc0ac\ud588\ub2e4. \\n\uad6c\ud604\ud558\uace0 \ub098\ub2c8 \ub2e4\ub978 \ud074\ub798\uc2a4\ub4e4\uc774 Position\uc5d0 \ub300\ud55c \uc758\uc874\ub3c4\uac00 \ub108\ubb34 \ub192\uc740 \uac83 \uac19\uc558\ub2e4. \\n\ub610\ud55c Players\uac00 \ubcc4\ub2e4\ub978 \ucc45\uc784\uc744 \uac00\uc9c0\uace0 \uc788\uc9c0 \uc54a\ub2e4\uace0 \ub290\uaf08\ub2e4. \\n\\n```mermaid\\ngraph TD\\n\\n LadderGameController --\x3e LadderGame\\n LadderGame --\x3e Ladder\\n LadderGame --\x3e Players\\n LadderGame --\x3e Items\\n\\n Ladder --\x3e Line\\n Line --\x3e LineStatus\\n\\n LadderGame --\x3e Position\\n Ladder --\x3e Position\\n Items --\x3e Position\\n Line --\x3e Position\\n Players --\x3e Position\\n\\n LadderGame --\x3e LadderGameResult\\n\\n Items --\x3e Item\\n Players --\x3e Player\\n\\n LadderGameController --\x3e InputView\\n LadderGameController --\x3e OutputView\\n\\n```\\n\\n```java\\npublic LadderGameResult play() {\\n final Map result = new LinkedHashMap<>();\\n // \uc0ac\uc6a9\uc790 \uc218\ub9cc\ud07c Position\uc744 \uac00\uc838\uc640\uc11c \uc0ac\ub2e4\ub9ac \uac8c\uc784\uc744 \uc9c4\ud589\ud55c\ub2e4.\\n for (Position position : Position.range(players.count())) {\\n final Position resultPosition = ladder.play(position);\\n result.put(players.get(position), items.get(resultPosition));\\n }\\n return new LadderGameResult(result);\\n}\\n```\\n\\n### Player\uc5d0\uac8c Ladder\ub97c \uc804\ub2ec\ud558\uc5ec \uac8c\uc784\uc744 \uc9c4\ud589\ud558\ub294 \ubc29\ubc95\\n\\nPosition\uc5d0 \ub300\ud55c \uac12\uc744 \uac00\uc9c0\uace0 \uc788\ub294 Player\uc5d0\uac8c Ladder\ub97c \ub118\uaca8\uc11c, Player\uac00 Ladder\uc5d0\uac8c \uba54\uc2dc\uc9c0\ub97c \ubcf4\ub0b4\ub3c4\ub85d \uad6c\ud604\ud558\uc600\ub2e4. \\n\uc774 \ubc29\ubc95\uc774 \uc0ac\ub2e4\ub9ac \uac8c\uc784\uc744 \uc704\ud574\uc11c \uac1d\uccb4\ub4e4\uc774 \uae34\ubc00\ud558\uac8c \ud611\ub825\ud558\uace0, \uc870\uae08 \ub354 \ucc45\uc784\uc758 \ubd84\ubc30\uac00 \uc798 \ub418\uc5b4\uc788\ub2e4\uace0 \uc0dd\uac01\uc774 \ub418\uc5c8\ub2e4. \\n\\n```mermaid\\ngraph TD\\n\\n LadderGameController --\x3e LadderGame\\n LadderGame --\x3e Ladder\\n LadderGame --\x3e Players\\n LadderGame --\x3e Items\\n\\n Ladder --\x3e Line\\n Line --\x3e LineStatus\\n Line --\x3e Position\\n\\n Players --\x3e Ladder\\n Player --\x3e Ladder\\n\\n Item --\x3e Position\\n Player --\x3e Position\\n\\n\\n LadderGame --\x3e LadderGameResult\\n\\n Items --\x3e Item --\x3e ItemName\\n Players --\x3e Player --\x3e PlayerName\\n\\n LadderGameController --\x3e InputView\\n LadderGameController --\x3e OutputView\\n\\n OutputView --\x3e LadderMessageGenerator\\n```\\n\\n```java\\npublic LadderGameResult play() {\\n // \ucc38\uac00\uc790\ub4e4\uc5d0\uac8c \uc0ac\ub2e4\ub9ac\ub97c \uc804\ub2ec\ud574\uc11c \uc0ac\ub2e4\ub9ac\uc5d0\uac8c \uba54\uc2dc\uc9c0\ub97c \ubcf4\ub0b4\ub3c4\ub85d \ud55c\ub2e4.\\n final Map playResult = players.play(ladder);\\n\\n final Map result = new LinkedHashMap<>();\\n for (Player player : playResult.keySet()) {\\n result.put(player, toItem(playResult.get(player)));\\n }\\n return new LadderGameResult(result);\\n}\\n```\\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n**\uc720\ube44\ucffc\ud130\uc2a4 \uc5b8\uc5b4\uc5d0 \uc2dc\uac04\uc744 \ub4e4\uc774\uae30** \\n\uc720\ube44\ucffc\ud130\uc2a4 \uc5b8\uc5b4\ub97c \uc815\ud558\ub294\ub370 \uc2dc\uac04\uc744 \uc870\uae08 \ub354 \ub4e4\uc5ec\uc57c\uaca0\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uc0ac\ub2e4\ub9ac \ud0c0\uae30\uc758 \uc2e4\ud589 \uacb0\uacfc\ub97c Item\uc73c\ub85c \uc9d3\ub2e4\ub2c8.. \ubb54\uac00 \ub9cc\uc871\uc2a4\ub7fd\uc9c0 \uc54a\ub2e4. \\n\uc774\uc804 \ubbf8\uc158\uacfc \ub9c8\ucc2c\uac00\uc9c0\ub85c, \uba85\uba85\ud558\ub294 \ubd80\ubd84\uc5d0\uc11c \ubd80\uc871\ud568\uc744 \ub9ce\uc774 \ub290\uaf08\ub2e4. \\n\\n**\ud398\uc5b4\uc640 \uc870\uae08 \ub354 \uce5c\ud574\uc9c0\uae30** \\n\uccab\ub0a0\uc740 \ud398\uc5b4\uc640 \uce5c\ud574\uc9c0\ub294 \uc2dc\uac04\uc744 \uc870\uae08 \ub354 \uac00\uc838\uc57c\uaca0\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uc6b0\uac00\ub791 \ud68c\uace0\ud560 \ub54c \ub0b4\uac00 \uc2dc\uc791\ud558\uc790\ub9c8\uc790 \ucee8\ubca4\uc158 \uc815\ud558\uc790\uace0 \ud574\uc11c \ub9ce\uc774 \ub2f9\ud669\uc2a4\ub7ec\uc6e0\ub2e4\uace0 \ud55c\ub2e4. \uc6b0\uac00 \ubbf8\uc548.. \ud83e\udd72\\n\\n**README\ub97c \uc870\uae08 \ub354 \uaf3c\uaf3c\ud558\uac8c** \\n\uc774\uc0c1\ud558\uac8c \ucf54\ub529\uc5d0 \uc9d1\uc911\ud558\uba74 README\ub97c \uc5c5\ub370\uc774\ud2b8\ud558\uba74\uc11c \uac19\uc774 \ucee4\ubc0b \ud558\ub294 \uac78 \ud56d\uc0c1 \uae4c\uba39\ub294\ub2e4. \\n\ub2e4\uc74c \ubbf8\uc158\uc5d0\ub294 \uc870\uae08 \ub354 \uc2e0\uacbd \uc368\uc57c\uaca0\ub2e4.\\n\\n**\uc88b\uc740 \uc9c8\ubb38\uc744 \uc0dd\uac01\ud558\uae30** \\n\uccab PR\ub54c \ub9ac\ubdf0\uc5b4\uc5d0\uac8c \uc9c8\ubb38\uc744 \ub0a8\uae30\uc9c0 \ubabb\ud588\ub2e4. \\n\ub9ac\ubdf0\uc5b4\uc640\uc758 \uc2dc\uac04\uc774 \uc18c\uc911\ud55c \uc2dc\uac04\uc774\ub77c\ub294 \uac83\uc744 \uae4c\uba39\uc9c0 \ub9d0\uace0, \ub098\uc758 \uc131\uc7a5\uc5d0 \ub3c4\uc6c0\uc774 \ub420 \uc218 \uc788\ub294 \uc9c8\ubb38\uc744 \uc0dd\uac01\ud574\uc57c\uaca0\ub2e4. \\n\\n**PR \ud6c4\uc5d0\ub3c4 \uaf3c\uaf3c\ud558\uac8c \ud655\uc778\ud558\uae30** \\n\ubd84\uba85 \uc54c\uace0 \uc788\ub294 \ubd80\ubd84\uc774\uc9c0\ub9cc, \ub193\uce5c \ubd80\ubd84\uc774 \ub9ce\uc740 \uac83 \uac19\uc558\ub2e4. \\nPR \ud558\uae30 \uc804\uc5d0\ub3c4 \uacc4\uc18d \ud655\uc778\uc744 \ud588\uc9c0\ub9cc, \uc544\ubb34\ub798\ub3c4 IntelliJ\uc5d0\uc11c \ubcf4\ub2c8 \ucf54\ub4dc\uc5d0 \uc775\uc219\ud574\uc838\uc11c \uadf8\ub7f0\uc9c0 \ubcc0\uacbd\ud574\uc57c \ud560 \ubd80\ubd84\uc774 \uc798 \uc548\ubcf4\uc600\ub2e4. \\ngithub pr\uc5d0\uc11c\ub294 \uc804\uccb4 \ubcc0\uacbd\uc0ac\ud56d\uc744 \ud655\uc778\ud560 \uc218 \uc788\uc73c\ub2c8 PR \ud6c4\uc5d0\ub3c4 \uaf2d \ud655\uc778\ud574\uc57c\uaca0\ub2e4.\\n\\n**\uc801\uadf9\uc801\uc73c\ub85c \ub098\uc758 \uc758\uacac\uc744 \ub9d0\ud558\uae30** \\n\uc758\uacac\uc744 \uc801\uadf9\uc801\uc73c\ub85c \ub0b4\ub294 \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c \ud398\uc5b4\uc758 \uc758\uacac\uc774 \uad1c\ucc2e\ub2e4\uace0 \uc0dd\uac01\ud558\uba74 \uc218\uc6a9 \ud6c4 \uac1c\uc120\uc744 \ud558\ub294 \ubc29\ud5a5\uc73c\ub85c \uc9c4\ud589\uc744 \ud588\uc5c8\ub294\ub370, \uc870\uae08 \ub354 \uac1c\uc120\ud560 \uc218 \uc788\ub294 \ubc29\ud5a5\uc774 \uc788\ub2e4\uba74 \ub098\ub3c4 \uc801\uadf9\uc801\uc73c\ub85c \uc758\uacac\uc744 \ub9d0\ud574\uc57c\uaca0\ub2e4\uace0 \uc0dd\uac01\uc774 \ub4e0\ub2e4. \\n\ub098\ub3c4 \uc124\ub4dd\ud558\ub294 \ud798\uc744 \uae30\ub974\uace0, \ud398\uc5b4\ub3c4 \uc88b\uc740 \ubc29\ud5a5\uc744 \uc54c \uc218 \uc788\uace0, \uacb0\uacfc\ubb3c\ub3c4 \uc88b\uc740 \ubc29\ud5a5\uc73c\ub85c \ub098\uc624\uc9c0 \uc54a\uc744\uae4c? (\uace0\ubbfc \ub4e4\uc5b4\uc8fc\uc2e0 \ub9ac\ubdf0\uc5b4 \ud130\ud2c0\ud83d\udc22 \uac10\uc0ac\ud569\ub2c8\ub2e4.)\\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**\uac1d\uccb4\uc758 \uc0dd\uc131 \ucc45\uc784** \\nPlayers\uac00 Position\uc744 \uc0dd\uc131\ud558\uace0 Player\uc758 \uc0dd\uc131\uc790\uc5d0 \ub123\uc5b4\uc8fc\uc5c8\ub2e4. \ud558\uc9c0\ub9cc \uc774 \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c \uc0dd\uc131 \ucc45\uc784\uc5d0 \uad00\ub828\ub41c \ucf54\uba58\ud2b8\uac00 \ub2ec\ub838\ub2e4.\\n\uc2dc\uac04\uc744 \uac00\uc9c0\uace0 \uc0dd\uac01\ud574 \ubcf4\ub2c8 Position\uc744 \uac00\uc9c0\uace0 \uc788\ub294 \uac74 Player\uae30 \ub54c\ubb38\uc5d0 \uc0dd\uc131 \ucc45\uc784\uc744 Player\uac00 \ub2f4\ub2f9\ud558\ub294 \uac83\uc774 \uc88b\ub2e4\uace0 \uc0dd\uac01\ub418\uc5c8\ub2e4. \\n\\n\uc0dd\uc131 \ucc45\uc784\uc5d0 \uad00\ud55c \ud328\ud134\uc73c\ub85c GRASP\uc758 Creator \ud328\ud134\uc774 \uc788\ub294\ub370 \ub2e4\uc74c\uc758 \uc694\uc18c\ub97c \ucd5c\ub300\ud55c \ub9cc\uc871\ud558\ub294 \ud074\ub798\uc2a4\uc5d0 \uc0dd\uc131 \ucc45\uc784\uc744 \ud560\ub2f9\ud558\ub294 \uac83\uc774 \uc88b\ub2e4. \\n- B\uac00 A \uac1d\uccb4\ub97c \ud3ec\ud568 \ub610\ub294 \ucc38\uc870\ud55c\ub2e4.\\n- B\uac00 A \uac1d\uccb4\ub97c \uae30\ub85d\ud55c\ub2e4.\\n- B\uac00 A\ub97c \uae34\ubc00\ud558\uac8c \uc0ac\uc6a9\ud55c\ub2e4.\\n- B\uac00 A\uc758 \ucd08\uae43\uac12\uc744 \uac00\uc9c0\uace0 \uc788\ub2e4. \\n\\n\uc2e4\uc81c\ub85c \uac1d\uccb4\uc758 \uc0dd\uc131 \ucc45\uc784\uc5d0 \uad00\ud574\uc11c \uae4a\uc774 \uc0dd\uac01\ud558\uba74\uc11c \ucf54\ub529\uc744 \ud558\uc9c0 \uc54a\uc558\ub294\ub370, \uc774\ubc88 \ubbf8\uc158\uc744 \ud1b5\ud574 \uc2dc\uc57c\uac00 \ub113\uc5b4\uc9c4 \uac83 \uac19\ub2e4.\\n\\n**\ud328\ud0a4\uc9c0 \ubd84\ub9ac \uae30\uc900** \\n\ud328\ud0a4\uc9c0 \ubd84\ub9ac\uc5d0 \ub300\ud55c \ub098\ub9cc\uc758 \uae30\uc900\uc774 \uc544\uc9c1 \uba85\ud655\ud558\uc9c0 \uc54a\uc544 \uc9c8\ubb38\uc774 \ub4e4\uc5b4\uc640\ub3c4 \uba85\ud655\ud558\uac8c \ub2f5\ubcc0\uc744 \ud558\uc9c0 \ubabb\ud588\ub2e4. \\n\ub9c8\uc9c0\ub9c9 \uc81c\ucd9c \uc804\uc5d0 \ub3c4\uba54\uc778 \ud328\ud0a4\uc9c0 \ub0b4\ubd80\ub97c \ubd84\ub9ac\ud574 \ubd24\ub294\ub370, \uae30\uc900\uc774 \uba85\ud655\ud558\uc9c0 \uc54a\uc558\uae30 \ub54c\ubb38\uc5d0 \uc88b\uc9c0 \uc54a\uc740 \uc120\ud0dd\uc774\uc5c8\ub358 \uac83 \uac19\ub2e4.\\n\ud604\uc7ac \uc9c4\ud589\ud558\ub294 \ubbf8\uc158\uc758 \uc560\ud50c\ub9ac\ucf00\uc774\uc158 \ud06c\uae30\uac00 \uadf8\ub807\uac8c \ud06c\uc9c0 \uc54a\uc73c\ub2c8, domain \ud328\ud0a4\uc9c0\uc5d0\uc11c \uc138\ubd80 \ud328\ud0a4\uc9c0\ub85c \ubd84\ub9ac\ud558\uc9c0 \uc54a\uc544\ub3c4 \ub420 \uac83 \uac19\ub2e4. \\n\\n**\uc0ac\uc6a9\ud558\ub294 \ucabd\uc5d0\uc11c \uc0dd\uac01\ud558\uae30 & \uc608\uce21\uac00\ub2a5\ud55c \ucf54\ub4dc \uc791\uc131\ud558\uae30** \\nPosition\uc5d0\uc11c \ub2e4\uc74c \uc704\uce58\ub098 \uc774\uc804 \uc704\uce58\ub97c \ubc18\ud658\ud558\ub294 \uba54\uc11c\ub4dc\ub97c \ud5c8\uc6a9 \ubc94\uc704(0~19)\uac00 \ubc97\uc5b4\ub09c\ub2e4\uba74, \uc758\ubbf8 \uc5c6\ub294 \uac12\uc774 \ub4e4\uc5b4\uac04 Position\uc744 \ubc18\ud658\ud558\ub3c4\ub85d \ud588\ub2e4. \\n\uc774\uac74 Position\uc744 \uc0ac\uc6a9\ud558\ub294 \uc785\uc7a5\uc744 \uace0\ub824\ud558\uc9c0 \ubabb\ud55c \ucf54\ub529\uc774\uc5c8\ub294\ub370, \uc0ac\uc6a9\ud558\ub294 \uc785\uc7a5\uc5d0\uc11c\ub294 0~19\uc758 \uac12\uc774 \ubcf4\uc7a5\ub418\uc5b4 \uc788\ub2e4\uace0 \uc0dd\uac01\ud560 \uac83\uc774\uae30 \ub54c\ubb38\uc774\ub2e4. \\n\ub530\ub77c\uc11c hasNext, hasPrevious\ub77c\ub294 \uc774\uc804 \uac12, \uc774\ud6c4 \uac12\uc774 \ubc94\uc704 \ub0b4\uc5d0 \uc788\ub294\uc9c0 \ud655\uc778\ud558\ub294 \uba54\uc11c\ub4dc\ub97c \ucd94\uac00\ud558\uace0, \uae30\uc874\uc758 \uac12\uc744 \uac00\uc838\uc624\ub294 \uba54\uc11c\ub4dc\ub294 \ubc94\uc704\uac00 \ubc97\uc5b4\ub098\uba74 \uc608\uc678\ub97c \ub358\uc9c0\ub294 \ubc29\ud5a5\uc73c\ub85c \ud574\uacb0\ud558\uc600\ub2e4. \\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n\ubc1d\uc740 \uae30\uc6b4\uc744 \uac00\uc9c0\uace0 \uc788\uace0 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uacfc \uce5c\ud654\ub825\uc774 \uc88b\uc740 \uac83 \uac19\uc558\ub2e4. \\n\uc774\ubc88\uc5d0 \ud398\uc5b4 \ud560 \ub54c \ucee8\ub514\uc158 \uad00\ub9ac\ub97c \uc81c\ub300\ub85c \ubabb\ud574\uc11c \ub9ce\uc774 \ubbf8\uc548\ud588\ub2e4. \ub2e4\uc74c\uc5d0\ub294 \ucd5c\uc0c1\uc758 \ucee8\ub514\uc158\uc73c\ub85c \ud398\uc5b4\ub97c \uc900\ube44\ud574 \ubd10\uc57c\uaca0\ub2e4. \\n\uadf8\ub9ac\uace0 \uc6b0\uac00\ub791 \ud398\uc5b4\ub97c \ud558\uace0 \ub098\uc11c, \ub098\ub3c4 \ub2e4\ub978 \uc0ac\ub78c\ub4e4\uacfc \ub354 \uc798 \uc9c0\ub0b4\ubd10\uc57c\uaca0\ub2e4\ub294 \uc0dd\uac01\uc774 \ub4e4\uc5b4 \uc870\uae08 \ub354 \uc6a9\uae30\ub97c \ub0b4 \uc7a1\ub2f4 \uc911\uc774\ub2e4! \\n\\n\uc758\uacac\uc744 \uc801\uadf9\uc801\uc73c\ub85c \ub0b4\uc918\uc11c \ud398\uc5b4\ud504\ub85c\uadf8\ub798\ubc0d \uc9c4\ud589\uc774 \uc798 \ub418\uc5c8\ub2e4. \\n\ub610\ud55c \ud398\uc5b4 \uc9c4\ud589\uc774 \ub290\ub9b0 \uac83 \uac19\ub2e4\uace0 \ub9d0\ud574\uc918\uc11c \uc548\uc815\uc801\uc73c\ub85c \uc2dc\uac04 \uc548\uc5d0 \ubbf8\uc158\uc744 \uc644\ub8cc\ud560 \uc218 \uc788\uc5c8\ub2e4. \\n\ud398\uc5b4\ud504\ub85c\uadf8\ub798\ubc0d \uc9c4\ud589 \uc18d\ub3c4\uc5d0 \ub300\ud574 \uc870\uae08 \ub354 \uc0dd\uac01\uc744 \ud574\ubd10\uc57c\uaca0\ub2e4!\\n\\n\ud56d\uc0c1 \uc9c0\ub098\uac08 \ub54c\ub9c8\ub2e4 \uc6c3\uc5b4\uc8fc\ub294\ub370, \ub098\ub3c4 \uc790\uc8fc \uc6c3\uc5b4\uc57c\uaca0\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\uc6c3\ub294 \uac83\ub9cc\uc73c\ub85c\ub3c4 \uc0ac\ub78c\uc774 \ubc1d\uc544 \ubcf4\uc5ec\uc11c \ub108\ubb34 \uc88b\uc740 \uac83 \uac19\ub2e4!"},{"id":"racing-car-retrospective","metadata":{"permalink":"/racing-car-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-02-14-\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0.mdx","source":"@site/blog/2023-1/2023-02-14-\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0.mdx","title":"\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0","description":"\uc790\ub3d9\ucc28 \uacbd\uc8fc","date":"2023-02-14T00:00:00.000Z","formattedDate":"2023\ub144 2\uc6d4 14\uc77c","tags":[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse"},{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":7.56,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0","slug":"racing-car-retrospective","tags":["Woowahan Techcourse","Retrospective"]},"prevItem":{"title":"\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0","permalink":"/ladder-retrospective"},"nextItem":{"title":"Parameterized Tests","permalink":"/parameterized-tests"}},"content":"### \uc790\ub3d9\ucc28 \uacbd\uc8fc\\n\\n\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158\uc5d0\uc11c\ub294 \ub2e4\uc990\uacfc \ud398\uc5b4\uac00 \ub9e4\uce6d\ub418\uc5c8\ub2e4. \\n\uc6b0\ud14c\ucf54 \ub4e4\uc5b4\uc640\uc11c \uccab \ud398\uc5b4\ud504\ub85c\uadf8\ub798\ubc0d\uc774\ub77c \ub9ce\uc774 \ub5a8\ub838\uc9c0\ub9cc, \ub2e4\uc990\uc774 \ub300\ud654\ub97c \uc798 \uc774\ub04c\uc5b4\uc918 \ub108\ubb34 \uc990\uac70\uc6e0\ub2e4. \\n\\n\uccab\ub0a0\uc740 \uac04\ub2e8\ud788 \ucee8\ubca4\uc158\uacfc \ud658\uacbd\uc744 \uc124\uc815\ud558\ub294 \uc2dc\uac04\uc744 \uac00\uc84c\uace0 \ub2e4\uc74c \ub0a0\ubd80\ud130 \uc790\ub3d9\ucc28 \uacbd\uc8fc\ub97c \uc2dc\uc791\ud588\ub2e4. \\n\uc2dc\uc791\uc740 \uac04\ub2e8\ud558\uac8c \uc694\uad6c\uc0ac\ud56d\uc744 \uc815\ub9ac\ud558\uace0, \uc5b4\ub5bb\uac8c \ucf54\ub4dc\ub97c \uc791\uc131\ud560\uc9c0 \uac19\uc774 \uace0\ubbfc\ud588\ub2e4. \\n\\n\uc2dc\uc791\ud558\uae30 \uc804 \uc544\ub798\uc640 \uac19\uc774 mermaid\ub97c \uc774\uc6a9\ud558\uc5ec \uc758\uc874\uc131 \ubc29\ud5a5\uc5d0 \ub300\ud574\uc11c \uac04\ub2e8\ud55c \ub2e4\uc774\uc5b4\uadf8\ub7a8\uc744 \ub9cc\ub4e4\uace0 \uc2dc\uc791\ud588\ub2e4. \\nmermaid\ub294 \ucf54\ub4dc\ub85c \ub2e4\uc774\uc5b4\uadf8\ub7a8\uc744 \uc0dd\uc131 \ud574\uc8fc\ub294 \ub3c4\uad6c\ub85c \ub2e4\uc74c\uacfc \uac19\uc740 \uc7a5\uc810\uc774 \uc788\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4.\\n\\n- \ucf54\ub4dc \uae30\ubc18\uc774\ub77c \ube60\ub978 \uc2dc\uac04 \uc548\uc5d0 \uc0dd\uac01\ud55c \uac83\uc744 \uc2dc\uac01\ud654\ud560 \uc218 \uc788\ub2e4. \\n- github\uc5d0\uc11c mermaid\ub97c \uc9c0\uc6d0\ud558\uae30 \ub54c\ubb38\uc5d0 \ub9ac\ubdf0\uc5b4\uc5d0\uac8c \ucf54\ub4dc\ub97c \uc774\ud574\ud560 \uc218 \uc788\ub294 \ubd80\uac00\uc801\uc778 \uc815\ubcf4\ub97c \uc81c\uacf5\ud560 \uc218 \uc788\ub2e4.\\n\\n```mermaid\\n---\\ntitle: \uc790\ub3d9\ucc28 \uacbd\uc8fc \uccab \ub9ac\ubdf0 \uc694\uccad\uc2dc \uad6c\uc870\\n---\\ngraph TD\\n Cars --\x3e Car\\n Car --\x3e Name\\n Car --\x3e Position\\n RacingGame --\x3e Count\\n RacingGame --\x3e NumberGenerator\\n RacingGame --\x3e Cars\\n RacingCarController --\x3e RacingGame\\n RandomNumberGenerator -.-> NumberGenerator\\n RacingCarController --\x3e InputView\\n InputView --\x3e InputValidator\\n RacingCarController --\x3e OutputView\\n```\\n\\n\ubbf8\uc158\uc744 \uc9c4\ud589\ud558\ub294 \ub370 \ud070 \uc5b4\ub824\uc6c0\uc774 \uc788\uc9c0\ub294 \uc54a\uc558\uace0, \ud398\uc5b4\ub97c \ub9c8\uce58\uae30 \uc804 \uc11c\ub85c \uace0\ubbfc\ub418\ub294 \ubd80\ubd84\uc744 \uc815\ub9ac\ud588\uc744 \ub54c \uc88b\uc558\ub2e4.\\n\\n\ud398\uc5b4\ud558\uba74\uc11c \uc798\ud588\ub2e4\uace0 \uc0dd\uac01\ud588\ub358 \uc810\uc740 \uc11c\ub85c\uc758 \uc0dd\uac01\uacfc \ub9ac\ubdf0 \ubc1b\uc740 \uac83\uc744 \uacf5\uc720\ud55c \uac83\uc774\ub2e4. \\n\ub9ac\ud329\ud130\ub9c1\uc744 \uc5b4\ub5bb\uac8c \ud588\ub294\uc9c0? \uc774\ub7f0 \ub9ac\ubdf0\uc5d0 \ub300\ud574 \uc5b4\ub5bb\uac8c \uc0dd\uac01\ud558\ub294\uc9c0 \uae4a\uac8c \uace0\ubbfc\ud558\ub294 \uc2dc\uac04\uc744 \uac00\uc9c8 \uc218 \uc788\uc5c8\ub2e4.\\n\\n### \ubd80\uc871\ud588\ub358 \ubd80\ubd84\\n\\n\ub9ac\ud329\ud130\ub9c1\uc774 \ub05d\ub09c \ud6c4 \uba54\uc11c\ub4dc\uba85, \ud14c\uc2a4\ud2b8\uc2dc \ucd9c\ub825\ud558\ub294 \uba54\uc2dc\uc9c0\uc5d0 \ub300\ud55c \ucf54\uba58\ud2b8\uac00 \ub9ce\uc774 \ub2ec\ub838\ub2e4. \\n\uac1d\uccb4\uac00 \uc5b4\ub5a4 \ucc45\uc784\uacfc \uc5ed\ud560\uc744 \uac00\uc9c0\ub294\uc9c0 \uc0dd\uac01\ud558\ub294 \uc2dc\uac04\uc744 \uac00\uc9c0\uace0 \uba85\ud655\ud55c \uba54\uc11c\ub4dc\uba85\uc744 \uc791\uc131\ud574\uc57c\uaca0\ub2e4\uace0 \uc0dd\uac01\ud588\ub2e4. \\n\\n\ud3c9\uc18c\uc5d0 \ud504\ub85c\uadf8\ub798\ubc0d \uc774\uc57c\uae30\uac00 \uc544\ub2cc \ub2e4\ub978 \uc8fc\uc81c\ub85c \uc774\uc57c\uae30\ud558\uba74 \uc798 \ub4e4\uc73c\ub824\uace0 \ud558\ub294 \ud3b8\uc774\uc9c0\ub9cc \\n\ub0b4\uac00 \uc88b\uc544\ud558\ub294 \uc8fc\uc81c, \uad00\uc2ec\uac00\ub294 \uc8fc\uc81c\uc778 \ud504\ub85c\uadf8\ub798\ubc0d\uc5d0 \ub300\ud55c \uc774\uc57c\uae30\ub97c \ud560 \ub550 \ub9d0\uc774 \ub9ce\uc544\uc9c4\ub2e4. \\n\ub2e4\uc74c \ubbf8\uc158\ubd80\ud130\ub294 \ub354 \ub9ce\uc740 \uc2dc\uac04\uc744 \ud398\uc5b4\uc758 \uc758\uacac\uacfc \uc774\uc57c\uae30\ub97c \ub4e3\ub294 \uacf3\uc5d0 \uc0ac\uc6a9\ud574\uc57c\uaca0\ub2e4.\\n\\n### \uc0c8\ub85c \ud559\uc2b5\ud55c \ubd80\ubd84\\n\\n**Assertions extracting**\\n\\n\uacb0\uacfc \ub0b4\ubd80\uc5d0 \uc788\ub294 \uac12\uc744 \ud655\uc778\ud558\uace0 \uc2f6\uc744 \ub54c extracting \ud0a4\uc6cc\ub4dc\ub97c \uc774\uc6a9\ud574\uc11c \ub0b4\ubd80\uc758 \uac12\uc744 \uac80\uc99d\ud560 \uc218 \uc788\ub2e4. \\n\uc774\uc804\uc5d0\ub294 \ud544\uc694\uc5d0 \ub530\ub77c stream\uc744 \uc774\uc6a9\ud558\uc5ec \uac80\uc99d\ud560 \uac12\uc744 \uc0dd\uc131\ud588\uc9c0\ub9cc, \ud574\ub2f9 \ubc29\ubc95\uc744 \uc774\uc6a9\ud574\uc11c \uc808\ucc28\ub97c \uc904\uc77c \uc218 \uc788\uc5c8\ub2e4.\\n\\n```java\\n@Test\\nvoid extracting() {\\n final Cars cars = new Cars(List.of(\\"car1\\", \\"car2\\"));\\n\\n assertThat(cars.getCars())\\n .extracting(Car::getName)\\n .containsExactly(\\"car1\\", \\"car2\\");\\n}\\n```\\n\\n---\\n\\n\uc544\ub798\ub294 \ub9ac\ubdf0\uc5b4\ub2d8\uacfc \ub300\ud654\ub97c \ub098\ub204\uba74\uc11c \uc5bb\uc740 \ub2f5\ubcc0 + \ub098\uc758 \uc758\uacac\uc774\ub2e4.\\n\\n**\uc81c\uc5b4\ud560 \uc218 \uc5c6\ub294 \ubd80\ubd84\uc5d0 \ub300\ud55c \ud14c\uc2a4\ud2b8**\\n\\n\ud14c\uc2a4\ud2b8 \ub300\uc0c1\uc774 \uac80\uc99d\ub41c \uac83\uc774\ub77c\uba74 \uc791\uc131\ud558\uc9c0 \uc54a\uac70\ub098, \uc81c\uc5b4\ud560 \uc218 \uc788\ub294 \ubd80\ubd84\uc5d0 \ub300\ud55c \ud14c\uc2a4\ud2b8\ub97c \ub354\uc6b1 \uaf3c\uaf3c\ud558\uac8c \uc791\uc131\ud55c\ub2e4. \\n\uc774\uac74 \uac1c\uc778\uc801\uc778 \uc0dd\uac01\uc774\uc9c0\ub9cc \ub0b4\uac00 \uc548\uc815\uac10\uc774 \ub4e4 \uc218 \uc788\uc744 \uc815\ub3c4\ub85c \ucd9c\ub825 \ubc94\uc704 \ub0b4\uc758 \uacb0\uacfc\ub97c \ubc18\ud658\ud558\ub294\uc9c0 \uc815\ub3c4 \ud14c\uc2a4\ud2b8\ud560 \uc218 \uc788\uc9c0 \uc54a\uc744\uae4c?\\n\\n**\ub2e8\uc21c \uc704\uc784\uc744 \ud558\ub294 \uba54\uc11c\ub4dc\uc5d0 \ub300\ud55c \ud14c\uc2a4\ud2b8**\\n\\n\uc704\uc784\uc774\ub77c\ub294 \uac83\uc740 \uc5ed\ud560\uacfc \ucc45\uc784\uc744 \ub118\uaca8\uc900\ub2e4\ub294 \uac83\uc774\ub2e4. \\n\ud638\ucd9c \ud69f\uc218\ub97c \uac80\uc99d\ud558\ub294 \uac83\ubcf4\ub2e4 \uacb0\uacfc\uc5d0 \ub300\ud55c \ud14c\uc2a4\ud2b8\ud558\ub294 \uac83\uc774 \uc88b\ub2e4. \\n\ub2e8\uc21c\ud788 \uc704\uc784\ub9cc \ud558\ub294 \ud14c\uc2a4\ud2b8\uc758 \uacbd\uc6b0 \uacb0\uacfc\ub97c \uac80\uc99d\ud55c\ub2e4\uba74 \ud14c\uc2a4\ud2b8\uac00 \uc911\ubcf5\ub418\uc9c0 \uc54a\uc744\uae4c \uc0dd\uac01\ud588\uc5c8\ub2e4. \\n\ub530\ub77c\uc11c \uc911\ubcf5\ub41c \ud14c\uc2a4\ud2b8\ub97c \uc904\uc774\uae30 \uc704\ud574 \ub0b4\ubd80\uc758 \uba54\uc11c\ub4dc\ub97c \ud638\ucd9c\ud558\ub294\uc9c0 \uac80\uc99d\ud558\ub294 \ubc29\ubc95\ub3c4 \uc788\ub2e4\ub294 \uac83\uc744 \uc54c\uac8c \ub418\uc5c8\uc9c0\ub9cc \\n\uc548\uc815\uc801\uc73c\ub85c \uacb0\uacfc\ub97c \ud14c\uc2a4\ud2b8 \ud558\ub294 \uac83\uc774 \ub354 \uc88b\uc740 \ubc29\ubc95\uc778 \uac83 \uac19\ub2e4.\\n\\n**\ud14c\uc2a4\ud2b8\ub97c \uc704\ud55c getter \uc0ac\uc6a9**\\n\\n\ud14c\uc2a4\ud2b8 \uc6a9\ub3c4\ub85c \ub3c4\uba54\uc778\uc5d0 \uc0c8\ub85c\uc6b4 \uba54\uc11c\ub4dc\uac00 \uc0dd\uc131\ub418\ub294 \uac83\uc740 \uc88b\uc9c0 \ubabb\ud558\ub2e4. \\n\ud544\uc694\uc758 \uacbd\uc6b0 \uc0dd\uc131\ud574\uc11c \uc0ac\uc6a9\ud560 \uc218 \uc788\uc9c0\ub9cc, \uae30\uc874\uc5d0 \uc788\ub294 \uba54\uc11c\ub4dc\ub4e4\uc744 \ud65c\uc6a9\ud574\ubcf4\ub294 \uac83\uc774 \ub354 \uc88b\uc740 \ubc29\ubc95\uc774\ub2e4. \\n\uc774 \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c \ub9e4\uc6b0 \ub3d9\uc758\ud558\uace0, \uc55e\uc73c\ub85c\ub3c4 \ucd5c\ub300\ud55c \ud14c\uc2a4\ud2b8\ub97c \uc704\ud55c \ucf54\ub4dc\ub97c \ub3c4\uba54\uc778\uc5d0 \uc791\uc131\ud558\uc9c0 \uc54a\uc744 \uac83 \uac19\ub2e4.\\n\\n### \ud398\uc5b4\uc5d0\uac8c \ubc30\uc6b8 \ubd80\ubd84\\n\\n\uc9c8\ubb38\uc774\ub098 \uc0dd\uac01\ud560 \uc810\uc774 \uc788\uc744 \ub54c \ub9e4\uc6b0 \uae4a\uac8c \uace0\ubbfc\ud558\ub294 \uac83 \uac19\uc558\ub2e4. \\n\uc0dd\uac01\uc744 \uc815\ub9ac\ud55c \ud6c4 \uc790\uc2e0\uc758 \uc758\uacac\uc744 \uba85\ub8cc\ud558\uac8c \uc804\ub2ec\ud574\uc8fc\uc5c8\ub2e4. \\n\uadf8\ub807\uae30 \ub54c\ubb38\uc5d0 \uc9c0\uc2dd\uc744 \ud6a8\uc728\uc801\uc73c\ub85c \uc2b5\ub4dd\ud55c\ub2e4. \\n\ub09c \uc0dd\uac01\uc744 \uc798 \uc815\ub9ac\ud558\uc9c0 \uc54a\uc740 \ucc44\ub85c \ub0b4\ubc84\ub824 \ub454 \uc595\uc740 \uc9c0\uc2dd\uc774 \ub9ce\uc740 \uac83 \uac19\ub2e4. (\uc774\ub7f0 \uac83\ub3c4 \uc544\ub294 \uac83\uc774\ub77c\uace0 \ud560 \uc218 \uc788\uc744\uae4c?) \\n\uc55e\uc73c\ub85c \uc870\uae08 \ub354 \uba38\ub9bf\uc18d\uc5d0\uc11c \uc815\ub9ac\ud558\uace0, \ubb38\uc81c\uc5d0 \ub300\ud574 \uae4a\uac8c \uace0\ubbfc\ud558\ub294 \uc2dc\uac04\uc744 \ub298\ub824\uc57c\uaca0\ub2e4.\\n\\n\uac1c\ubc1c\uc5d0 \uc5f4\uc815\uc744 \uac00\uc9c4 \uac8c \ub290\uaef4\uc9c4\ub2e4. \\n\ub098\ub3c4 \uac1c\ubc1c\uc744 \uc88b\uc544\ud558\uc9c0\ub9cc, \ucd5c\uadfc\uc5d0\ub294 \uc758\uc9c0\uac00 \uc57d\ud574\uc84c\uc5c8\ub2e4. \\n\uc5f4\uc815\uc774 \uac00\ub4dd\ud55c \uc0ac\ub78c\uc744 \ub9cc\ub098\ub2c8 \ub098\ub3c4 \uc5f4\uc815\uc801\uc778 \uc0ac\ub78c\uc774 \ub418\ub294 \uac83 \uac19\ub2e4.\\n\\n\uce6d\ucc2c\uc744 \ub9ce\uc774 \ud574\uc900\ub2e4. \ub2e8\uc21c\ud788 \ub9ce\uc774 \ud574\uc8fc\ub294 \uac83\uc774 \uc544\ub2c8\ub77c, \uc9c4\uc2ec\uc744 \ub2f4\uae34 \uce6d\ucc2c\uc744 \ud574\uc92c\ub2e4. \\n\uce6d\ucc2c\uc740 \uace0\ub798\ub3c4 \ucda4\ucd94\uac8c \ud558\ub358\uac00? \\n\uadf8\ub798\uc11c \uc990\uac70\uc6b4 \ub9c8\uc74c\uc73c\ub85c \ud398\uc5b4 \ud504\ub85c\uadf8\ub798\ubc0d\uc744 \ud588\uc5c8\ub358 \uac83 \uac19\ub2e4.\\n\\n\uc5b4\ub5a4 \uc774\uc720 \ub54c\ubb38\uc778\uc9c0 \ubaa8\ub974\uaca0\uc9c0\ub9cc \uac19\uc774 \ud398\uc5b4\ud558\ub294\ub370 \ud3b8\ud55c \ub9c8\uc74c\uc774 \ub4e4\uc5c8\ub2e4. \\n\uc774\uac74 \ubc14\ub85c \ubc30\uc6b8 \uc218 \uc5c6\uc9c0\ub9cc. \\n\ub098\ub3c4 \uac19\uc774 \uc77c\ud560 \ub54c \ud3b8\ud55c \uc0ac\ub78c, \uac19\uc774 \uc77c\ud558\uace0 \uc2f6\uc740 \uc0ac\ub78c\uc774 \ub418\uae30 \uc704\ud574 \uae4a\uc774 \uace0\ubbfc\ud574\ubd10\uc57c\uaca0\ub2e4."},{"id":"parameterized-tests","metadata":{"permalink":"/parameterized-tests","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-02-12-Parameterized Tests.mdx","source":"@site/blog/2023-1/2023-02-12-Parameterized Tests.mdx","title":"Parameterized Tests","description":"\ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub2e4\ubcf4\uba74 \ub9e4\uac1c\ubcc0\uc218\uc5d0 \ub530\ub77c \ubc18\ubcf5\uc774 \ub418\ub294 \ud14c\uc2a4\ud2b8\ub4e4\uc774 \uc0dd\uae34\ub2e4.","date":"2023-02-12T00:00:00.000Z","formattedDate":"2023\ub144 2\uc6d4 12\uc77c","tags":[{"label":"Java","permalink":"/tags/java"}],"readingTime":3.17,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"Parameterized Tests","slug":"parameterized-tests","tags":["Java"]},"prevItem":{"title":"\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0","permalink":"/racing-car-retrospective"},"nextItem":{"title":"IntelliJ \uc124\uc815","permalink":"/intellij-settings"}},"content":"\ud14c\uc2a4\ud2b8\ub97c \uc791\uc131\ud558\ub2e4\ubcf4\uba74 \ub9e4\uac1c\ubcc0\uc218\uc5d0 \ub530\ub77c \ubc18\ubcf5\uc774 \ub418\ub294 \ud14c\uc2a4\ud2b8\ub4e4\uc774 \uc0dd\uae34\ub2e4. \\n\uc774 \ub54c `@ParameterizedTest`\ub97c \uc0ac\uc6a9\ud558\uba74 \ub2e8\uc77c \ud14c\uc2a4\ud2b8\ub97c \ub9e4\uac1c\ubcc0\uc218\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc5ec\ub7ec \ubc88 \ubc18\ubcf5\ud560 \uc218 \uc788\ub2e4.\\n\\n## Argument Sources\\n\\n`@ParameterizedTest`\ub97c \uc0ac\uc6a9\ud558\ub824\uba74 \ucd5c\uc18c \ud558\ub098 \uc774\uc0c1\uc758 Source \uc560\ub178\ud14c\uc774\uc158\uc774 \ud544\uc694\ud558\ub2e4. \\nJUnit\uc774 \uc81c\uacf5\ud558\ub294 \ub2e4\uc591\ud55c Source\uac00 \uc788\uae30 \ub54c\ubb38\uc5d0, \ud14c\uc2a4\ud2b8\uc5d0 \ub9de\ucdb0 \ub2e4\uc591\ud558\uac8c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n### Value Source\\n\\n\uac12\uc744 \uc774\uc6a9\ud558\uc5ec \uc81c\uacf5\ud558\ub294 \ud615\ud0dc\ub85c, \ub2e4\uc74c\uacfc \uac19\uc740 \ud0c0\uc785\uc758 \uac12\uc744 \ub9e4\uac1c\ubcc0\uc218\ub85c \uc81c\uacf5\ud560 \uc218 \uc788\ub2e4. \\n- short, int, long, float, double\\n- byte, char, boolean, String, Class \\n\\n```java\\n@ParameterizedTest\\n@ValueSource(ints = {1, 100, Integer.MAX_VALUE})\\nvoid valueTest(final int value) {\\n Assertions.assertThat(value).isPositive();\\n}\\n```\\n\\n### Null & Empty Source\\n\\nnull \uac12, \ube48 \uac12\uc744 \uc81c\uacf5\ud55c\ub2e4. \\nEmpty Source\uc758 \uacbd\uc6b0 \ub2e4\uc74c\uacfc \uac19\uc740 \ud0c0\uc785\uc5d0 \ud55c\ud574 \ub9e4\uac1c\ubcc0\uc218\ub85c \uc81c\uacf5\ud560 \uc218 \uc788\ub2e4.\\n- String\\n- java.util.List, java.util.Set, java.util.Map\\n- primitive arrays \u2014 ex) int[]\\n- object arrays \u2014 ex) String[]\\n\\n```java\\n@ParameterizedTest\\n@NullAndEmptySource\\nvoid nullAndEmptyTest(final String value) {\\n Assertions.assertThat(value).isNullOrEmpty();\\n}\\n```\\n\\n### Enum Source\\n\\nEnumSource\ub97c \uc774\uc6a9\ud558\uc5ec Enum \ub610\ud55c \ub9e4\uac1c\ubcc0\uc218\ub85c \uc81c\uacf5\ud560 \uc218 \uc788\ub2e4.\\n\\n```java\\nenum Day {\\n MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;\\n}\\n\\n@ParameterizedTest\\n@EnumSource(Day.class)\\nvoid enumTest(final Day day) {\\n assertThat(day).isInstanceOf(Day.class);\\n}\\n```\\n\\n\ub2e4\uc74c\uacfc \uac19\uc774 mode \uac12\uc744 \uc774\uc6a9\ud558\uc5ec \ud2b9\uc9d5 Enum\uc744 \uc81c\uc678\ud558\uac70\ub098, \ud3ec\ud568\uc2dc\ud0ac \uc218 \uc788\ub2e4. (default: Mode.Include)\\n\\n```java\\n@ParameterizedTest\\n@EnumSource(value = Day.class, names = {\\"SATURDAY\\", \\"SUNDAY\\"}, mode = Mode.EXCLUDE)\\nvoid enumTest(final Day day) {\\n // MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY\\n assertThat(day).isInstanceOf(Day.class);\\n}\\n```\\n\\n### CSV Source\\n\\ncsv \ud615\uc2dd\uc758 \uac12\uc744 \uc774\uc6a9\ud558\uc5ec \ub9e4\uac1c\ubcc0\uc218\ub97c \uc81c\uacf5\ud55c\ub2e4. \\n\uad6c\ubd84\uc790\uc758 \uae30\ubcf8\uac12\uc740 \uc27c\ud45c(,)\ub85c \uad6c\ubd84\uc790\ub97c \ubcc0\uacbd\ud558\uace0 \uc2f6\uc744 \ub550 delimeter \uac12\uc744 \ub530\ub85c \uc804\ub2ec\ud558\uc5ec \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\uac1c\uc778\uc801\uc73c\ub85c 2\uac1c \uc815\ub3c4\uc758 \uac12\uc744 \ub9e4\uac1c\ubcc0\uc218\ub85c \uc804\ub2ec\ud558\ub294 \uacbd\uc6b0 CsvSource\ub97c \uc0ac\uc6a9\ud55c\ub2e4.\\n\\n```java\\n@ParameterizedTest\\n@CsvSource({\\"1,1\\", \\"2,4\\", \\"3,9\\", \\"4,16\\"})\\nvoid csvTest(final int number, final int result) {\\n assertThat(number * number).isEqualTo(result);\\n}\\n```\\n\\n### Method Source\\n\\n\ubcf5\uc7a1\ud55c \ud0c0\uc785\uc758 \uac12\uc744 \uc804\ub2ec\ud560 \ub54c \uc0ac\uc6a9\ud55c\ub2e4. \\n\uba54\uc11c\ub4dc\uba85\uc744 \uc785\ub825\ud558\uc5ec \ub9e4\uac1c\ubcc0\uc218\ub97c \uc81c\uacf5\ud558\ub294 \uba54\uc11c\ub4dc\ub97c \uc9c0\uc815\ud560 \uc218 \uc788\ub2e4. \\n\uba54\uc11c\ub4dc\uba85\uc744 \ub530\ub85c \uc785\ub825\ud558\uc9c0 \uc54a\uc73c\uba74 \ud14c\uc2a4\ud2b8\uba85\uacfc \ub3d9\uc77c\ud55c static \uba54\uc11c\ub4dc\uac00 \uc9c0\uc815\ub41c\ub2e4.\\n\\n```java\\n@ParameterizedTest\\n@MethodSource\\nvoid methodTest(final List numbers, final int count) {\\n assertThat(numbers).hasSize(count);\\n}\\n\\nprivate static Stream methodTest() {\\n return Stream.of(\\n Arguments.of(List.of(1), 1),\\n Arguments.of(List.of(1, 2), 2),\\n Arguments.of(List.of(1, 2, 3), 3)\\n );\\n}\\n```\\n\\n### ETC.\\n\\n\uc704\uc5d0\uc11c \uc5b8\uae09\ud55c \ubc29\ubc95 \uc774\uc678\uc5d0\ub3c4 \ub2e4\uc591\ud55c \ubc29\ubc95\uc73c\ub85c \ub9e4\uac1c\ubcc0\uc218\ub97c \uc81c\uacf5\ud560 \uc218 \uc788\ub2e4.\\n\\n- CSV \ud30c\uc77c\uc744 \uc774\uc6a9\ud55c CsvFileSource\\n- ArgumentsProvider \uad6c\ud604\ud55c \ud074\ub798\uc2a4\ub97c \uc774\uc6a9\ud558\ub294 ArgumentsSource\\n\\n## \ucc38\uace0 \uc790\ub8cc\\n\\n- [Guide to JUnit 5 Parameterized Tests](https://www.baeldung.com/parameterized-tests-junit-5)"},{"id":"intellij-settings","metadata":{"permalink":"/intellij-settings","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-30-IntelliJ \uc124\uc815.mdx/index.mdx","source":"@site/blog/2023-1/2023-01-30-IntelliJ \uc124\uc815.mdx/index.mdx","title":"IntelliJ \uc124\uc815","description":"Import \uc790\ub3d9 \uc801\uc6a9","date":"2023-01-30T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 30\uc77c","tags":[{"label":"IntelliJ","permalink":"/tags/intelli-j"}],"readingTime":0.465,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"IntelliJ \uc124\uc815","slug":"intellij-settings","tags":["IntelliJ"]},"prevItem":{"title":"Parameterized Tests","permalink":"/parameterized-tests"},"nextItem":{"title":"Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95","permalink":"/kotlin-null"}},"content":"### Import \uc790\ub3d9 \uc801\uc6a9\\n\\nPrefrences > Editor > General > Auto Import > Add unambiguous imports on the fly\\n\\n![auto-import](./auto-import.png)\\n\\n### \uc800\uc7a5\uc2dc \ub3d9\uc791\\n\\nPrefrences > Tools > Actions on Save\\n\\n![actions-on-save](./actions-on-save.png)\\n\\nReformat Code: Code Reformmating\\n\\nOptimize imports: \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 Import \uc81c\uac70\\n\\nRearrange: Code Style > Arrangement \uc124\uc815 \uae30\ubc18 \ucf54\ub4dc \uc7ac\uc815\ub82c\\n\\n### \uba54\uc18c\ub4dc \ucd94\ucd9c, \ubcc0\uc218 \ucd94\ucd9c\uc2dc final \uc801\uc6a9\\n\\nPrefrences > Editor > Code Style > Java > Code Generation > Final Modifier\\n\\n![final-modifier](./final-modifier.png)"},{"id":"kotlin-null","metadata":{"permalink":"/kotlin-null","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-16-Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95.mdx","source":"@site/blog/2023-1/2023-01-16-Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95.mdx","title":"Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95","description":"nullable \ud0c0\uc785","date":"2023-01-16T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 16\uc77c","tags":[{"label":"Kotlin","permalink":"/tags/kotlin"}],"readingTime":4.225,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95","slug":"kotlin-null","tags":["Kotlin"]},"prevItem":{"title":"IntelliJ \uc124\uc815","permalink":"/intellij-settings"},"nextItem":{"title":"JSR-310","permalink":"/jsr-310"}},"content":"import Tabs from \\"@theme/Tabs\\";\\nimport TabItem from \\"@theme/TabItem\\";\\n\\n### nullable \ud0c0\uc785\\n\\n\ucf54\ud2c0\ub9b0\uc740 `NullPointerException` \uc608\uc678\ub97c \ucd5c\ub300\ud55c \ubc1c\uc0dd\uc2dc\ud0a4\uc9c0 \uc54a\uae30 \uc704\ud574 \ud0c0\uc785 \uc2dc\uc2a4\ud15c\uc774 \uc124\uacc4\ub418\uc5b4 \uc788\ub2e4. \\n\uc774\ub294 \uc2e4\ud589 \uc2dc\uc810\uc774 \uc544\ub2cc \ucef4\ud30c\uc77c \uc2dc \ubbf8\ub9ac \uc624\ub958\uac00 \ubc1c\uc0dd\ud560 \uac00\ub2a5\uc131\uc774 \uc788\ub294 \ubd80\ubd84\uc744 \ubbf8\ub9ac \uac10\uc9c0\ud558\uc5ec NPE \ubc1c\uc0dd\uc758 \uac00\ub2a5\uc131\uc744 \uc904\uc5ec\uc900\ub2e4.\\n\\n\ucf54\ud2c0\ub9b0\uc758 \uacbd\uc6b0 nullable \ud0c0\uc785\uc744 \ub2e4\uc74c\uacfc \uac19\uc774 \ud45c\ud604\ud55c\ub2e4.\\n\\n```kotlin\\nval number: Int?\\n```\\n\\n\ud0c0\uc785 \ub4a4\uc5d0 `?`\ub97c \ubd99\uc5ec \ud574\ub2f9 \uac12\uc774 null\uc774 \ub420 \uc218 \uc788\ub2e4\ub294 \uac83\uc744 \uc758\ubbf8\ud55c\ub2e4. \\n\ub9cc\uc57d `?`\ub97c \ubd99\uc774\uc9c0 \uc54a\uc744 \ub54c null\uc744 \ubc1b\ub294 \uacbd\uc6b0 \ucef4\ud30c\uc77c \uc2dc \uc624\ub958\uac00 \ubc1c\uc0dd\ud55c\ub2e4.\\n\\n### `?.` Safe Calls \uc5f0\uc0b0\uc790\\n\\n\uc790\ubc14\uc5d0\uc11c NPE\ub97c \ubc1c\uc0dd\uc2dc\ud0a4\uc9c0 \uc54a\uae30 \uc704\ud574 null\uc744 \ucc98\ub9ac\ud558\ub294 \uac00\uc7a5 \uac04\ub2e8\ud55c \ubc29\ubc95\uc73c\ub85c\ub294 \ubd84\uae30\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc774 \uc788\ub2e4.\\n\\n\ucf54\ud2c0\ub9b0\uc740 \uc548\uc804\ud55c \ud638\ucd9c \uc5f0\uc0b0\uc790\uc778 `?.` \uc5f0\uc0b0\uc790\ub97c \uc9c0\uc6d0\ud55c\ub2e4. \\n\ub530\ub77c\uc11c \ucc38\uc870 \uac12\uc774 null\uc774 \uc544\ub2d0 \uacbd\uc6b0\uc5d0\ub9cc \uba54\uc11c\ub4dc \ud638\ucd9c\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\ucc38\uc870 \uac12\uc774 null\uc778 \uacbd\uc6b0 \uba54\uc11c\ub4dc \ud638\ucd9c\uc774 \ubb34\uc2dc\ub418\uace0, null\uc744 \ubc18\ud658\ud55c\ub2e4. \\n\\n\\n\\n\\n```java\\npublic String repeat(String word) {\\n if (word == null) {\\n return null;\\n }\\n return word.repeat(2);\\n}\\n```\\n\\n\\n\\n\\n```kotlin\\nfun repeat(word: String?): String? {\\n return word?.repeat(2)\\n}\\n```\\n\\n\\n\\n\\n### `?:` \uc5d8\ube44\uc2a4 \uc5f0\uc0b0\uc790\\n\\n\ucc38\uc870\ud558\ub824\ub294 \uac12\uc774 null\uc77c \uacbd\uc6b0 \uae30\ubcf8 \uac12\uc744 \ubc18\ud658\ud558\uace0 \uc2f6\uc744 \ub54c\ub294 \uc5b4\ub5bb\uac8c \ud574\uc57c \ud560\uae4c? \\n\ucf54\ud2c0\ub9b0\uc740 null\uc774 \uc544\ub2cc \uacbd\uc6b0 \uae30\ubcf8 \uac12\uc744 \uc9c0\uc815\ud560 \ub54c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub294 \uc5d8\ube44\uc2a4 \uc5f0\uc0b0\uc790\ub97c \uc9c0\uc6d0\ud55c\ub2e4.\\n\\n\\n\\n\\n```java\\npublic String stringSafe(String word) {\\n if (word == null) {\\n return \\"\\";\\n }\\n return word;\\n}\\n```\\n\\n\\n\\n\\n```kotlin\\nfun stringSafe(word: String?): String {\\n return word ?: \\"\\"\\n}\\n```\\n\\n\\n\\n\\n\ucf54\ud2c0\ub9b0\uc5d0\uc11c\ub294 throw\ub3c4 \uc2dd\uc774\uae30 \ub54c\ubb38\uc5d0 \uc5d8\ube44\uc2a4 \uc5f0\uc0b0\uc790\ub97c \uc774\uc6a9\ud558\uc5ec \uc608\uc678\ub97c \ub358\uc9c8 \uc218 \uc788\ub2e4. \\n\uc608\ub97c \ub4e4\uc5b4 \uc0ac\uc6a9\uc790 \uc815\ubcf4\uac00 \uc788\ub294 \uc800\uc7a5\uc18c\uc5d0 \ucc3e\ub294 \uc0ac\uc6a9\uc790\uac00 \uc5c6\ub294 \uacbd\uc6b0 \uc544\ub798\uc640 \uac19\uc774 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.\\n\\n```kotlin\\nuserRepository.findByName(name) ?: throw IllegalArgumentException()\\n```\\n\\n### `!!` \ub110 \uc544\ub2d8 \ub2e8\uc5b8 \uc5f0\uc0b0\uc790\\n\\n!! \uc5f0\uc0b0\uc790\ub97c \uc774\uc6a9\ud55c\ub2e4\uba74 \uac15\uc81c\ub85c \uc5b4\ub5a4 \uac12\uc774\ub4e0 non-nullable \ud0c0\uc785\uc73c\ub85c \ubcc0\uacbd\ud560 \uc218 \uc788\ub2e4. \\n\ud558\uc9c0\ub9cc null\uc778 \uac12\uc5d0 \uc0ac\uc6a9\ud55c\ub2e4\uba74 NPE\uac00 \ubc1c\uc0dd\ud558\uac8c \ub41c\ub2e4. \\n\uc77c\ubc18\uc801\uc778 \uacbd\uc6b0\uc5d0\ub294 !! \uc5f0\uc0b0\uc790\ub97c \uc0ac\uc6a9\ud558\ub294 \uac83\uc740 \uc704\ud5d8\ud558\ub2e4. \\n\uc0ac\uc6a9\ud558\uae30 \uc27d\uc9c0\ub9cc, \ub9ac\uc2a4\ud06c\uac00 \ud06c\uace0 \ud639\uc2dc\ub098 \ud574\ub2f9 \uac12\uc774 \ucd94\ud6c4\uc5d0\ub294 null\uc774 \ub420 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \uc9c0\uc591\ud574\uc57c \ub41c\ub2e4\uace0 \uc0dd\uac01\ud55c\ub2e4.\\n\\n```kotlin\\nval length: Int = word!!.length\\n```\\n\\n### `as?` \uc548\uc804\ud55c \uce90\uc2a4\ud305\\n\\n\ud0c0\uc785 \ubcc0\ud658\uc744 \ud560 \ub54c \uc9c0\uc815\ud55c \ud0c0\uc785\uc73c\ub85c \ubcc0\uacbd\ud560 \uc218 \uc5c6\ub2e4\uba74 `ClassCastException`\uc774 \ubc1c\uc0dd\ud55c\ub2e4. \\n\ucf54\ud2c0\ub9b0\uc5d0\uc11c\ub294 as \ub4a4\uc5d0 ?\ub97c \ubd99\uc5ec \uc548\uc804\ud558\uac8c \ud0c0\uc785 \ubcc0\ud658\uc744 \ud560 \uc218 \uc788\ub2e4. \\n\ub530\ub77c\uc11c \ubbf8\ub9ac \ubcc0\ud658 \uac00\ub2a5\ud55c \ud0c0\uc785\uc778\uc9c0 \ud655\uc778\ud558\uc9c0 \uc54a\uace0, \uc548\uc804\ud558\uac8c \ud0c0\uc785\uc744 \ubcc0\ud658 \ud560 \uc218 \uc788\ub2e4. \\n\\n\ud0c0\uc785 \ubcc0\ud658\uc774 \ubd88\uac00\ub2a5 \ud560 \uacbd\uc6b0 \uc608\uc678\ub97c \ubc1c\uc0dd\uc2dc\ud0a4\uc9c0 \uc54a\uace0 null\uc744 \ubc18\ud658\ud55c\ub2e4.\\n\\n```kotlin\\nval value: Int? = something as? Int\\n```\\n\\n### List\uc5d0\uc11c\uc758 null \ucc98\ub9ac\\n\\nList\uc5d0\ub294 null\uc774 \uc544\ub2cc \uac12\ub9cc \ubc18\ud658\ud558\ub294 `filterNotNull` \uc720\ud2f8\ub9ac\ud2f0 \uba54\uc11c\ub4dc\ub97c \uc81c\uacf5\ud55c\ub2e4.\\n\\n```kotlin\\nval foodsWithNull: List = listOf(\\"Pizza\\", \\"Cheese\\", null, \\"Potato\\")\\nval foods = foodsWithNull.filterNotNull()\\n```\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n- [Kotlin in Action](https://product.kyobobook.co.kr/detail/S000001804588)\\n- [Effective Kotlin Item 8](https://product.kyobobook.co.kr/detail/S000001033129)\\n- [Comprehensive Guide to Null Safety in Kotlin](https://www.baeldung.com/kotlin/null-safety)\\n- [Kotlin NullSafety](https://kotlinlang.org/docs/null-safety.html)"},{"id":"jsr-310","metadata":{"permalink":"/jsr-310","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-08-JSR-310.mdx","source":"@site/blog/2023-1/2023-01-08-JSR-310.mdx","title":"JSR-310","description":"\uc774\uc804\uc5d0 \ub9ce\uc740 \ubb38\uc81c\uac00 \uc788\ub358 \uc790\ubc14\uc758 \ud074\ub798\uc2a4(Calendar, Date)\ub97c \ub300\uccb4\ud558\ub294 \ub0a0\uc9dc\uc640 \uc2dc\uac04 API","date":"2023-01-08T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 8\uc77c","tags":[{"label":"Java","permalink":"/tags/java"},{"label":"Time","permalink":"/tags/time"}],"readingTime":1.685,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"JSR-310","slug":"jsr-310","tags":["Java","Time"]},"prevItem":{"title":"Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95","permalink":"/kotlin-null"},"nextItem":{"title":"[\ucc45] \uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574","permalink":"/the-essence-of-object-orientation"}},"content":"\uc774\uc804\uc5d0 \ub9ce\uc740 \ubb38\uc81c\uac00 \uc788\ub358 \uc790\ubc14\uc758 \ud074\ub798\uc2a4(Calendar, Date)\ub97c \ub300\uccb4\ud558\ub294 \ub0a0\uc9dc\uc640 \uc2dc\uac04 API \\nISO-8601\uc744 \uae30\ubc18\uc73c\ub85c \uc791\uc131 \\n\uc124\uacc4 \ubaa9\ud45c \u2192 \ubd88\ubcc0, Fluent API, \uba85\ud655\ud558\uace0 \uba85\uc2dc\uc801, \ud655\uc7a5 \uac00\ub2a5\uc131\\n\\n:::note ISO-8601\\n\\n\ub0a0\uc9dc\uc640 \uc2dc\uac04\uc5d0 \uad00\ub828\ub41c \ub370\uc774\ud130\ub97c \ub2e4\ub8e8\ub294 \uad6d\uc81c \ud45c\uc900\\n\\n:::\\n\\n### LocalDate, LocalTime, LocalDateTime\\n\\n\ub0a0\uc9dc\uc640 \uc2dc\uac04\uc744 \ud45c\ud604\ud558\ub294 \ud074\ub798\uc2a4\\n\\n### Instant\\n\\n\uc720\ub2c9\uc2a4 \uc2dc\uac04(1970-01-01, 00:00:00 UTC) \uae30\uc900\uc73c\ub85c \ud2b9\uc815 \uc9c0\uc810\uae4c\uc9c0\uc758 \uc2dc\uac04\uc744 \ucd08\ub85c \ud45c\ud604\ud558\ub294 \ud074\ub798\uc2a4 \\n\uae30\uacc4\uc758 \uad00\uc810\uc5d0\uc11c \uc2dc\uac04 \ud45c\ud604\\n\\n### Duration, Period\\n\\n\uac04\uaca9\uc744 \ud45c\ud604\ud558\ub294 \ud074\ub798\uc2a4\\n\\n### TemporalAdjusters\\n\\n\ubcf5\uc7a1\ud55c \ub0a0\uc9dc \uc870\uc815\uc774 \ud544\uc694\ud560 \ub54c \uc0ac\uc6a9 \\n\ud544\uc694\ud55c \uacbd\uc6b0 \ub2e4\uc74c \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud558\uc5ec \ucee4\uc2a4\ud140 TemporalAdjuster\ub97c \uad6c\ud604 \uac00\ub2a5\\n\\n```java\\n@FunctionalInterface\\npublic interface TemporalAdjuster {\\n Temporal adjustInto(Temporal temporal);\\n}\\n```\\n\\n### DateTimeFormatter\\n\\n\ub0a0\uc9dc\uc640 \uc2dc\uac04 \ud3ec\ub9f7 \ud074\ub798\uc2a4 \\n\ud2b9\uc815 \ub0a0\uc9dc \ud328\ud134\uc774\ub098, DateTimeFormatterBuilder\ub97c \uc774\uc6a9\ud574\uc11c \ucee4\uc2a4\ud140\ud55c \ud3ec\ub9f7\uc744 \uc0dd\uc131 \uac00\ub2a5\\n\\n### ZoneId, ZoneOffset\\n\\nZoneId\ub294 \uc9c0\uc5ed ID\ub294 `\u2018\uc9c0\uc5ed/\ub3c4\uc2dc\u2019` \ud615\uc2dd, ZoneOffset\uc740 \uc2dc\ucc28 UTC \uae30\uc900 \uace0\uc815\ub41c \uc2dc\uac04 \ucc28\uc774 \uc774\uc6a9 \\nZoneId\uc758 \uacbd\uc6b0 IANA Time Zone Database\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uc9c0\uc5ed \uc9d1\ud569 \uc815\ubcf4 \uc0ac\uc6a9\\n\\n```java\\nInstant instant = Instant.now();\\nLocalDateTime utc = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);\\n```\\n\\n### \ucc38\uace0 \uc790\ub8cc\\n\\n- [\ubaa8\ub358 \uc790\ubc14 \uc778 \uc561\uc158](https://product.kyobobook.co.kr/detail/S000001810171)\\n- [Java\uc758 \ub0a0\uc9dc\uc640 \uc2dc\uac04 API](https://d2.naver.com/helloworld/645609)\\n- [ISO-8601](https://www.w3.org/TR/NOTE-datetime)\\n- [JSR-310 Spec](https://download.oracle.com/otn-pub/jcp/date_time-0.2-edr-oth-JSpec/date_time-0_2-edr-spec.pdf?AuthParam=1673171124_74a718be92efe4911c6977c02965aff4)\\n- [Temporal Adjuster](https://www.baeldung.com/java-temporal-adjuster)\\n- [DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)"},{"id":"the-essence-of-object-orientation","metadata":{"permalink":"/the-essence-of-object-orientation","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-07-\uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574.mdx","source":"@site/blog/2023-1/2023-01-07-\uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574.mdx","title":"[\ucc45] \uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574","description":"\ucc45 \uc815\ubcf4","date":"2023-01-07T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 7\uc77c","tags":[{"label":"Book","permalink":"/tags/book"}],"readingTime":5.415,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ucc45] \uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574","slug":"the-essence-of-object-orientation","tags":["Book"]},"prevItem":{"title":"JSR-310","permalink":"/jsr-310"},"nextItem":{"title":"2022\ub144 \ud68c\uace0","permalink":"/2022-retrospective"}},"content":"### \ucc45 \uc815\ubcf4\\n\\n> \uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574 \\n> \uc870\uc601\ud638\\n> \\n\\n### \uc77d\uace0 \ub098\uc11c\\n\\n\uc870\uc601\ud638\ub2d8\uc758 \uc624\ube0c\uc81d\ud2b8\ub97c \uc77d\uace0 \ub098\uc11c \ub2e4\uc2dc \ud55c \ubc88 \uc77d\uc5b4\ubcf4\uc558\ub2e4. \\n\uc544\uc9c1 \uc774\ud574\uac00 \uc548\ub418\ub294 \ubd80\ubd84\uc774 \ub9ce\uc9c0\ub9cc, \uadf8\ub798\ub3c4 \ud56d\uc0c1 \uc0c8\ub85c\uc6c0\uc744 \ub290\ub080\ub2e4. \\n\ub354\ud560 \ub098\uc704 \uc5c6\uc774 \ud73c\ub96d\ud55c \uac1d\uccb4\uc9c0\ud5a5 \ucc45\uc774\uace0, \uc870\uae08 \ub354 \uacf5\ubd80\ud558\uace0 \ub2e4\uc2dc \uc77d\uc5b4\ubd10\uc57c\ub420 \uac83 \uac19\ub2e4. \\n\\n\ucee4\ud53c \uc804\ubb38\uc810, \uc9c0\ud558\ucca0 \ub178\uc120\ub3c4, \uc774\uc0c1\ud55c \ub098\ub77c\uc758 \uc5d8\ub9ac\uc2a4\ub97c \uc608\uc2dc\ub85c \ub4e0 \uc124\uba85\uc774 \ub108\ubb34 \uc88b\uc558\uace0 \\n\uc88b\uc740 \ub0b4\uc6a9\uc744 \ub2f4\uace0 \uc788\uc9c0\ub9cc \uadf8\ub807\ub2e4\uace0 \ub108\ubb34 \ubb34\uac81\uc9c0 \uc54a\uc544 \uac00\ubccd\uac8c \uc77d\uae30\ub3c4 \uc88b\uc740 \uac83 \uac19\ub2e4.\\n\\n### \ucc45\uc784\uc758 \uc790\uc728\uc131\uc744 \uac15\uc870\ud558\ub294 \uc774\uc720 p.173\\n\\n\ud611\ub825\uc744 \ub2e8\uc21c\ud558\uac8c \ub9cc\ub4e0\ub2e4.\\n\\n- \uc758\ub3c4\ub97c \uba85\ud655\ud558\uac8c \ud45c\ud604 \u2192 \ud611\ub825\uc758 \ubcf5\uc7a1\ud568 \uc800\ud558\\n- \ucc45\uc784\uc758 \ucd94\uc0c1\ud654\\n\\n\uc678\ubd80\uc640 \ub0b4\ubd80\ub97c \uba85\ud655\ud558\uac8c \ubd84\ub9ac\ud55c\ub2e4.\\n\\n- \uc694\uccad\ud558\ub294 \uac1d\uccb4\uac00 \ubab0\ub77c\ub3c4 \ub418\ub294 \ubd80\ubd84\uc774 \ucea1\uc290\ud654\ub428\uc73c\ub85c \uc778\ud130\ud398\uc774\uc2a4\uc640 \uad6c\ud604\uc758 \ubd84\ub9ac\\n\\n\ucc45\uc784\uc744 \uc218\ud589\ud558\ub294 \ub0b4\ubd80\uc801\uc778 \ubc29\ubc95\uc744 \ubcc0\uacbd\ud558\ub354\ub77c\ub3c4 \uc678\ubd80\uc5d0 \uc601\ud5a5\uc744 \ubbf8\uce58\uc9c0 \uc54a\ub294\ub2e4.\\n\\n- \ubcc0\uacbd\uc758 \ud30c\uae09\ud6a8\uacfc\ub97c \uac1d\uccb4 \ub0b4\ubd80\ub85c \ucea1\uc290\ud654 \u2192 \uba54\uc2dc\uc9c0\ub97c \ubcf4\ub0b4\ub294 \uac1d\uccb4\uc640\uc758 \uacb0\ud569\ub3c4 \uc800\ud558\\n\\n\ud611\ub825\uc758 \ub300\uc0c1\uc744 \ub2e4\uc591\ud558\uac8c \uc120\ud0dd\ud560 \uc218 \uc788\ub294 \uc720\uc5f0\uc131\uc744 \uc81c\uacf5\ud55c\ub2e4.\\n\\n- \uc720\uc5f0\ud55c \uc124\uacc4 \u2192 \uc7ac\uc0ac\uc6a9\uc131 \uc99d\uac00\\n\\n\uac1d\uccb4\uc758 \uc5ed\ud560\uc744 \uc774\ud574\ud558\uae30 \uc26c\uc6cc\uc9c4\ub2e4.\\n\\n- \uc751\uc9d1\ub3c4\ub97c \ub192\uc740 \uc0c1\ud0dc\ub85c \uc720\uc9c0\\n\\n### \ubc11\uc904 \uce5c \ubb38\uc7a5\ub4e4\\n\\n> \uac1d\uccb4\uc9c0\ud5a5\uc758 \ubaa9\ud45c\ub294 \uc2e4\uc138\uacc4\ub97c \ubaa8\ubc29\ud558\ub294 \uac83\uc774 \uc544\ub2c8\ub2e4.\\n\uc624\ud788\ub824 \uc0c8\ub85c\uc6b4 \uc138\uacc4\ub97c \ucc3d\uc870\ud558\ub294 \uac83\uc774\ub2e4.\\n\uc18c\ud504\ud2b8\uc6e8\uc5b4 \uac1c\ubc1c\uc790\uc758 \uc5ed\ud560\uc740 \ub2e8\uc21c\ud788 \uc2e4\uc138\uacc4\ub97c \uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc548\uc73c\ub85c \uc62e\uaca8 \ub2f4\ub294 \uac83\uc774 \uc544\ub2c8\ub77c \uace0\uac1d\uacfc \uc0ac\uc6a9\uc790\ub97c \ub9cc\uc871\uc2dc\ud0ac \uc218 \uc788\ub294 \uc2e0\uc138\uacc4\ub97c \ucc3d\uc870\ud558\ub294 \uac83\uc774\ub2e4.\\np.21\\n> \\n\\n> \uacfc\uac70\uc758 \uc804\ud1b5\uc801\uc778 \uac1c\ubc1c \ubc29\ubc95\uc740 \ub370\uc774\ud130\uc640 \ud504\ub85c\uc138\uc2a4\ub97c \uc5c4\uaca9\ud558\uac8c \uad6c\ubd84\ud55c\ub2e4.\\n\uc774\uc5d0 \ubc18\ud574 \uac1d\uccb4\uc9c0\ud5a5\uc5d0\uc11c\ub294 \ub370\uc774\ud130\uc640 \ud504\ub85c\uc138\uc2a4\ub97c \uac1d\uccb4\ub77c\ub294 \ud558\ub098\uc758 \ud2c0 \uc548\uc5d0 \ud568\uaed8 \ubb36\uc5b4 \ub193\uc74c\uc73c\ub85c\uc368 \uac1d\uccb4\uc758 \uc790\uc728\uc131\uc744 \ubcf4\uc7a5\ud55c\ub2e4.\\n\uc790\uc728\uc801\uc778 \uac1d\uccb4\ub85c \uad6c\uc131\ub41c \uacf5\ub3d9\uccb4\ub294 \uc720\uc9c0 \ubcf4\uc218\uac00 \uc27d\uace0 \uc7ac\uc0ac\uc6a9\uc774 \uc6a9\uc774\ud55c \uc2dc\uc2a4\ud15c\uc744 \uad6c\ucd95\ud560 \uc218 \uc788\ub294 \uac00\ub2a5\uc131\uc744 \uc81c\uc2dc\ud55c\ub2e4.\\np.33\\n> \\n\\n> **\uac1d\uccb4\uc9c0\ud5a5\uc758 \ubcf8\uc9c8**\\n> \\n> \\n> \uc2dc\uc2a4\ud15c\uc744 \uc0c1\ud638\uc791\uc6a9\ud558\ub294 \uc790\uc728\uc801\uc778 \uac1d\uccb4\ub4e4\uc758 \uacf5\ub3d9\uccb4\ub85c \ubc14\ub77c\ubcf4\uace0 \uac1d\uccb4\ub97c \uc774\uc6a9\ud574 \uc2dc\uc2a4\ud15c\uc744 \ubd84\ud560\ud558\ub294 \ubc29\ubc95\\n> \\n> \uc790\uc728\uc801\uc778 \uac1d\uccb4\ub780 \uc0c1\ud0dc\uc640 \ud589\uc704\ub97c \ud568\uaed8 \uc9c0\ub2c8\uba70 \uc2a4\uc2a4\ub85c \uc790\uae30 \uc790\uc2e0\uc744 \ucc45\uc784\uc9c0\ub294 \uac1d\uccb4\ub97c \uc758\ubbf8\ud55c\ub2e4.\\n> \\n> \uac1d\uccb4\ub294 \uc2dc\uc2a4\ud15c\uc758 \ud589\uc704\ub97c \uad6c\ud604\ud558\uae30 \uc704\ud574 \ub2e4\ub978 \uac1d\uccb4\uc640 \ud611\ub825\ud55c\ub2e4. \uac01 \uac1d\uccb4\ub294 \ud611\ub825 \ub0b4\uc5d0\uc11c \uc815\ud574\uc9c4 \uc5ed\ud560\uc744 \uc218\ud589\ud558\uba70 \uc5ed\ud560\uc740 \uad00\ub828\ub41c \ucc45\uc784\uc758 \uc9d1\ud569\uc774\ub2e4.\\n> \\n> \uac1d\uccb4\ub294 \ub2e4\ub978 \uac1d\uccb4\uc640 \ud611\ub825\ud558\uae30 \uc704\ud574 \uba54\uc2dc\uc9c0\ub97c \uc804\uc1a1\ud558\uace0, \uba54\uc2dc\uc9c0\ub97c \uc218\uc2e0\ud55c \uac1d\uccb4\ub294 \uba54\uc2dc\uc9c0\ub97c \ucc98\ub9ac\ud558\ub294 \ub370 \uc801\ud569\ud55c \uba54\uc11c\ub4dc\ub97c \uc790\uc728\uc801\uc73c\ub85c \uc120\ud0dd\ud55c\ub2e4.\\n> p.35\\n> \\n\\n> \ud074\ub798\uc2a4\uc758 \uad6c\uc870\uc640 \uba54\uc11c\ub4dc\uac00 \uc544\ub2c8\ub77c \uac1d\uccb4\uc758 \uc5ed\ud560, \ucc45\uc784, \ud611\ub825\uc5d0 \uc9d1\uc911\ud558\ub77c.\\n\uac1d\uccb4\uc9c0\ud5a5\uc740 \uac1d\uccb4\ub97c \uc9c0\ud5a5\ud558\ub294 \uac83\uc774\uc9c0 \ud074\ub798\uc2a4\ub97c \uc9c0\ud5a5\ud558\ub294 \uac83\uc774 \uc544\ub2c8\ub2e4.\\np.38\\n> \\n\\n> \uac1d\uccb4\uc9c0\ud5a5\uc5d0\uc11c \uc911\uc694\ud55c \uac83\uc740 \ub3d9\uc801\uc73c\ub85c \ubcc0\ud558\ub294 \uac1d\uccb4\uc758 \u2018\uc0c1\ud0dc\u2019\uc640 \uc0c1\ud0dc\ub97c \ubcc0\uacbd\ud558\ub294 \u2018\ud589\uc704\u2019\ub2e4.\\n\ud074\ub798\uc2a4\ub294 \ud0c0\uc785\uc744 \uad6c\ud604\ud558\uae30 \uc704\ud574 \ud504\ub85c\uadf8\ub798\ubc0d \uc5b8\uc5b4\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uad6c\ud604 \uba54\ucee4\ub2c8\uc998\uc774\ub77c\ub294 \uc0ac\uc2e4\uc744 \uae30\uc5b5\ud558\ub77c.\\np.105\\n> \\n\\n> \ucc45\uc784 \uc8fc\ub3c4 \uc124\uacc4\uc758 \ud575\uc2ec\uc740 \uc5b4\ub5a4 \ud589\uc704\uac00 \ud544\uc694\ud55c\uc9c0\ub97c \uba3c\uc800 \uacb0\uc815\ud55c \ud6c4\uc5d0 \uc774 \ud589\uc704\ub97c \uc218\ud589\ud560 \uac1d\uccb4\ub97c \uacb0\uc815\ud558\ub294 \uac83\uc774\ub2e4.\\n\uc774 \uacfc\uc815\uc744 \ud754\ud788 What/Who \uc0ac\uc774\ud074\uc774\ub77c\uace0 \ud55c\ub2e4.\\n\u2019\uc5b4\ub5a4 \ud589\uc704(What)\u2019\ub97c \uc218\ud589\ud560 \uac83\uc778\uc9c0 \uacb0\uc815\ud55c \ud6c4 \u2018\ub204\uac00(who)\u2019 \uadf8 \ud589\uc704\ub97c \uc218\ud589\ud560 \uac83\uc778\uc9c0 \uacb0\uc815\ud574\uc57c \ud55c\ub2e4.\\n\uc5ec\uae30\uc11c \u2018\uc5b4\ub5a4 \ud589\uc704\u2019\uac00 \ubc14\ub85c \uba54\uc2dc\uc9c0\ub2e4.\\np.158\\n>"},{"id":"2022-retrospective","metadata":{"permalink":"/2022-retrospective","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-02-2022\ub144 \ud68c\uace0.mdx","source":"@site/blog/2023-1/2023-01-02-2022\ub144 \ud68c\uace0.mdx","title":"2022\ub144 \ud68c\uace0","description":"\uc801\ub2f9\ud55c \uc804\ud658\uc810, 2022\ub144\uc744 \ub3cc\uc544\ubcf4\uba70","date":"2023-01-02T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 2\uc77c","tags":[{"label":"Retrospective","permalink":"/tags/retrospective"}],"readingTime":3.705,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"2022\ub144 \ud68c\uace0","slug":"2022-retrospective","tags":["Retrospective"]},"prevItem":{"title":"[\ucc45] \uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574","permalink":"/the-essence-of-object-orientation"},"nextItem":{"title":"[\ucc45] \uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.","permalink":"/book-writer"}},"content":"\uc801\ub2f9\ud55c \uc804\ud658\uc810, 2022\ub144\uc744 \ub3cc\uc544\ubcf4\uba70 \\n\\n### \uc804\uc5ed\\n\\n\uc57d 1\ub144 6\uac1c\uc6d4\uac04\uc758 \uacf5\uad70 \uc815\ubcf4\ubcf4\ud638\ubcd1 \uc0dd\ud65c\uc744 \ub9c8\uce58\uace0 \uc804\uc5ed\uc744 \ud588\ub2e4. \\n\uc870\uae30 \uc804\uc5ed \ub54c\ubb38\uc5d0 2021\ub144 12\uc6d4\uc5d0 \ub098\uc654\uc9c0\ub9cc, \uc2e4\uc81c \uc804\uc5ed \ub0a0\uc9dc\ub294 2022\ub144\uc774\ub2c8 \ud68c\uace0\uc5d0 \uc801\uc5b4\ub3c4 \uc0c1\uad00\uc5c6\uaca0\uc9c0. \\n\\n\uc870\uae08 \ub354 \ubbf8\ub798\uc5d0 \ub300\ud55c \uc0dd\uac01\uc744 \ud574\ubcfc\uac78 \uadf8\ub7ac\ub2e4. \\n\uc804\uc5ed\uc744 \ud588\uc9c0\ub9cc \ubb50 \ud558\ub098 \uc81c\ub300\ub85c \ud560 \uc904 \uc544\ub294 \uac83\ub3c4 \uc5c6\uc73c\ub2c8 \ub113\uc740 \ubc14\ub2f7\uc18d\uc5d0 \ub369\uadf8\ub7ec\ub2c8 \ub193\uc544\uc9c4 \uae30\ubd84\uc774 \uad1c\ud788 \ub4e4\uc5c8\uc5c8\ub2e4. \\n\uc77c\ucc0d \uc0dd\uac01\uc744 \uc815\ub9ac\ud558\uc5ec \ubc29\ud5a5\uc744 \uc7a1\uc9c0 \ubabb\ud588\uae30\uc5d0 \uc544\uc26c\uc6c0\uc774 \ub9ce\uc774 \ub0a8\uc558\ub2e4. \\n\\n### \uc790\ubc14\\n\\n\uc804\uc5ed\uc744 \ud558\uace0 \uc9c4\ub85c\ub97c \uace0\ubbfc\ud558\ub2e4 \ud5a5\ub85c\ub2d8\uc758 [\uc790\ubc14 \uacf5\ud654\uad6d](https://jojoldu.tistory.com/609) \ud3ec\uc2a4\ud305\uc744 \uc77d\uace0 \ub098\uc11c \uc790\ubc14 \uacf5\ubd80\ub97c \uc2dc\uc791\ud588\ub2e4. \\n\uc720\uba85\ud55c \uc778\ud504\ub7f0\uc758 \uae40\uc601\ud55c\ub2d8\uc758 \uc2a4\ud504\ub9c1 \uac15\uc758\ub3c4 \uc788\uace0, \uc88b\uc740 \uc790\ubc14 \uac1c\ubc1c \uc11c\uc801\uc774 \ub9ce\uc544\uc11c \ub3c5\ud559\ud558\uae30\ub85c \uacb0\uc815\ud588\ub2e4. \\n\ud558\ub2e4 \ubcf4\ub2c8 \uc790\ubc14\uc640 \uc2a4\ud504\ub9c1\uc744 \uacf5\ubd80\ud558\uba74\uc11c \u201c\uc65c \uc9c4\uc791\ud558\uc9c0 \uc54a\uc558\uc9c0\u201d\ub77c\ub294 \uc0dd\uac01\ub3c4 \ub9ce\uc774 \ub4e4\uc5c8\ub2e4. \\n\uc591\uc9c8\uc758 \uc790\ub8cc\ub3c4 \ub9ce\uc558\uae30 \ub54c\ubb38\uc5d0, \uc608\uc804\uc5d0 \ub178\ub4dc\ub85c \uac1c\ubc1c\ud588\uc744 \ub54c \ud480\uc9c0 \ubabb\ud588\ub358 \ub2f5\ub2f5\ud568\uc744 \ub9ce\uc774 \ud574\uc18c\ud588\ub358 \uac83 \uac19\ub2e4.\\n\\n23\ub144\uc5d0\ub294 \uc870\uae08 \ub354 \uae4a\uac8c \uc790\ubc14\ub97c \uacf5\ubd80\ud574\ubcfc \uc0dd\uac01\uc774\ub2e4. \\n\uc5b8\uc5b4\ub97c \ud558\ub098 \uae4a\uac8c \uacf5\ubd80\ud558\ub294 \uac74 \ub9ce\uc740 \ub3c4\uc6c0\uc774 \ub418\ub294 \uac83 \uac19\ub2e4.\\n\\n### \uc2a4\ud130\ub514\\n\\n\uae40\uc601\ud55c\ub2d8\uc758 \uac15\uc758\ub97c \uac70\uc758 \ub2e4 \ub4e4\uc5c8\uc744 \ub54c\ucbe4, \ud56d\uc0c1 \uac15\uc758\uc5d0\uc11c \uc5b8\uae09\ub418\ub294 \ud1a0\ube44\uc758 \uc2a4\ud504\ub9c1\uc744 \uc77d\uc5b4\ubcf4\uace0 \uc2f6\uc5b4\uc84c\uace0 \\n\ud63c\uc790 \uacf5\ubd80\ud558\uae30\uc5d0\ub294 \ub3d9\uae30\ubd80\uc5ec\ub3c4 \ubd80\uc871\ud588\uae30 \ub54c\ubb38\uc5d0 \uc2a4\ud130\ub514\ub97c \uc2dc\uc791\ud588\ub2e4. \\n\ub2e4\ub978 \uc0ac\ub78c\uc5d0\uac8c \uc124\uba85\uc744 \ud574\uc57c \ud588\uae30 \ub54c\ubb38\uc5d0 \ub354\uc6b1 \uaf3c\uaf3c\ud558\uac8c \uacf5\ubd80\ub97c \ud560 \uc218 \uc788\uc5b4\uc11c \uc88b\uc558\uc9c0\ub9cc \ub098\uc5d0\uac8c\ub294 \ub0b4\uc6a9\uc774 \uaf64\ub098 \uc5b4\ub824\uc6cc\uc11c \uc2dc\uac04\uc744 \ub9ce\uc774 \uc18c\ube44\ud588\ub2e4. \\n\uac19\uc774 \uc2a4\ud130\ub514\ud558\uc2dc\ub294 \ubd84\uacfc 7\uac1c\uc6d4 \ub3d9\uc548 \uc2a4\ud130\ub514\ub97c \uafb8\uc900\ud788 \uc774\uc5b4\ub098\uac00 \ucd1d 3\uad8c\uc758 \ucc45\uc744 \uc77d\uc744 \uc218 \uc788\uc5c8\ub2e4.\\n\\n### \uc6b0\uc544\ud55c \ud14c\ud06c\ucf54\uc2a4\\n\\n\uad70 \ubcf5\ubb34 \uc911\uc77c \ub54c \uc9c0\uc6d0\ud588\ub2e4 \ub5a8\uc5b4\uc9c4 \uc6b0\uc544\ud55c \ud14c\ud06c\ucf54\uc2a4\ub97c \ub2e4\uc2dc \uc9c0\uc6d0\ud588\ub2e4. \\n\uc774\ubc88 \uc5f0\ub3c4\uc5d0 \ucde8\uc5c5\uc744 \ud558\ub294 \uac8c \ubaa9\ud45c\uc600\uc9c0\ub9cc \ub0b4\uac00 \uac00\uc9c0\uace0 \uc788\ub294 \ud2b9\ubcc4\ud55c \ubb34\uae30\uac00 \uc5c6\ub2e4\ub294 \uac78 \uae68\ub2ec\uc558\ub2e4. \\n\uc801\uc9c0 \uc54a\uc740 \uc2dc\uac04\uc744 \ud22c\uc790\ud574 \uc900\ube44\ub97c \ud588\uace0, \uac10\uc0ac\ud558\uac8c\ub3c4 \uc774\ubc88\uc5d0\ub294 \ucd5c\uc885 \ud569\uaca9\uc744 \ud588\ub2e4. \\n\\n\ub09c \uc0ac\ub78c\ub4e4\uacfc \uc18c\ud1b5\ud558\uace0, \ud611\uc5c5\ud558\ub294 \ub2a5\ub825\uc774 \ubd80\uc871\ud558\ub2e4\uace0 \uc0dd\uac01\uc744 \ub9ce\uc774 \ud588\ub2e4. \\n\uc6b0\uc544\ud55c \ud14c\ud06c\ucf54\uc2a4\ub97c \ud1b5\ud574 \uadf8 \ube48 \ubd80\ubd84\uc744 \ucc44\uc6b0\ub3c4\ub85d \ub178\ub825\ud574\uc57c\uaca0\ub2e4. \\n\\n### 2023\ub144\uc5d0\ub294\\n\\n\ub9c8\uc74c\uc758 \uc5ec\uc720\uac00 \uc5c6\uc5c8\ub358 2022\ub144\uc774\uc5c8\ub358 \uac83 \uac19\ub2e4. \\n\ud558\uace0 \uc2f6\uc740 \uac74 \ub9ce\uc9c0\ub9cc, \uc774\ubc88\uc5d0\ub294 \uc5ec\uc720\ub97c \uac00\uc9c0\uace0 \ud560 \uc218 \uc788\ub294 \uac83\uc5d0 \ucd5c\uc120\uc744 \ub2e4\ud574\uc57c\uaca0\ub2e4."},{"id":"book-writer","metadata":{"permalink":"/book-writer","editUrl":"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-1/2023-01-01-\uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.mdx","source":"@site/blog/2023-1/2023-01-01-\uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.mdx","title":"[\ucc45] \uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.","description":"\ucc45 \uc815\ubcf4","date":"2023-01-01T00:00:00.000Z","formattedDate":"2023\ub144 1\uc6d4 1\uc77c","tags":[{"label":"Book","permalink":"/tags/book"}],"readingTime":4.425,"hasTruncateMarker":false,"authors":[],"frontMatter":{"title":"[\ucc45] \uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.","slug":"book-writer","tags":["Book"]},"prevItem":{"title":"2022\ub144 \ud68c\uace0","permalink":"/2022-retrospective"}},"content":"### \ucc45 \uc815\ubcf4\\n\\n> \uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \\n> \ubc15\uc194\ubbf8 \\n>\\n\\n### \uc77d\uace0 \ub098\uc11c\\n\\n\uc800\uc790\uc758 \uacbd\ud5d8\uacfc \ud568\uaed8 \uae00\uc4f0\uae30\uc5d0 \ub300\ud55c \uac00\ubcbc\uc6b4 \uc870\uc5b8\uc774 \ub2f4\uaca8\uc788\uc5b4 \uac00\ubccd\uac8c \uc77d\uae30 \uc88b\uc558\ub2e4. \\n\uae00\uc744 \uc798 \uc791\uc131\ud574 \ubcf4\uace0 \uc2f6\uc744 \ub54c \uc801\uc6a9\ud574 \ubcfc \uc218 \uc788\ub294 \uc815\ubcf4\uac00 \ub9ce\uc544\uc11c \ub3c4\uc6c0\uc774 \ub418\uc5c8\ub2e4. \\n\\n\uc6b0\uc544\ud55c \ud14c\ud06c\ucf54\uc2a4\uc758 \ud504\ub9ac\ucf54\uc2a4\ub97c \uc9c4\ud589\ud560 \ub54c \ud6c4\uae30\ub97c \uc791\uc131\ud558\uace0 \ub098\uba74 \ud56d\uc0c1 \uae00\uc774 \ub531\ub531\ud558\ub2e4\ub294 \ub290\ub08c\uc744 \ubc1b\uc558\ub2e4. \\n\ub2e4\ub978 \uc9c0\uc6d0\uc790\ub4e4\uc758 \uc77d\uae30 \ud3b8\ud558\uace0, \ubc1d\uc740 \ub290\ub08c\uc744 \uc8fc\ub294 \uae00\uc744 \ubcf4\uba74 \ubd80\ub7ec\uc6b4 \ub9c8\uc74c\uc744 \uac00\uc9c0\uae30\ub3c4 \ud588\ub2e4. \\n\uc774 \ucc45\uc744 \uc77d\uc5c8\uc73c\ub2c8 2023\ub144\uc5d0\ub294 \uc870\uae08 \ub354 \uae00\uc744 \uc798 \uc801\uc5b4\ubcf4\ub824\uace0 \ud55c\ub2e4.\\n\\n### \ubc11\uc904 \uce5c \ubb38\uc7a5\ub4e4\\n\\n> \ubb38\uc7a5\uc774 \uc2ec\uc2ec\ud558\uace0 \uc9c0\ub8e8\ud558\ub2e4\uba74\\n\ub0b4\uc6a9\uc744 \uc77c\ubaa9\uc694\uc5f0\ud558\uac8c \uc815\ub9ac\ud588\uace0, \uae00\uc758 \uc758\ub3c4\ub3c4 \uc090\ub6a4\uc9c0 \uc54a\uace0, \ub2e8\uc5b4\ub3c4 \uc801\uc808\ud55c \uac83\uc73c\ub85c \uace8\ub790\ub294\ub370\u2026 \uadf8\ub7f0\ub370\ub3c4 \uc5b4\ub518\uac00\uac00 \uc2ec\uc2ec\ud558\uace0 \uc9c0\ub8e8\ud558\ub2e4\uba74? \ucd95\ucd95 \ucc98\uc9c0\uace0 \ub530\ubd84\ud558\ub2e4\uba74? \ub9d0\uaf2c\ub9ac\ub97c \ubaa8\uc870\ub9ac \u2018~\ub2e4\u2019\ub85c \ud1b5\uc77c\ud55c \uac74 \uc544\ub2cc\uc9c0 \uc810\uac80\ud574 \ubcf4\uc138\uc694.\\n> \\n\\n> \ub9d0\uaf2c\ub9ac\ub97c \uc798 \uac16\uace0 \ub180\uc544\uc57c \ud569\ub2c8\ub2e4. \ubb38\uc7a5\uc758 \ub9c8\uc9c0\ub9c9 \uae00\uc790\ub97c \ub9e4\ubc88 \ub2e4\ub974\uac8c \uace0\uccd0\uc4f0\ub294 \uac83\ub9cc\uc73c\ub85c\ub3c4 \uae00\uc5d0 \ud65c\uae30\ub97c \ub354\ud560 \uc218 \uc788\uc8e0. \ub54c\ub860 \ubb38\uc7a5\uc744 \ub2e4 \ub9c8\uce58\uc9c0 \uc54a\uace0, \ub2e8\uc5b4\ub85c\ub9cc \ub05d\ub9fa\ub294 \uac83\ub3c4 \ubc29\ubc95. \ubb38\uc7a5\uacfc \ubb38\uc7a5 \uc0ac\uc774\uc5d0 \uc27c\ud45c\uac00 \ub4e4\uc5b4\uc11c\uba70 \uae00 \uc804\uccb4\uc5d0 \ud65c\uae30\uac00 \ub3cc\uac8c \ub3fc\uc694. \ubb38\uc7a5\uc758 \uae38\uc774\ub3c4 \ub2e4\ucc44\ub85c\uc6cc\uc9c0\ub294 \ub355\ubd84\uc5d0 \ub364\uc73c\ub85c \uc5bb\uac8c \ub418\ub294 \uac83\ub3c4 \uc788\uc2b5\ub2c8\ub2e4. \ubc14\ub85c, \uae00\uc758 \ub9ac\ub4ec.\\n> \\n\\n> \uc774\uc804 \ubb38\uc7a5\uc5d0\uc11c \ub05d\ub09c \uae00\uc790\ub85c, \ub2e4\uc74c \ubb38\uc7a5\uc744 \ub05d\ub9fa\uc9c0 \uc54a\uae30. \ud55c\ub450 \ubb38\ub2e8\ub9c8\ub2e4 \ub2e8\uc5b4 \uc218\uc900\uc758 \uc544\uc8fc \uc9e7\uc740 \ubb38\uc7a5 \ubc30\uce58\ud558\uae30.\\n> \\n\\n> \uae00\uc758 \uc9c4\uc9dc \uc774\uc720, \uae00\uc758 \uc9c4\uc9dc \ubaa9\uc801, \uae00\uc758 \uc9c4\uc9dc \ub300\uc0c1\uc744 \ucc3e\uc73c\ub824\uace0 \uc560\uc37c\uc2b5\ub2c8\ub2e4. \uc9c0\uae08\ucc98\ub7fc \ud2c0\uc744 \ub5a0\uc62c\ub9b0\ub2e4\uac70\ub098, \ub208\uce58\ub97c \ubcf8\ub2e4\uac70\ub098, \uc815\uce58\uc801\uc778 \uc148\ub3c4 \ud558\uc9c0 \uc54a\uc558\uc5b4\uc694.\\n> \\n\\n> \uc81c\ubaa9\uc740 \uc9e7\uac8c, \ubcf4\uae30 \uc27d\uac8c, \uc77d\uae30 \uc27d\uac8c, \ubc1c\uc74c\uc774 \ube44\uc2b7\ud558\uac8c, \uc21c\uc11c\ub97c \ubc14\uafd4\uc11c\\n> \\n\\n> \uae00\uc744 \ub9c8\uc9c0\ub9c9\uc73c\ub85c \ub2e4\ub4ec\uc744 \ub54c, \ub178\ub798\uc5d0 \uac00\uae4c\uc6cc\uc9c8 \ubc29\ubc95\uc740 \uc5c6\uc744\uc9c0 \uace0\ubbfc\ud574\ubd05\ub2c8\ub2e4. \uac10\ud788 \uac00 \ub2ff\uc744 \uc218 \uc5c6\ub294 \ubaa9\ud45c\uc774\uaca0\uc9c0\ub9cc, \ud560 \uc218 \uc788\ub294 \ucd5c\uc18c\ud55c\uc758 \ub9ac\ub4ec\uc774\ub77c\ub3c4 \ubd99\uc5ec\uc8fc\uace0 \uc2f6\uc5b4\uc694.\\n> \\n\\n> \uc5ec\ub294 \ub9d0\uacfc \ub9c8\uc9c0\ub9c9 \ub9d0\uc5d0 \uc791\uc815\ud558\uace0 \ub9c8\uc74c\uc744 \ub2f4\ub294 \uc5f0\uc2b5\uc744 \ud574\ubd05\uc2dc\ub2e4. \uae00\uc758 \uc5b4\ub290 \uad6c\uc11d\uc774\ub77c\ub3c4 \ubed4\ud55c \uae00\uc790\ub294 \ub0a8\uae30\uc9c0 \uc54a\uaca0\ub178\ub77c \ub2e4\uc9d0\ud558\uba70 \uc368\ubcf4\ub294 \uac81\ub2c8\ub2e4. \ub098\ub9cc\uc774 \uac00\uc9c4 \uc720\uc77c\ud55c \uba54\uc2dc\uc9c0\uc5d0 \uc9d1\uc911\ud558\uba74\uc11c\uc694. \uadf8\ub7fc \uc0dd\uac01\uc774 \ub2ec\ub77c\uc9c0\uace0, \uace0\ub974\ub294 \ub2e8\uc5b4\ub3c4 \ub2ec\ub77c\uc9c0\uace0, \ub0a8\uae34 \ubb38\uc7a5\ub3c4 \ub2ec\ub77c\uc838\uc694. \uacb0\uad6d\uc5d0\ub294 \uae00\uc744 \uc4f4 \uc0ac\ub78c\uc778 \ub098 \uc790\uc2e0\ub3c4 \ub0a8\ub2ec\ub77c\uc9c8 \uac81\ub2c8\ub2e4.\\n> \\n\\n> \ub9de\ucda4\ubc95\uc740 \uc911\uc694\ud569\ub2c8\ub2e4. \ud558\uc9c0\ub9cc \ub9de\ucda4\ubc95\ubcf4\ub2e4 \ub354 \uc911\uc694\ud55c \uac74 \uac70\uae30\uc5d0 \ub2f4\uae34 \ub9c8\uc74c\uc785\ub2c8\ub2e4. \ub0b4 \ub9c8\uc74c\uc744 \uae00\uc5d0 \ub2f4\uc544 \uc2e4\uc5b4 \ubcf4\ub0b4\uae30 \uc804, \ub9de\ucda4\ubc95\uc744 \uc810\uac80\ud558\ub294 \uc774\uc720 \uc5ed\uc2dc \uadf8\uac81\ub2c8\ub2e4. \uc624\uc9c1 \ub0b4 \ub9c8\uc74c\uc774 \ub0a8\uc5d0\uac8c \uc77d\ud788\ub294 \ub3d9\uc548 \ubc29\ud574\uac00 \ub418\uc9c0 \uc54a\uae30\ub97c \ubc14\ub77c\uae30 \ub54c\ubb38\uc774\uc8e0. \ub0b4\uac00 \uc4f4 \uae00\ub3c4, \ub0a8\uc774 \uc4f4 \uae00\ub3c4. \uc5b8\uc81c\ub098 \uadf8 \uc548\uc5d0 \ub2f4\uae34 \ub9c8\uc74c\uc774 \uba3c\uc800\uc785\ub2c8\ub2e4.\\n> \\n\\n> \uae00\uc744 \uc4f4\ub2e4\uace0 \uae00\uc774 \uc644\uc131\ub418\ub294 \uac8c \uc544\ub2c8\uc5d0\uc694. \uae00\uacfc \ub2ee\uc740 \ubaa8\uc2b5\uc73c\ub85c \uc0b4 \ub54c, \uae00\uc740 \ube44\ub85c\uc18c \uc644\uc131\ub429\ub2c8\ub2e4.\\n>"}]}')}}]); \ No newline at end of file diff --git a/assets/js/b421ebb7.a60899a2.js b/assets/js/b421ebb7.867062fd.js similarity index 98% rename from assets/js/b421ebb7.a60899a2.js rename to assets/js/b421ebb7.867062fd.js index 2bec94dbe..b354ace59 100644 --- a/assets/js/b421ebb7.a60899a2.js +++ b/assets/js/b421ebb7.867062fd.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[8518],{3905:(e,t,r)=>{r.d(t,{Zo:()=>i,kt:()=>h});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function p(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var u=n.createContext({}),c=function(e){var t=n.useContext(u),r=t;return e&&(r="function"==typeof e?e(t):p(p({},t),e)),r},i=function(e){var t=c(e.components);return n.createElement(u.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,u=e.parentName,i=l(e,["components","mdxType","originalType","parentName"]),m=c(r),h=a,d=m["".concat(u,".").concat(h)]||m[h]||s[h]||o;return r?n.createElement(d,p(p({ref:t},i),{},{components:r})):n.createElement(d,p({ref:t},i))}));function h(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,p=new Array(o);p[0]=m;var l={};for(var u in t)hasOwnProperty.call(t,u)&&(l[u]=t[u]);l.originalType=e,l.mdxType="string"==typeof e?e:a,p[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>u,contentTitle:()=>p,default:()=>s,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var n=r(87462),a=(r(67294),r(3905));const o={title:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",slug:"/performance/throughput-latency",tags:["throughput","latency"]},p=void 0,l={unversionedId:"\uc131\ub2a5/Throughput\uacfc Latency",id:"\uc131\ub2a5/Throughput\uacfc Latency",title:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",description:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",source:"@site/docs/\uc131\ub2a5/Throughput\uacfc Latency.mdx",sourceDirName:"\uc131\ub2a5",slug:"/performance/throughput-latency",permalink:"/docs/performance/throughput-latency",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uc131\ub2a5/Throughput\uacfc Latency.mdx",tags:[{label:"throughput",permalink:"/docs/tags/throughput"},{label:"latency",permalink:"/docs/tags/latency"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",slug:"/performance/throughput-latency",tags:["throughput","latency"]},sidebar:"tutorialSidebar",previous:{title:"Throughput \ubaa9\ud46f\uac12",permalink:"/docs/performance/throughput"},next:{title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc720\ud615",permalink:"/docs/performance/types"}},u={},c=[{value:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",id:"\uc2dc\uc2a4\ud15c-\uc131\ub2a5-\uc9c0\ud45c",level:3},{value:"Throughput",id:"throughput",level:3},{value:"Latency",id:"latency",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],i={toc:c};function s(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},i,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\uc2dc\uc2a4\ud15c-\uc131\ub2a5-\uc9c0\ud45c"},"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c"),(0,a.kt)("p",null,"\uc2dc\uc2a4\ud15c \uc131\ub2a5\uc740 \uc5b4\ub5bb\uac8c \uce21\uc815\ud560\uae4c?",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"\uc131\ub2a5\uc774 \ube68\ub77c\uc84c\ub2e4.")," \ub77c\ub294 \uac83\uc744 \uc5b4\ub5bb\uac8c \uc218\uce58\ud654\ud560 \uc218 \uc788\uc744\uae4c?",(0,a.kt)("br",{parentName:"p"}),"\n","\uc77c\ubc18\uc801\uc73c\ub85c \uc2dc\uc2a4\ud15c \uc131\ub2a5\uc740 Throughput\uacfc Latency\ub77c\ub294 \uc131\ub2a5 \uc9c0\ud45c\ub85c \uce21\uc815\uc744 \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"throughput"},"Throughput"),(0,a.kt)("p",null,"\ucd08\ub2f9 \ucc98\ub9ac\ud558\ub294 \uc791\uc5c5\uc758 \uc218, \uc989 \ucc98\ub9ac\ub7c9\uc744 \uc758\ubbf8\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc77c\ubc18\uc801\uc73c\ub85c \ucd08\ub2f9 \uba87 \uac1c\uc758 \uc694\uccad\uc744 \ucc98\ub9ac\ud558\ub294\uc9c0(RPS, Request Per Second)\ub97c \uae30\uc900\uc73c\ub85c \uce21\uc815\uc744 \ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e4\uc74c\uacfc \uac19\uc740 \uc2dc\uc2a4\ud15c\uc774 \uc788\ub2e4\uace0 \uac00\uc815\ud574\ubcf4\uc790."),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\uc2dc\uc2a4\ud15c A: \ucd08\ub2f9 \uc57d 100\uac1c\uc758 \uc694\uccad \ucc98\ub9ac"),(0,a.kt)("li",{parentName:"ul"},"\uc2dc\uc2a4\ud15c B: \ucd08\ub2f9 \uc57d 200\uac1c\uc758 \uc694\uccad \ucc98\ub9ac")),(0,a.kt)("p",null,"\uc2dc\uc2a4\ud15c B\uac00 \ub3d9\uc2dc\uac04 \ub300\ube44 \ub354 \ub192\uc740 \ucc98\ub9ac\ub7c9\uc744 \ubcf4\uc5ec\uc8fc\uace0 \uc788\uc73c\ub2c8, \uc2dc\uc2a4\ud15c B\uc758 \uc131\ub2a5\uc774 \ub354 \uc88b\ub2e4\uace0 \ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"latency"},"Latency"),(0,a.kt)("p",null,"\uc2dc\uc2a4\ud15c\uc758 \ud3c9\uade0 \uc751\ub2f5 \uc2dc\uac04, \uc989 \ucc98\ub9ac \uc2dc\uac04\uc744 \uc758\ubbf8\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","Latency\ub294 \ub2e4\uc74c\uacfc \uac19\uc774 \ub450 \uac00\uc9c0\ub85c \uad6c\ubd84\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"\uc0ac\uc6a9\uc790\uac00 \ubcf8 \ucc98\ub9ac \uc2dc\uac04"),": \uc0ac\uc6a9\uc790\uac00 \uc694\uccad\uc744 \ubcf4\ub0b4\uace0 \uc751\ub2f5\uc744 \ubc1b\uc744 \ub54c\uae4c\uc9c0\uc758 \uc2dc\uac04",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"\uc2dc\uc2a4\ud15c\uc5d0\uc11c \ubcf8 \ucc98\ub9ac \uc2dc\uac04"),": \uc2dc\uc2a4\ud15c\uc774 \uc694\uccad\uc744 \ubc1b\uace0 \uc751\ub2f5\uc744 \ubcf4\ub0bc \ub54c\uae4c\uc9c0\uc758 \uc2dc\uac04 "),(0,a.kt)("p",null,"\uc0ac\uc6a9\uc790\uac00 \ubcf8 \ucc98\ub9ac \uc2dc\uac04\uc758 \uacbd\uc6b0 \ub124\ud2b8\uc6cc\ud06c\uc5d0 \ub300\ud55c \uc2dc\uac04\uc774 \ud3ec\ud568\ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub530\ub77c\uc11c \uc131\ub2a5 \uce21\uc815\uacfc \uac1c\uc120\uc740 \uc2dc\uc2a4\ud15c\uc5d0\uc11c \ubcf8 \ucc98\ub9ac \uc2dc\uac04\uc73c\ub85c \uce21\uc815\ud558\uace0 \uac1c\uc120\ud558\ub294 \uac83\uc774 \ub354 \uc815\ud655\ud574 \ubcf4\uc778\ub2e4. "),(0,a.kt)("p",null,"\ub2e4\uc74c\uacfc \uac19\uc740 \uc2dc\uc2a4\ud15c\uc774 \uc788\ub2e4\uace0 \uac00\uc815\ud574\ubcf4\uc790. "),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\uc2dc\uc2a4\ud15c A: \uc694\uccad\uc2dc \ud3c9\uade0 200ms \ub0b4 \uc751\ub2f5 "),(0,a.kt)("li",{parentName:"ul"},"\uc2dc\uc2a4\ud15c B: \uc694\uccad\uc2dc \ud3c9\uade0 100ms \ub0b4 \uc751\ub2f5 ")),(0,a.kt)("p",null,"\uc2dc\uc2a4\ud15c B\uac00 \uc694\uccad\uc5d0 \ub300\ud55c \uc751\ub2f5\uc774 \ub354 \ube60\ub974\ub2c8, \uc2dc\uc2a4\ud15c B\uc758 \uc131\ub2a5\uc774 \ub354 \uc88b\ub2e4\uace0 \ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uc544\ub9c8\uc874 \uc6f9 \uc11c\ube44\uc2a4 \ubd80\ud558 \ud14c\uc2a4\ud2b8 \uc785\ubb38 - \ub098\uce74\uac00\uc640 \ud0c0\ub8e8\ud558\uce58, \ubaa8\ub9ac\uc2dc\ud0c0 \ucf04",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://aws.amazon.com/ko/compare/the-difference-between-throughput-and-latency/"},"difference between throughput and latency, AWS")," - \ud574\ub2f9 \ub0b4\uc6a9\uc740 \ub124\ud2b8\uc6cc\ud06c \uae30\uc900\uc774\ub2e4."))}s.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[8518],{3905:(e,t,r)=>{r.d(t,{Zo:()=>i,kt:()=>h});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function p(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var u=n.createContext({}),c=function(e){var t=n.useContext(u),r=t;return e&&(r="function"==typeof e?e(t):p(p({},t),e)),r},i=function(e){var t=c(e.components);return n.createElement(u.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,u=e.parentName,i=l(e,["components","mdxType","originalType","parentName"]),m=c(r),h=a,d=m["".concat(u,".").concat(h)]||m[h]||s[h]||o;return r?n.createElement(d,p(p({ref:t},i),{},{components:r})):n.createElement(d,p({ref:t},i))}));function h(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,p=new Array(o);p[0]=m;var l={};for(var u in t)hasOwnProperty.call(t,u)&&(l[u]=t[u]);l.originalType=e,l.mdxType="string"==typeof e?e:a,p[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>u,contentTitle:()=>p,default:()=>s,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var n=r(87462),a=(r(67294),r(3905));const o={title:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",slug:"/performance/throughput-latency",tags:["throughput","latency"]},p=void 0,l={unversionedId:"\uc131\ub2a5/Throughput\uacfc Latency",id:"\uc131\ub2a5/Throughput\uacfc Latency",title:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",description:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",source:"@site/docs/\uc131\ub2a5/Throughput\uacfc Latency.mdx",sourceDirName:"\uc131\ub2a5",slug:"/performance/throughput-latency",permalink:"/docs/performance/throughput-latency",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uc131\ub2a5/Throughput\uacfc Latency.mdx",tags:[{label:"throughput",permalink:"/docs/tags/throughput"},{label:"latency",permalink:"/docs/tags/latency"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",slug:"/performance/throughput-latency",tags:["throughput","latency"]},sidebar:"tutorialSidebar",previous:{title:"Throughput \ubaa9\ud46f\uac12",permalink:"/docs/performance/throughput"},next:{title:"\uc131\ub2a5 \ud14c\uc2a4\ud2b8 \uc720\ud615",permalink:"/docs/performance/types"}},u={},c=[{value:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",id:"\uc2dc\uc2a4\ud15c-\uc131\ub2a5-\uc9c0\ud45c",level:3},{value:"Throughput",id:"throughput",level:3},{value:"Latency",id:"latency",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],i={toc:c};function s(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},i,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\uc2dc\uc2a4\ud15c-\uc131\ub2a5-\uc9c0\ud45c"},"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c"),(0,a.kt)("p",null,"\uc2dc\uc2a4\ud15c \uc131\ub2a5\uc740 \uc5b4\ub5bb\uac8c \uce21\uc815\ud560\uae4c?",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"\uc131\ub2a5\uc774 \ube68\ub77c\uc84c\ub2e4.")," \ub77c\ub294 \uac83\uc744 \uc5b4\ub5bb\uac8c \uc218\uce58\ud654\ud560 \uc218 \uc788\uc744\uae4c?",(0,a.kt)("br",{parentName:"p"}),"\n","\uc77c\ubc18\uc801\uc73c\ub85c \uc2dc\uc2a4\ud15c \uc131\ub2a5\uc740 Throughput\uacfc Latency\ub77c\ub294 \uc131\ub2a5 \uc9c0\ud45c\ub85c \uce21\uc815\uc744 \ud55c\ub2e4. "),(0,a.kt)("h3",{id:"throughput"},"Throughput"),(0,a.kt)("p",null,"\ucd08\ub2f9 \ucc98\ub9ac\ud558\ub294 \uc791\uc5c5\uc758 \uc218, \uc989 \ucc98\ub9ac\ub7c9\uc744 \uc758\ubbf8\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc77c\ubc18\uc801\uc73c\ub85c \ucd08\ub2f9 \uba87 \uac1c\uc758 \uc694\uccad\uc744 \ucc98\ub9ac\ud558\ub294\uc9c0(RPS, Request Per Second)\ub97c \uae30\uc900\uc73c\ub85c \uce21\uc815\uc744 \ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2e4\uc74c\uacfc \uac19\uc740 \uc2dc\uc2a4\ud15c\uc774 \uc788\ub2e4\uace0 \uac00\uc815\ud574\ubcf4\uc790."),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\uc2dc\uc2a4\ud15c A: \ucd08\ub2f9 \uc57d 100\uac1c\uc758 \uc694\uccad \ucc98\ub9ac"),(0,a.kt)("li",{parentName:"ul"},"\uc2dc\uc2a4\ud15c B: \ucd08\ub2f9 \uc57d 200\uac1c\uc758 \uc694\uccad \ucc98\ub9ac")),(0,a.kt)("p",null,"\uc2dc\uc2a4\ud15c B\uac00 \ub3d9\uc2dc\uac04 \ub300\ube44 \ub354 \ub192\uc740 \ucc98\ub9ac\ub7c9\uc744 \ubcf4\uc5ec\uc8fc\uace0 \uc788\uc73c\ub2c8, \uc2dc\uc2a4\ud15c B\uc758 \uc131\ub2a5\uc774 \ub354 \uc88b\ub2e4\uace0 \ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"latency"},"Latency"),(0,a.kt)("p",null,"\uc2dc\uc2a4\ud15c\uc758 \ud3c9\uade0 \uc751\ub2f5 \uc2dc\uac04, \uc989 \ucc98\ub9ac \uc2dc\uac04\uc744 \uc758\ubbf8\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","Latency\ub294 \ub2e4\uc74c\uacfc \uac19\uc774 \ub450 \uac00\uc9c0\ub85c \uad6c\ubd84\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"\uc0ac\uc6a9\uc790\uac00 \ubcf8 \ucc98\ub9ac \uc2dc\uac04"),": \uc0ac\uc6a9\uc790\uac00 \uc694\uccad\uc744 \ubcf4\ub0b4\uace0 \uc751\ub2f5\uc744 \ubc1b\uc744 \ub54c\uae4c\uc9c0\uc758 \uc2dc\uac04",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("inlineCode",{parentName:"p"},"\uc2dc\uc2a4\ud15c\uc5d0\uc11c \ubcf8 \ucc98\ub9ac \uc2dc\uac04"),": \uc2dc\uc2a4\ud15c\uc774 \uc694\uccad\uc744 \ubc1b\uace0 \uc751\ub2f5\uc744 \ubcf4\ub0bc \ub54c\uae4c\uc9c0\uc758 \uc2dc\uac04 "),(0,a.kt)("p",null,"\uc0ac\uc6a9\uc790\uac00 \ubcf8 \ucc98\ub9ac \uc2dc\uac04\uc758 \uacbd\uc6b0 \ub124\ud2b8\uc6cc\ud06c\uc5d0 \ub300\ud55c \uc2dc\uac04\uc774 \ud3ec\ud568\ub41c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub530\ub77c\uc11c \uc131\ub2a5 \uce21\uc815\uacfc \uac1c\uc120\uc740 \uc2dc\uc2a4\ud15c\uc5d0\uc11c \ubcf8 \ucc98\ub9ac \uc2dc\uac04\uc73c\ub85c \uce21\uc815\ud558\uace0 \uac1c\uc120\ud558\ub294 \uac83\uc774 \ub354 \uc815\ud655\ud574 \ubcf4\uc778\ub2e4. "),(0,a.kt)("p",null,"\ub2e4\uc74c\uacfc \uac19\uc740 \uc2dc\uc2a4\ud15c\uc774 \uc788\ub2e4\uace0 \uac00\uc815\ud574\ubcf4\uc790. "),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\uc2dc\uc2a4\ud15c A: \uc694\uccad\uc2dc \ud3c9\uade0 200ms \ub0b4 \uc751\ub2f5 "),(0,a.kt)("li",{parentName:"ul"},"\uc2dc\uc2a4\ud15c B: \uc694\uccad\uc2dc \ud3c9\uade0 100ms \ub0b4 \uc751\ub2f5 ")),(0,a.kt)("p",null,"\uc2dc\uc2a4\ud15c B\uac00 \uc694\uccad\uc5d0 \ub300\ud55c \uc751\ub2f5\uc774 \ub354 \ube60\ub974\ub2c8, \uc2dc\uc2a4\ud15c B\uc758 \uc131\ub2a5\uc774 \ub354 \uc88b\ub2e4\uace0 \ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,"\uc544\ub9c8\uc874 \uc6f9 \uc11c\ube44\uc2a4 \ubd80\ud558 \ud14c\uc2a4\ud2b8 \uc785\ubb38 - \ub098\uce74\uac00\uc640 \ud0c0\ub8e8\ud558\uce58, \ubaa8\ub9ac\uc2dc\ud0c0 \ucf04",(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://aws.amazon.com/ko/compare/the-difference-between-throughput-and-latency/"},"difference between throughput and latency, AWS")," - \ud574\ub2f9 \ub0b4\uc6a9\uc740 \ub124\ud2b8\uc6cc\ud06c \uae30\uc900\uc774\ub2e4."))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/b88cb85b.f345a857.js b/assets/js/b88cb85b.d779ea6a.js similarity index 98% rename from assets/js/b88cb85b.f345a857.js rename to assets/js/b88cb85b.d779ea6a.js index 0ce66339b..4d6625780 100644 --- a/assets/js/b88cb85b.f345a857.js +++ b/assets/js/b88cb85b.d779ea6a.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2293],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var d=r.createContext({}),c=function(e){var t=r.useContext(d),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(d.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},u=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,d=e.parentName,p=i(e,["components","mdxType","originalType","parentName"]),u=c(n),m=a,h=u["".concat(d,".").concat(m)]||u[m]||s[m]||o;return n?r.createElement(h,l(l({ref:t},p),{},{components:n})):r.createElement(h,l({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,l=new Array(o);l[0]=u;var i={};for(var d in t)hasOwnProperty.call(t,d)&&(i[d]=t[d]);i.originalType=e,i.mdxType="string"==typeof e?e:a,l[1]=i;for(var c=2;c{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>l,default:()=>s,frontMatter:()=>o,metadata:()=>i,toc:()=>c});var r=n(87462),a=(n(67294),n(3905));const o={title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",slug:"/network/load-balancing-algorithm",tags:["network","load balancing"]},l=void 0,i={unversionedId:"\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",id:"\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",description:"\ub77c\uc6b4\ub4dc\ub85c\ube48 \ubc29\uc2dd(Round Robin Method)",source:"@site/docs/\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998.mdx",sourceDirName:"\ub124\ud2b8\uc6cc\ud06c",slug:"/network/load-balancing-algorithm",permalink:"/docs/network/load-balancing-algorithm",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998.mdx",tags:[{label:"network",permalink:"/docs/tags/network"},{label:"load balancing",permalink:"/docs/tags/load-balancing"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",slug:"/network/load-balancing-algorithm",tags:["network","load balancing"]},sidebar:"tutorialSidebar",previous:{title:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",permalink:"/docs/etc/communication"},next:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1",permalink:"/docs/network/load-balancing"}},d={},c=[{value:"\ub77c\uc6b4\ub4dc\ub85c\ube48 \ubc29\uc2dd(Round Robin Method)",id:"\ub77c\uc6b4\ub4dc\ub85c\ube48-\ubc29\uc2ddround-robin-method",level:3},{value:"\uac00\uc911 \uae30\ubc18 \ub77c\uc6b4\ub4dc \ub85c\ube48 \ubc29\uc2dd(Weighted Round Robin Method)",id:"\uac00\uc911-\uae30\ubc18-\ub77c\uc6b4\ub4dc-\ub85c\ube48-\ubc29\uc2ddweighted-round-robin-method",level:3},{value:"\ucd5c\uc18c \uc5f0\uacb0 \uae30\ubc18 \ubc29\uc2dd(Least Connection Method)",id:"\ucd5c\uc18c-\uc5f0\uacb0-\uae30\ubc18-\ubc29\uc2ddleast-connection-method",level:3},{value:"IP \ud574\uc2dc \ubc29\uc2dd(IP Hash Method)",id:"ip-\ud574\uc2dc-\ubc29\uc2ddip-hash-method",level:3},{value:"\ucd5c\uc18c \uc751\ub2f5 \uc2dc\uac04 \ubc29\uc2dd(Least Response Time Method)",id:"\ucd5c\uc18c-\uc751\ub2f5-\uc2dc\uac04-\ubc29\uc2ddleast-response-time-method",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],p={toc:c};function s(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ub77c\uc6b4\ub4dc\ub85c\ube48-\ubc29\uc2ddround-robin-method"},"\ub77c\uc6b4\ub4dc\ub85c\ube48 \ubc29\uc2dd(Round Robin Method)"),(0,a.kt)("p",null,"\ud074\ub77c\uc774\uc5b8\ud2b8\ub85c\ubd80\ud130 \ubc1b\uc740 \uc694\uccad\uc744 \ub85c\ub4dc \ubc38\ub7f0\uc2f1 \ub300\uc0c1 \uc11c\ubc84\uc5d0 \uc21c\uc11c\ub300\ub85c \ud560\ub2f9\ubc1b\ub294 \ubc29\uc2dd"),(0,a.kt)("mermaid",{value:"graph LR\n LB[Load Balancer] -- 33% --\x3e A\n LB -- 33% --\x3e B\n LB -- 33% --\x3e C"}),(0,a.kt)("h3",{id:"\uac00\uc911-\uae30\ubc18-\ub77c\uc6b4\ub4dc-\ub85c\ube48-\ubc29\uc2ddweighted-round-robin-method"},"\uac00\uc911 \uae30\ubc18 \ub77c\uc6b4\ub4dc \ub85c\ube48 \ubc29\uc2dd(Weighted Round Robin Method)"),(0,a.kt)("p",null,"\uc2e4\uc81c \uc11c\ubc84\uc5d0 \uc11c\ub85c \ub2e4\ub978 \ucc98\ub9ac \uc6a9\ub7c9\uc744 \uc9c0\uc815\ud558\ub294 \ubc29\uc2dd"),(0,a.kt)("mermaid",{value:"graph LR\n LB[Load Balancer] -- 80% --\x3e A[A \uc11c\ubc84 4]\n LB -- 20% --\x3e B[B \uc11c\ubc84 1]"}),(0,a.kt)("h3",{id:"\ucd5c\uc18c-\uc5f0\uacb0-\uae30\ubc18-\ubc29\uc2ddleast-connection-method"},"\ucd5c\uc18c \uc5f0\uacb0 \uae30\ubc18 \ubc29\uc2dd(Least Connection Method)"),(0,a.kt)("p",null,"\ub85c\ub4dc \ubc38\ub7f0\uc11c\ub294 \ud65c\uc131 \uc5f0\uacb0\uc774 \uac00\uc7a5 \uc801\uc740 \uc11c\ubc84\ub97c \ud655\uc778\ud558\uace0 \ud574\ub2f9 \uc11c\ubc84\ub85c \ud2b8\ub798\ud53d\uc744 \uc804\uc1a1"),(0,a.kt)("mermaid",{value:"graph LR\n LB[Load Balancer] -- X --\x3e A[A \uc11c\ubc84 \uc5f0\uacb0 \uc218 4]\n LB -- \uc5f0\uacb0\uc218\uac00 \uc801\uc740 \uc11c\ubc84\ub85c \uc120\ud0dd --\x3e B[B \uc11c\ubc84 \uc5f0\uacb0 \uc218 3]"}),(0,a.kt)("h3",{id:"ip-\ud574\uc2dc-\ubc29\uc2ddip-hash-method"},"IP \ud574\uc2dc \ubc29\uc2dd(IP Hash Method)"),(0,a.kt)("p",null,"\uac01 \ud074\ub77c\uc774\uc5b8\ud2b8\uc5d0 \ub300\ud574 Hashing key\ub97c \uac00\uc9c0\uace0 \uacbd\ub85c\ub97c \uc9c0\uc815",(0,a.kt)("br",{parentName:"p"}),"\n","Hashing key\ub294 \ud074\ub77c\uc774\uc5b8\ud2b8\uc758 IP + port \ud639\uc740 IP \uc8fc\uc18c\ub9cc\uc73c\ub85c \uacb0\uc815",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0ac\uc6a9\uc790\uac00 \ud56d\uc0c1 \ub3d9\uc77c\ud55c \uc11c\ubc84\ub85c \uc5f0\uacb0\ub418\ub294 \uac83\uc744 \ubcf4\uc7a5"),(0,a.kt)("mermaid",{value:'graph LR\n LB[Load Balancer] -- "hash(13.32.45.56)" --\x3e A\n LB -- "hash(142.2.55.24)" --\x3e B\n LB -- "hash(142.2.55.24) \uc7ac\uc694\uccad" --\x3e B'}),(0,a.kt)("h3",{id:"\ucd5c\uc18c-\uc751\ub2f5-\uc2dc\uac04-\ubc29\uc2ddleast-response-time-method"},"\ucd5c\uc18c \uc751\ub2f5 \uc2dc\uac04 \ubc29\uc2dd(Least Response Time Method)"),(0,a.kt)("p",null,"\uc11c\ubc84\uc758 \ud604\uc7ac \uc5f0\uacb0 \uc0c1\ud0dc, \uc751\ub2f5 \uc2dc\uac04 \uace0\ub824\ud558\uc5ec \ud2b8\ub798\ud53d\uc744 \uc804\uc1a1",(0,a.kt)("br",{parentName:"p"}),"\n","\uac00\uc7a5 \uc801\uc740 \uc5f0\uacb0 \uc0c1\ud0dc\uc640 \uac00\uc7a5 \uc9e7\uc740 \uc751\ub2f5 \uc2dc\uac04\uc744 \ubcf4\uc774\ub294 \uc11c\ubc84\uc5d0 \uc6b0\uc120\uc801\uc73c\ub85c \ub85c\ub4dc\ub97c \ubc30\ubd84\ud558\ub294 \ubc29\uc2dd "),(0,a.kt)("mermaid",{value:"graph LR\n LB[Load Balancer] -- \uc751\ub2f5 \uc2dc\uac04\uc774 \ub354 \ube60\ub974\ub2c8 A \uc11c\ubc84\ub85c \uc804\ub2ec --\x3e A[A \uc751\ub2f5\uc2dc\uac04 100ms]\n LB -- X --\x3e B[B \uc751\ub2f5\uc2dc\uac04 150ms]"}),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://aws.amazon.com/ko/what-is/load-balancing/"},"load balancing, AWS"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://tecoble.techcourse.co.kr/post/2021-11-07-load-balancing/"},"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc5d0 \ub300\ud574 \uc54c\uc544\ubcf4\uc790, \ud14c\ucf54\ube14")))}s.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2293],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var d=r.createContext({}),c=function(e){var t=r.useContext(d),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(d.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},u=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,d=e.parentName,p=i(e,["components","mdxType","originalType","parentName"]),u=c(n),m=a,h=u["".concat(d,".").concat(m)]||u[m]||s[m]||o;return n?r.createElement(h,l(l({ref:t},p),{},{components:n})):r.createElement(h,l({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,l=new Array(o);l[0]=u;var i={};for(var d in t)hasOwnProperty.call(t,d)&&(i[d]=t[d]);i.originalType=e,i.mdxType="string"==typeof e?e:a,l[1]=i;for(var c=2;c{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>l,default:()=>s,frontMatter:()=>o,metadata:()=>i,toc:()=>c});var r=n(87462),a=(n(67294),n(3905));const o={title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",slug:"/network/load-balancing-algorithm",tags:["network","load balancing"]},l=void 0,i={unversionedId:"\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",id:"\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",description:"\ub77c\uc6b4\ub4dc\ub85c\ube48 \ubc29\uc2dd(Round Robin Method)",source:"@site/docs/\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998.mdx",sourceDirName:"\ub124\ud2b8\uc6cc\ud06c",slug:"/network/load-balancing-algorithm",permalink:"/docs/network/load-balancing-algorithm",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998.mdx",tags:[{label:"network",permalink:"/docs/tags/network"},{label:"load balancing",permalink:"/docs/tags/load-balancing"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",slug:"/network/load-balancing-algorithm",tags:["network","load balancing"]},sidebar:"tutorialSidebar",previous:{title:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",permalink:"/docs/etc/communication"},next:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1",permalink:"/docs/network/load-balancing"}},d={},c=[{value:"\ub77c\uc6b4\ub4dc\ub85c\ube48 \ubc29\uc2dd(Round Robin Method)",id:"\ub77c\uc6b4\ub4dc\ub85c\ube48-\ubc29\uc2ddround-robin-method",level:3},{value:"\uac00\uc911 \uae30\ubc18 \ub77c\uc6b4\ub4dc \ub85c\ube48 \ubc29\uc2dd(Weighted Round Robin Method)",id:"\uac00\uc911-\uae30\ubc18-\ub77c\uc6b4\ub4dc-\ub85c\ube48-\ubc29\uc2ddweighted-round-robin-method",level:3},{value:"\ucd5c\uc18c \uc5f0\uacb0 \uae30\ubc18 \ubc29\uc2dd(Least Connection Method)",id:"\ucd5c\uc18c-\uc5f0\uacb0-\uae30\ubc18-\ubc29\uc2ddleast-connection-method",level:3},{value:"IP \ud574\uc2dc \ubc29\uc2dd(IP Hash Method)",id:"ip-\ud574\uc2dc-\ubc29\uc2ddip-hash-method",level:3},{value:"\ucd5c\uc18c \uc751\ub2f5 \uc2dc\uac04 \ubc29\uc2dd(Least Response Time Method)",id:"\ucd5c\uc18c-\uc751\ub2f5-\uc2dc\uac04-\ubc29\uc2ddleast-response-time-method",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],p={toc:c};function s(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ub77c\uc6b4\ub4dc\ub85c\ube48-\ubc29\uc2ddround-robin-method"},"\ub77c\uc6b4\ub4dc\ub85c\ube48 \ubc29\uc2dd(Round Robin Method)"),(0,a.kt)("p",null,"\ud074\ub77c\uc774\uc5b8\ud2b8\ub85c\ubd80\ud130 \ubc1b\uc740 \uc694\uccad\uc744 \ub85c\ub4dc \ubc38\ub7f0\uc2f1 \ub300\uc0c1 \uc11c\ubc84\uc5d0 \uc21c\uc11c\ub300\ub85c \ud560\ub2f9\ubc1b\ub294 \ubc29\uc2dd"),(0,a.kt)("mermaid",{value:"graph LR\n LB[Load Balancer] -- 33% --\x3e A\n LB -- 33% --\x3e B\n LB -- 33% --\x3e C"}),(0,a.kt)("h3",{id:"\uac00\uc911-\uae30\ubc18-\ub77c\uc6b4\ub4dc-\ub85c\ube48-\ubc29\uc2ddweighted-round-robin-method"},"\uac00\uc911 \uae30\ubc18 \ub77c\uc6b4\ub4dc \ub85c\ube48 \ubc29\uc2dd(Weighted Round Robin Method)"),(0,a.kt)("p",null,"\uc2e4\uc81c \uc11c\ubc84\uc5d0 \uc11c\ub85c \ub2e4\ub978 \ucc98\ub9ac \uc6a9\ub7c9\uc744 \uc9c0\uc815\ud558\ub294 \ubc29\uc2dd"),(0,a.kt)("mermaid",{value:"graph LR\n LB[Load Balancer] -- 80% --\x3e A[A \uc11c\ubc84 4]\n LB -- 20% --\x3e B[B \uc11c\ubc84 1]"}),(0,a.kt)("h3",{id:"\ucd5c\uc18c-\uc5f0\uacb0-\uae30\ubc18-\ubc29\uc2ddleast-connection-method"},"\ucd5c\uc18c \uc5f0\uacb0 \uae30\ubc18 \ubc29\uc2dd(Least Connection Method)"),(0,a.kt)("p",null,"\ub85c\ub4dc \ubc38\ub7f0\uc11c\ub294 \ud65c\uc131 \uc5f0\uacb0\uc774 \uac00\uc7a5 \uc801\uc740 \uc11c\ubc84\ub97c \ud655\uc778\ud558\uace0 \ud574\ub2f9 \uc11c\ubc84\ub85c \ud2b8\ub798\ud53d\uc744 \uc804\uc1a1"),(0,a.kt)("mermaid",{value:"graph LR\n LB[Load Balancer] -- X --\x3e A[A \uc11c\ubc84 \uc5f0\uacb0 \uc218 4]\n LB -- \uc5f0\uacb0\uc218\uac00 \uc801\uc740 \uc11c\ubc84\ub85c \uc120\ud0dd --\x3e B[B \uc11c\ubc84 \uc5f0\uacb0 \uc218 3]"}),(0,a.kt)("h3",{id:"ip-\ud574\uc2dc-\ubc29\uc2ddip-hash-method"},"IP \ud574\uc2dc \ubc29\uc2dd(IP Hash Method)"),(0,a.kt)("p",null,"\uac01 \ud074\ub77c\uc774\uc5b8\ud2b8\uc5d0 \ub300\ud574 Hashing key\ub97c \uac00\uc9c0\uace0 \uacbd\ub85c\ub97c \uc9c0\uc815",(0,a.kt)("br",{parentName:"p"}),"\n","Hashing key\ub294 \ud074\ub77c\uc774\uc5b8\ud2b8\uc758 IP + port \ud639\uc740 IP \uc8fc\uc18c\ub9cc\uc73c\ub85c \uacb0\uc815",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0ac\uc6a9\uc790\uac00 \ud56d\uc0c1 \ub3d9\uc77c\ud55c \uc11c\ubc84\ub85c \uc5f0\uacb0\ub418\ub294 \uac83\uc744 \ubcf4\uc7a5"),(0,a.kt)("mermaid",{value:'graph LR\n LB[Load Balancer] -- "hash(13.32.45.56)" --\x3e A\n LB -- "hash(142.2.55.24)" --\x3e B\n LB -- "hash(142.2.55.24) \uc7ac\uc694\uccad" --\x3e B'}),(0,a.kt)("h3",{id:"\ucd5c\uc18c-\uc751\ub2f5-\uc2dc\uac04-\ubc29\uc2ddleast-response-time-method"},"\ucd5c\uc18c \uc751\ub2f5 \uc2dc\uac04 \ubc29\uc2dd(Least Response Time Method)"),(0,a.kt)("p",null,"\uc11c\ubc84\uc758 \ud604\uc7ac \uc5f0\uacb0 \uc0c1\ud0dc, \uc751\ub2f5 \uc2dc\uac04 \uace0\ub824\ud558\uc5ec \ud2b8\ub798\ud53d\uc744 \uc804\uc1a1",(0,a.kt)("br",{parentName:"p"}),"\n","\uac00\uc7a5 \uc801\uc740 \uc5f0\uacb0 \uc0c1\ud0dc\uc640 \uac00\uc7a5 \uc9e7\uc740 \uc751\ub2f5 \uc2dc\uac04\uc744 \ubcf4\uc774\ub294 \uc11c\ubc84\uc5d0 \uc6b0\uc120\uc801\uc73c\ub85c \ub85c\ub4dc\ub97c \ubc30\ubd84\ud558\ub294 \ubc29\uc2dd "),(0,a.kt)("mermaid",{value:"graph LR\n LB[Load Balancer] -- \uc751\ub2f5 \uc2dc\uac04\uc774 \ub354 \ube60\ub974\ub2c8 A \uc11c\ubc84\ub85c \uc804\ub2ec --\x3e A[A \uc751\ub2f5\uc2dc\uac04 100ms]\n LB -- X --\x3e B[B \uc751\ub2f5\uc2dc\uac04 150ms]"}),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://aws.amazon.com/ko/what-is/load-balancing/"},"load balancing, AWS"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://tecoble.techcourse.co.kr/post/2021-11-07-load-balancing/"},"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc5d0 \ub300\ud574 \uc54c\uc544\ubcf4\uc790, \ud14c\ucf54\ube14")))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/c29bedb9.da75fb17.js b/assets/js/c29bedb9.64d8690b.js similarity index 57% rename from assets/js/c29bedb9.da75fb17.js rename to assets/js/c29bedb9.64d8690b.js index 1a8c6ed2b..8955cd561 100644 --- a/assets/js/c29bedb9.da75fb17.js +++ b/assets/js/c29bedb9.64d8690b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9242],{44025:e=>{e.exports=JSON.parse('{"permalink":"/page/35","page":35,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/34","nextPage":"/page/36","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9242],{44025:e=>{e.exports=JSON.parse('{"permalink":"/page/35","page":35,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/34","nextPage":"/page/36","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/c55d205b.6a9b956d.js b/assets/js/c55d205b.8cce9cb6.js similarity index 98% rename from assets/js/c55d205b.6a9b956d.js rename to assets/js/c55d205b.8cce9cb6.js index b45adf9ea..2aa59a77d 100644 --- a/assets/js/c55d205b.6a9b956d.js +++ b/assets/js/c55d205b.8cce9cb6.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3438],{3905:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var r=t(67294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function l(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var p=r.createContext({}),c=function(e){var n=r.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},u=function(e){var n=c(e.components);return r.createElement(p.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},s=r.forwardRef((function(e,n){var t=e.components,i=e.mdxType,a=e.originalType,p=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),s=c(t),d=i,g=s["".concat(p,".").concat(d)]||s[d]||m[d]||a;return t?r.createElement(g,l(l({ref:n},u),{},{components:t})):r.createElement(g,l({ref:n},u))}));function d(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var a=t.length,l=new Array(a);l[0]=s;var o={};for(var p in n)hasOwnProperty.call(n,p)&&(o[p]=n[p]);o.originalType=e,o.mdxType="string"==typeof e?e:i,l[1]=o;for(var c=2;c{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>l,default:()=>m,frontMatter:()=>a,metadata:()=>o,toc:()=>c});var r=t(87462),i=(t(67294),t(3905));const a={title:"\uad6c\uc870 \ubc0f \uba85\ub839\uc5b4",slug:"/nginx/command",tags:["nginx"]},l=void 0,o={unversionedId:"Nginx/\uad6c\uc870_\ubc0f_\uba85\ub839\uc5b4",id:"Nginx/\uad6c\uc870_\ubc0f_\uba85\ub839\uc5b4",title:"\uad6c\uc870 \ubc0f \uba85\ub839\uc5b4",description:"\ud3f4\ub354 \ubc0f \ud30c\uc77c",source:"@site/docs/Nginx/\uad6c\uc870_\ubc0f_\uba85\ub839\uc5b4.mdx",sourceDirName:"Nginx",slug:"/nginx/command",permalink:"/docs/nginx/command",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/Nginx/\uad6c\uc870_\ubc0f_\uba85\ub839\uc5b4.mdx",tags:[{label:"nginx",permalink:"/docs/tags/nginx"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\uad6c\uc870 \ubc0f \uba85\ub839\uc5b4",slug:"/nginx/command",tags:["nginx"]},sidebar:"tutorialSidebar",previous:{title:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",permalink:"/docs/jpa/key"},next:{title:"\uc815\uc801 \ucee8\ud150\uce20 \uc81c\uacf5",permalink:"/docs/nginx/static-file"}},p={},c=[{value:"\ud3f4\ub354 \ubc0f \ud30c\uc77c",id:"\ud3f4\ub354-\ubc0f-\ud30c\uc77c",level:3},{value:"\uba85\ub839\uc5b4",id:"\uba85\ub839\uc5b4",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:c};function m(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h3",{id:"\ud3f4\ub354-\ubc0f-\ud30c\uc77c"},"\ud3f4\ub354 \ubc0f \ud30c\uc77c"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"/etc/nginx/")),(0,i.kt)("p",null,"\uae30\ubcf8 \uc124\uc815\uc774 \uc800\uc7a5\ub41c \ub8e8\ud2b8 \ub514\ub809\ud130\ub9ac"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"/etc/nginx/nginx.conf")),(0,i.kt)("p",null,"\uae30\ubcf8 \uc124\uc815 \ud30c\uc77c\ub85c \ubaa8\ub4e0 \uc124\uc815\uc5d0 \ub300\ud55c \uc9c4\uc785\uc810",(0,i.kt)("br",{parentName:"p"}),"\n","\ub514\ub809\ud130\ub9ac\uc5d0 \uc704\uce58\ud55c \ubaa8\ub4e0 \uc124\uc815 \ud30c\uc77c\uc744 \ud3ec\ud568\ud558\ub294 \ucd5c\uc0c1\uc704 Http \ube14\ub85d\uc744 \ud3ec\ud568\ud55c\ub2e4. "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash",metastring:'title="nginx\uc5d0 \ud3ec\ud568\ub41c \uc124\uc815 \ud30c\uc77c \ud3ec\ud568 include \uad6c\ubb38"',title:'"nginx\uc5d0',"\ud3ec\ud568\ub41c":!0,"\uc124\uc815":!0,"\ud30c\uc77c":!0,"\ud3ec\ud568":!0,include:!0,'\uad6c\ubb38"':!0},"http {\n ...\n include /etc/nginx/conf.d/*.conf;\n include /etc/nginx/sites-enabled/*;\n}\n")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"/etc/nginx/conf.d/")),(0,i.kt)("p",null,"\uae30\ubcf8 HTTP \uc11c\ubc84 \uc124\uc815 \ud30c\uc77c\uc5d0 \ub300\ud55c \ub514\ub809\ud130\ub9ac",(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("inlineCode",{parentName:"p"},".conf")," \ub85c \ub05d\ub098\ub294 \uacbd\uc6b0 \uc704\uc758 include \uc124\uc815\uc5d0 \uc758\ud574 \ucd5c\uc0c1\uc704 http \ube14\ub85d\uc5d0 \ud3ec\ud568\ub41c\ub2e4.",(0,i.kt)("br",{parentName:"p"}),"\n","conf.d \ub514\ub809\ud130\ub9ac \ub300\uc2e0 site-enabled \ub514\ub809\ud130\ub9ac\uc640 symlink\ub97c \ud1b5\ud574 \uc124\uc815 \ud30c\uc77c\uc744 \uc5f0\uacb0\ud558\ub294 \ubc29\ubc95\uc740 \ub354 \uc774\uc0c1 \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294\ub2e4. "),(0,i.kt)("admonition",{title:"NIGNX \uc124\uc815",type:"note"},(0,i.kt)("p",{parentName:"admonition"},"nginx \uc124\uc815\uc758 \uacbd\uc6b0 include \uad6c\ubb38\uc744 \ud65c\uc6a9\ud574 \uad6c\uc870\ud654\ud558\uc5ec \uc124\uc815 \ud30c\uc77c\uc744 \uac04\uacb0\ud558\uac8c \uc720\uc9c0\ud558\ub294 \uac83\uc774 \uc88b\ub2e4.")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"/var/log/nginx/")),(0,i.kt)("p",null,"\uc5d4\uc9c4\uc5d1\uc2a4 \ub85c\uadf8\uac00 \uc800\uc7a5\ub418\ub294 \ub514\ub809\ud130\ub9ac\ub85c access \ub85c\uadf8\uc640 error \ub85c\uadf8\ub97c \ud655\uc778\ud560 \uc218 \uc788\ub2e4.",(0,i.kt)("br",{parentName:"p"}),"\n","\ub85c\uadf8 \ud615\uc2dd\uc758 \uacbd\uc6b0 \uc124\uc815 \ud30c\uc77c\uc758 log_format \uad6c\ubb38\uc744 \uc774\uc6a9\ud574\uc11c \ubcc0\uacbd\ud560 \uc218 \uc788\ub2e4. "),(0,i.kt)("h3",{id:"\uba85\ub839\uc5b4"},"\uba85\ub839\uc5b4"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"nginx -t")),(0,i.kt)("p",null,"nginx \uc124\uc815\uc774 \uc815\uc0c1\uc778\uc9c0 \ud655\uc778\ud55c\ub2e4. "),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"nginx -T")," "),(0,i.kt)("p",null,"nginx \uc124\uc815 \ud655\uc778\uc758 \uacb0\uacfc\ub97c \uc870\uae08 \ub354 \uc790\uc138\ud558\uac8c \ucd9c\ub825\ud574\uc900\ub2e4. "),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"nginx -s ")),(0,i.kt)("p",null,"\uc5ec\uae30\uc11c SIGNAL\uc740 \ub2e4\uc74c \uc911 \ud558\ub098\ub97c \uc120\ud0dd\ud560 \uc218 \uc788\ub2e4."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"quit: \uc815\uc0c1\uc801\uc73c\ub85c \uc885\ub8cc(SIGQUIT)"),(0,i.kt)("li",{parentName:"ul"},"reload: \uc124\uc815 \ud30c\uc77c \ub9ac\ub85c\ub4dc(SIGHUP)"),(0,i.kt)("li",{parentName:"ul"},"reopen: \ub85c\uadf8 \ud30c\uc77c\uc744 \ub2e4\uc2dc \uc5f4\ub3c4\ub85d \uc694\uccad(SIGUSR1)"),(0,i.kt)("li",{parentName:"ul"},"stop: \uc885\ub8cc \uc694\uccad(SIGTERM)")),(0,i.kt)("p",null,"\uc5ec\uae30\uc11c SIGQUIT & SIGTREM \ubaa8\ub450 graceful shutdown\uc744 \uc218\ud589\ud55c\ub2e4."),(0,i.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,i.kt)("p",null,"NGINX \ucfe1\ubd81, \ub370\ub9ad \ub514\uc6a9\uae30 p.22 ~ p.23",(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("a",{parentName:"p",href:"https://docs.nginx.com/"},"https://docs.nginx.com/"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("a",{parentName:"p",href:"https://docs.nginx.com/nginx/admin-guide/monitoring/logging/"},"https://docs.nginx.com/nginx/admin-guide/monitoring/logging/")))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3438],{3905:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var r=t(67294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function l(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var p=r.createContext({}),c=function(e){var n=r.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},u=function(e){var n=c(e.components);return r.createElement(p.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},s=r.forwardRef((function(e,n){var t=e.components,i=e.mdxType,a=e.originalType,p=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),s=c(t),d=i,g=s["".concat(p,".").concat(d)]||s[d]||m[d]||a;return t?r.createElement(g,l(l({ref:n},u),{},{components:t})):r.createElement(g,l({ref:n},u))}));function d(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var a=t.length,l=new Array(a);l[0]=s;var o={};for(var p in n)hasOwnProperty.call(n,p)&&(o[p]=n[p]);o.originalType=e,o.mdxType="string"==typeof e?e:i,l[1]=o;for(var c=2;c{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>l,default:()=>m,frontMatter:()=>a,metadata:()=>o,toc:()=>c});var r=t(87462),i=(t(67294),t(3905));const a={title:"\uad6c\uc870 \ubc0f \uba85\ub839\uc5b4",slug:"/nginx/command",tags:["nginx"]},l=void 0,o={unversionedId:"Nginx/\uad6c\uc870_\ubc0f_\uba85\ub839\uc5b4",id:"Nginx/\uad6c\uc870_\ubc0f_\uba85\ub839\uc5b4",title:"\uad6c\uc870 \ubc0f \uba85\ub839\uc5b4",description:"\ud3f4\ub354 \ubc0f \ud30c\uc77c",source:"@site/docs/Nginx/\uad6c\uc870_\ubc0f_\uba85\ub839\uc5b4.mdx",sourceDirName:"Nginx",slug:"/nginx/command",permalink:"/docs/nginx/command",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/Nginx/\uad6c\uc870_\ubc0f_\uba85\ub839\uc5b4.mdx",tags:[{label:"nginx",permalink:"/docs/tags/nginx"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\uad6c\uc870 \ubc0f \uba85\ub839\uc5b4",slug:"/nginx/command",tags:["nginx"]},sidebar:"tutorialSidebar",previous:{title:"\uae30\ubcf8 \ud0a4 \ub9e4\ud551",permalink:"/docs/jpa/key"},next:{title:"\uc815\uc801 \ucee8\ud150\uce20 \uc81c\uacf5",permalink:"/docs/nginx/static-file"}},p={},c=[{value:"\ud3f4\ub354 \ubc0f \ud30c\uc77c",id:"\ud3f4\ub354-\ubc0f-\ud30c\uc77c",level:3},{value:"\uba85\ub839\uc5b4",id:"\uba85\ub839\uc5b4",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:c};function m(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h3",{id:"\ud3f4\ub354-\ubc0f-\ud30c\uc77c"},"\ud3f4\ub354 \ubc0f \ud30c\uc77c"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"/etc/nginx/")),(0,i.kt)("p",null,"\uae30\ubcf8 \uc124\uc815\uc774 \uc800\uc7a5\ub41c \ub8e8\ud2b8 \ub514\ub809\ud130\ub9ac"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"/etc/nginx/nginx.conf")),(0,i.kt)("p",null,"\uae30\ubcf8 \uc124\uc815 \ud30c\uc77c\ub85c \ubaa8\ub4e0 \uc124\uc815\uc5d0 \ub300\ud55c \uc9c4\uc785\uc810",(0,i.kt)("br",{parentName:"p"}),"\n","\ub514\ub809\ud130\ub9ac\uc5d0 \uc704\uce58\ud55c \ubaa8\ub4e0 \uc124\uc815 \ud30c\uc77c\uc744 \ud3ec\ud568\ud558\ub294 \ucd5c\uc0c1\uc704 Http \ube14\ub85d\uc744 \ud3ec\ud568\ud55c\ub2e4. "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash",metastring:'title="nginx\uc5d0 \ud3ec\ud568\ub41c \uc124\uc815 \ud30c\uc77c \ud3ec\ud568 include \uad6c\ubb38"',title:'"nginx\uc5d0',"\ud3ec\ud568\ub41c":!0,"\uc124\uc815":!0,"\ud30c\uc77c":!0,"\ud3ec\ud568":!0,include:!0,'\uad6c\ubb38"':!0},"http {\n ...\n include /etc/nginx/conf.d/*.conf;\n include /etc/nginx/sites-enabled/*;\n}\n")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"/etc/nginx/conf.d/")),(0,i.kt)("p",null,"\uae30\ubcf8 HTTP \uc11c\ubc84 \uc124\uc815 \ud30c\uc77c\uc5d0 \ub300\ud55c \ub514\ub809\ud130\ub9ac",(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("inlineCode",{parentName:"p"},".conf")," \ub85c \ub05d\ub098\ub294 \uacbd\uc6b0 \uc704\uc758 include \uc124\uc815\uc5d0 \uc758\ud574 \ucd5c\uc0c1\uc704 http \ube14\ub85d\uc5d0 \ud3ec\ud568\ub41c\ub2e4.",(0,i.kt)("br",{parentName:"p"}),"\n","conf.d \ub514\ub809\ud130\ub9ac \ub300\uc2e0 site-enabled \ub514\ub809\ud130\ub9ac\uc640 symlink\ub97c \ud1b5\ud574 \uc124\uc815 \ud30c\uc77c\uc744 \uc5f0\uacb0\ud558\ub294 \ubc29\ubc95\uc740 \ub354 \uc774\uc0c1 \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294\ub2e4. "),(0,i.kt)("admonition",{title:"NIGNX \uc124\uc815",type:"note"},(0,i.kt)("p",{parentName:"admonition"},"nginx \uc124\uc815\uc758 \uacbd\uc6b0 include \uad6c\ubb38\uc744 \ud65c\uc6a9\ud574 \uad6c\uc870\ud654\ud558\uc5ec \uc124\uc815 \ud30c\uc77c\uc744 \uac04\uacb0\ud558\uac8c \uc720\uc9c0\ud558\ub294 \uac83\uc774 \uc88b\ub2e4.")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"/var/log/nginx/")),(0,i.kt)("p",null,"\uc5d4\uc9c4\uc5d1\uc2a4 \ub85c\uadf8\uac00 \uc800\uc7a5\ub418\ub294 \ub514\ub809\ud130\ub9ac\ub85c access \ub85c\uadf8\uc640 error \ub85c\uadf8\ub97c \ud655\uc778\ud560 \uc218 \uc788\ub2e4.",(0,i.kt)("br",{parentName:"p"}),"\n","\ub85c\uadf8 \ud615\uc2dd\uc758 \uacbd\uc6b0 \uc124\uc815 \ud30c\uc77c\uc758 log_format \uad6c\ubb38\uc744 \uc774\uc6a9\ud574\uc11c \ubcc0\uacbd\ud560 \uc218 \uc788\ub2e4. "),(0,i.kt)("h3",{id:"\uba85\ub839\uc5b4"},"\uba85\ub839\uc5b4"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"nginx -t")),(0,i.kt)("p",null,"nginx \uc124\uc815\uc774 \uc815\uc0c1\uc778\uc9c0 \ud655\uc778\ud55c\ub2e4. "),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"nginx -T")," "),(0,i.kt)("p",null,"nginx \uc124\uc815 \ud655\uc778\uc758 \uacb0\uacfc\ub97c \uc870\uae08 \ub354 \uc790\uc138\ud558\uac8c \ucd9c\ub825\ud574\uc900\ub2e4. "),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"nginx -s ")),(0,i.kt)("p",null,"\uc5ec\uae30\uc11c SIGNAL\uc740 \ub2e4\uc74c \uc911 \ud558\ub098\ub97c \uc120\ud0dd\ud560 \uc218 \uc788\ub2e4."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"quit: \uc815\uc0c1\uc801\uc73c\ub85c \uc885\ub8cc(SIGQUIT)"),(0,i.kt)("li",{parentName:"ul"},"reload: \uc124\uc815 \ud30c\uc77c \ub9ac\ub85c\ub4dc(SIGHUP)"),(0,i.kt)("li",{parentName:"ul"},"reopen: \ub85c\uadf8 \ud30c\uc77c\uc744 \ub2e4\uc2dc \uc5f4\ub3c4\ub85d \uc694\uccad(SIGUSR1)"),(0,i.kt)("li",{parentName:"ul"},"stop: \uc885\ub8cc \uc694\uccad(SIGTERM)")),(0,i.kt)("p",null,"\uc5ec\uae30\uc11c SIGQUIT & SIGTREM \ubaa8\ub450 graceful shutdown\uc744 \uc218\ud589\ud55c\ub2e4."),(0,i.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,i.kt)("p",null,"NGINX \ucfe1\ubd81, \ub370\ub9ad \ub514\uc6a9\uae30 p.22 ~ p.23",(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("a",{parentName:"p",href:"https://docs.nginx.com/"},"https://docs.nginx.com/"),(0,i.kt)("br",{parentName:"p"}),"\n",(0,i.kt)("a",{parentName:"p",href:"https://docs.nginx.com/nginx/admin-guide/monitoring/logging/"},"https://docs.nginx.com/nginx/admin-guide/monitoring/logging/")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/c573638f.2758b3b4.js b/assets/js/c573638f.2758b3b4.js new file mode 100644 index 000000000..b2cb0582a --- /dev/null +++ b/assets/js/c573638f.2758b3b4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[964],{28866:a=>{a.exports=JSON.parse('[{"label":"async","permalink":"/tags/async","count":2},{"label":"exception","permalink":"/tags/exception","count":1},{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse","count":13},{"label":"Retrospective","permalink":"/tags/retrospective","count":17},{"label":"performance test","permalink":"/tags/performance-test","count":1},{"label":"mysql","permalink":"/tags/mysql","count":1},{"label":"replication","permalink":"/tags/replication","count":1},{"label":"cloudwatch","permalink":"/tags/cloudwatch","count":1},{"label":"log","permalink":"/tags/log","count":1},{"label":"monitoring","permalink":"/tags/monitoring","count":1},{"label":"event","permalink":"/tags/event","count":1},{"label":"image","permalink":"/tags/image","count":3},{"label":"awt","permalink":"/tags/awt","count":2},{"label":"Python","permalink":"/tags/python","count":1},{"label":"Mockito","permalink":"/tags/mockito","count":1},{"label":"static","permalink":"/tags/static","count":1},{"label":"Java","permalink":"/tags/java","count":5},{"label":"Spring Boot","permalink":"/tags/spring-boot","count":1},{"label":"Spring","permalink":"/tags/spring","count":1},{"label":"WebSocket","permalink":"/tags/web-socket","count":1},{"label":"Documentation","permalink":"/tags/documentation","count":1},{"label":"TecoChat","permalink":"/tags/teco-chat","count":3},{"label":"Pattern","permalink":"/tags/pattern","count":1},{"label":"Composite","permalink":"/tags/composite","count":1},{"label":"DTO","permalink":"/tags/dto","count":1},{"label":"Jenkins","permalink":"/tags/jenkins","count":1},{"label":"Elastic Beanstalk","permalink":"/tags/elastic-beanstalk","count":1},{"label":"Book","permalink":"/tags/book","count":3},{"label":"DataBase","permalink":"/tags/data-base","count":3},{"label":"Lock","permalink":"/tags/lock","count":2},{"label":"InnoDB","permalink":"/tags/inno-db","count":1},{"label":"MySQL","permalink":"/tags/my-sql","count":1},{"label":"Transaction","permalink":"/tags/transaction","count":1},{"label":"Isolation","permalink":"/tags/isolation","count":1},{"label":"Test","permalink":"/tags/test","count":1},{"label":"Mock","permalink":"/tags/mock","count":1},{"label":"Class","permalink":"/tags/class","count":1},{"label":"JDBC","permalink":"/tags/jdbc","count":1},{"label":"GRASP","permalink":"/tags/grasp","count":1},{"label":"OOP","permalink":"/tags/oop","count":1},{"label":"IntelliJ","permalink":"/tags/intelli-j","count":1},{"label":"Kotlin","permalink":"/tags/kotlin","count":1},{"label":"Time","permalink":"/tags/time","count":1}]')}}]); \ No newline at end of file diff --git a/assets/js/c573638f.8593a47b.js b/assets/js/c573638f.8593a47b.js deleted file mode 100644 index 2179cc1b6..000000000 --- a/assets/js/c573638f.8593a47b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[964],{28866:a=>{a.exports=JSON.parse('[{"label":"Woowahan Techcourse","permalink":"/tags/woowahan-techcourse","count":13},{"label":"Retrospective","permalink":"/tags/retrospective","count":17},{"label":"performance test","permalink":"/tags/performance-test","count":1},{"label":"mysql","permalink":"/tags/mysql","count":1},{"label":"replication","permalink":"/tags/replication","count":1},{"label":"cloudwatch","permalink":"/tags/cloudwatch","count":1},{"label":"log","permalink":"/tags/log","count":1},{"label":"monitoring","permalink":"/tags/monitoring","count":1},{"label":"async","permalink":"/tags/async","count":1},{"label":"event","permalink":"/tags/event","count":1},{"label":"image","permalink":"/tags/image","count":3},{"label":"awt","permalink":"/tags/awt","count":2},{"label":"Python","permalink":"/tags/python","count":1},{"label":"Mockito","permalink":"/tags/mockito","count":1},{"label":"static","permalink":"/tags/static","count":1},{"label":"Java","permalink":"/tags/java","count":5},{"label":"Spring Boot","permalink":"/tags/spring-boot","count":1},{"label":"Spring","permalink":"/tags/spring","count":1},{"label":"WebSocket","permalink":"/tags/web-socket","count":1},{"label":"Documentation","permalink":"/tags/documentation","count":1},{"label":"TecoChat","permalink":"/tags/teco-chat","count":3},{"label":"Pattern","permalink":"/tags/pattern","count":1},{"label":"Composite","permalink":"/tags/composite","count":1},{"label":"DTO","permalink":"/tags/dto","count":1},{"label":"Jenkins","permalink":"/tags/jenkins","count":1},{"label":"Elastic Beanstalk","permalink":"/tags/elastic-beanstalk","count":1},{"label":"Book","permalink":"/tags/book","count":3},{"label":"DataBase","permalink":"/tags/data-base","count":3},{"label":"Lock","permalink":"/tags/lock","count":2},{"label":"InnoDB","permalink":"/tags/inno-db","count":1},{"label":"MySQL","permalink":"/tags/my-sql","count":1},{"label":"Transaction","permalink":"/tags/transaction","count":1},{"label":"Isolation","permalink":"/tags/isolation","count":1},{"label":"Test","permalink":"/tags/test","count":1},{"label":"Mock","permalink":"/tags/mock","count":1},{"label":"Class","permalink":"/tags/class","count":1},{"label":"JDBC","permalink":"/tags/jdbc","count":1},{"label":"GRASP","permalink":"/tags/grasp","count":1},{"label":"OOP","permalink":"/tags/oop","count":1},{"label":"IntelliJ","permalink":"/tags/intelli-j","count":1},{"label":"Kotlin","permalink":"/tags/kotlin","count":1},{"label":"Time","permalink":"/tags/time","count":1}]')}}]); \ No newline at end of file diff --git a/assets/js/caf1b628.3470f566.js b/assets/js/caf1b628.0cf6df3a.js similarity index 98% rename from assets/js/caf1b628.3470f566.js rename to assets/js/caf1b628.0cf6df3a.js index eb22a92bf..0b7b6f933 100644 --- a/assets/js/caf1b628.3470f566.js +++ b/assets/js/caf1b628.0cf6df3a.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7230],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),c=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=c(e.components);return r.createElement(p.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),d=c(n),m=a,b=d["".concat(p,".").concat(m)]||d[m]||s[m]||o;return n?r.createElement(b,l(l({ref:t},u),{},{components:n})):r.createElement(b,l({ref:t},u))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,l=new Array(o);l[0]=d;var i={};for(var p in t)hasOwnProperty.call(t,p)&&(i[p]=t[p]);i.originalType=e,i.mdxType="string"==typeof e?e:a,l[1]=i;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>s,frontMatter:()=>o,metadata:()=>i,toc:()=>c});var r=n(87462),a=(n(67294),n(3905));const o={title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1",slug:"/network/load-balancing",tags:["network","load balancing"]},l=void 0,i={unversionedId:"\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1",id:"\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1",title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1",description:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc774\ub780?",source:"@site/docs/\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1.mdx",sourceDirName:"\ub124\ud2b8\uc6cc\ud06c",slug:"/network/load-balancing",permalink:"/docs/network/load-balancing",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1.mdx",tags:[{label:"network",permalink:"/docs/tags/network"},{label:"load balancing",permalink:"/docs/tags/load-balancing"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1",slug:"/network/load-balancing",tags:["network","load balancing"]},sidebar:"tutorialSidebar",previous:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",permalink:"/docs/network/load-balancing-algorithm"},next:{title:"\uc0c1\uc790 \ubc16\uc73c\ub85c \ud0c8\ucd9c\ud558\uae30",permalink:"/docs/book/getting-out-of-the-box"}},p={},c=[{value:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc774\ub780?",id:"\ub85c\ub4dc-\ubc38\ub7f0\uc2f1\uc774\ub780",level:3},{value:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc758 \uc7a5\uc810",id:"\ub85c\ub4dc-\ubc38\ub7f0\uc2f1\uc758-\uc7a5\uc810",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:c};function s(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ub85c\ub4dc-\ubc38\ub7f0\uc2f1\uc774\ub780"},"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc774\ub780?"),(0,a.kt)("p",null,"\uc11c\ubc84\uac00 \ucc98\ub9ac\ud574\uc57c \ud560 \uc694\uccad\uc744 \uc5ec\ub7ec \ub300\uc758 \uc11c\ubc84\ub85c \ub098\ub204\uc5b4 \ucc98\ub9ac\ud558\ub294 \uac83\uc744 \uc758\ubbf8\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc740 \ub85c\ub4dc \ubc38\ub7f0\uc11c\ub97c \ud1b5\ud574 \uc774\ub8e8\uc5b4\uc9c4\ub2e4. "),(0,a.kt)("admonition",{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uacfc \ub85c\ub4dc \ubc38\ub7f0\uc11c",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"\ub85c\ub4dc \ubc38\ub7f0\uc2f1: \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uc9c0\uc6d0\ud558\ub294 \ub9ac\uc18c\uc2a4 \ud480 \uc804\uccb4\uc5d0 \ub124\ud2b8\uc6cc\ud06c \ud2b8\ub798\ud53d\uc744 \uade0\ub4f1\ud558\uac8c \ubc30\ud3ec\ud558\ub294 \ubc29\ubc95",(0,a.kt)("br",{parentName:"p"}),"\n","\ub85c\ub4dc \ubc38\ub7f0\uc11c: \uc0ac\uc6a9\uc790\uc640 \uc11c\ubc84 \uadf8\ub8f9 \uc0ac\uc774\uc5d0 \uc704\uce58\ud558\uba70 \ubcf4\uc774\uc9c0 \uc54a\ub294 \ucd09\uc9c4\uc790 \uc5ed\ud560\uc744 \ud558\uc5ec \ubaa8\ub4e0 \ub9ac\uc18c\uc2a4 \uc11c\ubc84\uac00 \ub3d9\uc77c\ud558\uac8c \uc0ac\uc6a9\ub418\ub3c4\ub85d \ud558\ub294 \ub514\ubc14\uc774\uc2a4")),(0,a.kt)("h3",{id:"\ub85c\ub4dc-\ubc38\ub7f0\uc2f1\uc758-\uc7a5\uc810"},"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc758 \uc7a5\uc810"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\uac00\uc6a9\uc131")),(0,a.kt)("p",null,"\uc11c\ubc84\uc5d0 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 \ub85c\ub4dc\ubc38\ub7f0\uc11c\uac00 \ud574\ub2f9 \ubb38\uc81c\ub97c \uac10\uc9c0(\ud5ec\uc2a4 \uccb4\ud06c\ub97c \ud1b5\ud574)\ud558\uc5ec \ud2b8\ub798\ud53d\uc744 \uc0ac\uc6a9 \uac00\ub2a5\ud55c \uc11c\ubc84\ub85c \ubcf4\ub0c4\uc73c\ub85c\uc368 \ub0b4\uacb0\ud568\uc131\uc744 \ub192\ud78c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub85c\ub4dc\ubc38\ub7f0\uc11c\ub97c \ud65c\uc6a9\ud55c \ubb34\uc911\ub2e8 \ubc30\ud3ec "),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\ud655\uc7a5\uc131")),(0,a.kt)("p",null,"\uc218\ud3c9 \ud655\uc7a5\uc131(Horizontal Scalability) \uc6a9\uc774",(0,a.kt)("br",{parentName:"p"}),"\n","\ud2b8\ub798\ud53d\uc774 \uc99d\uac00\ud558\ub294 \uacbd\uc6b0 AutoScaling\uc744 \ud1b5\ud574 \uc11c\ubc84\ub97c \uc790\ub3d9\uc73c\ub85c \ud655\uc7a5(Scale-out)",(0,a.kt)("br",{parentName:"p"}),"\n","\ud2b8\ub798\ud53d\uc774 \uac10\uc18c\ud558\ub294 \uacbd\uc6b0 \ucd95\uc18c(Scale-in) "),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\ubcf4\uc548")),(0,a.kt)("p",null,"\ubd84\uc0b0 \uc11c\ube44\uc2a4 \uac70\ubd80 \uacf5\uaca9(DDoS)\uc774 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 \ud574\ub2f9 \ud2b8\ub798\ud53d\uc744 \ubd80\ud558 \ubd84\uc0b0\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\uc131\ub2a5")),(0,a.kt)("p",null,"\uc11c\ubc84 \uac04 \ub85c\ub4dc\ub97c \uade0\ub4f1\ud558\uac8c \ubc30\ud3ec\ud558\uc5ec \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc758 \uc131\ub2a5\uc744 \ud5a5\uc0c1 \uc2dc\ud0a8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud074\ub77c\uc774\uc5b8\ud2b8 \uc694\uccad\uc744 \uc9c0\ub9ac\uc801\uc73c\ub85c \ub354 \uac00\uae4c\uc6b4 \uc11c\ubc84\ub85c \ub9ac\ub2e4\uc774\ub809\uc158\ud558\uc5ec \uc9c0\uc5f0 \uc2dc\uac04\uc744 \ub2e8\ucd95\uc2dc\ud0a8\ub2e4. "),(0,a.kt)("admonition",{title:"\ub0b4\uacb0\ud568\uc131(Fault Tolerance)\uacfc \ud655\uc7a5\uc131(Scalability)",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"\ub0b4\uacb0\ud568\uc131: \uc2dc\uc2a4\ud15c\uc758 \uc77c\ubd80 \uad6c\uc131 \uc694\uc18c\uac00 \uc791\ub3d9\ud558\uc9c0 \uc54a\ub354\ub77c\ub3c4 \uacc4\uc18d \uc791\ub3d9\ud560 \uc218 \uc788\ub294 \uae30\ub2a5",(0,a.kt)("br",{parentName:"p"}),"\n","\ud655\uc7a5\uc131: \ub300\uaddc\ubaa8\uc801\uc778 \uc7ac\uc124\uacc4/\uc7ac\uc124\uce58 \ub4f1\uc758 \ud544\uc694\uc5c6\uc774 \ud655\uc7a5\uc774 \uc5bc\ub9c8\ub098 \uc27d\uace0 \uac00\ub2a5\ud55c\uac00\uc5d0 \ub300\ud55c \uc6a9\uc774\uc131")),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://aws.amazon.com/ko/what-is/load-balancing/"},"load balancing, AWS")))}s.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[7230],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),c=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=c(e.components);return r.createElement(p.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),d=c(n),m=a,b=d["".concat(p,".").concat(m)]||d[m]||s[m]||o;return n?r.createElement(b,l(l({ref:t},u),{},{components:n})):r.createElement(b,l({ref:t},u))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,l=new Array(o);l[0]=d;var i={};for(var p in t)hasOwnProperty.call(t,p)&&(i[p]=t[p]);i.originalType=e,i.mdxType="string"==typeof e?e:a,l[1]=i;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>s,frontMatter:()=>o,metadata:()=>i,toc:()=>c});var r=n(87462),a=(n(67294),n(3905));const o={title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1",slug:"/network/load-balancing",tags:["network","load balancing"]},l=void 0,i={unversionedId:"\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1",id:"\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1",title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1",description:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc774\ub780?",source:"@site/docs/\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1.mdx",sourceDirName:"\ub124\ud2b8\uc6cc\ud06c",slug:"/network/load-balancing",permalink:"/docs/network/load-balancing",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1.mdx",tags:[{label:"network",permalink:"/docs/tags/network"},{label:"load balancing",permalink:"/docs/tags/load-balancing"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1",slug:"/network/load-balancing",tags:["network","load balancing"]},sidebar:"tutorialSidebar",previous:{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998",permalink:"/docs/network/load-balancing-algorithm"},next:{title:"\uc0c1\uc790 \ubc16\uc73c\ub85c \ud0c8\ucd9c\ud558\uae30",permalink:"/docs/book/getting-out-of-the-box"}},p={},c=[{value:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc774\ub780?",id:"\ub85c\ub4dc-\ubc38\ub7f0\uc2f1\uc774\ub780",level:3},{value:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc758 \uc7a5\uc810",id:"\ub85c\ub4dc-\ubc38\ub7f0\uc2f1\uc758-\uc7a5\uc810",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],u={toc:c};function s(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\ub85c\ub4dc-\ubc38\ub7f0\uc2f1\uc774\ub780"},"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc774\ub780?"),(0,a.kt)("p",null,"\uc11c\ubc84\uac00 \ucc98\ub9ac\ud574\uc57c \ud560 \uc694\uccad\uc744 \uc5ec\ub7ec \ub300\uc758 \uc11c\ubc84\ub85c \ub098\ub204\uc5b4 \ucc98\ub9ac\ud558\ub294 \uac83\uc744 \uc758\ubbf8\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc740 \ub85c\ub4dc \ubc38\ub7f0\uc11c\ub97c \ud1b5\ud574 \uc774\ub8e8\uc5b4\uc9c4\ub2e4. "),(0,a.kt)("admonition",{title:"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uacfc \ub85c\ub4dc \ubc38\ub7f0\uc11c",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"\ub85c\ub4dc \ubc38\ub7f0\uc2f1: \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uc9c0\uc6d0\ud558\ub294 \ub9ac\uc18c\uc2a4 \ud480 \uc804\uccb4\uc5d0 \ub124\ud2b8\uc6cc\ud06c \ud2b8\ub798\ud53d\uc744 \uade0\ub4f1\ud558\uac8c \ubc30\ud3ec\ud558\ub294 \ubc29\ubc95",(0,a.kt)("br",{parentName:"p"}),"\n","\ub85c\ub4dc \ubc38\ub7f0\uc11c: \uc0ac\uc6a9\uc790\uc640 \uc11c\ubc84 \uadf8\ub8f9 \uc0ac\uc774\uc5d0 \uc704\uce58\ud558\uba70 \ubcf4\uc774\uc9c0 \uc54a\ub294 \ucd09\uc9c4\uc790 \uc5ed\ud560\uc744 \ud558\uc5ec \ubaa8\ub4e0 \ub9ac\uc18c\uc2a4 \uc11c\ubc84\uac00 \ub3d9\uc77c\ud558\uac8c \uc0ac\uc6a9\ub418\ub3c4\ub85d \ud558\ub294 \ub514\ubc14\uc774\uc2a4")),(0,a.kt)("h3",{id:"\ub85c\ub4dc-\ubc38\ub7f0\uc2f1\uc758-\uc7a5\uc810"},"\ub85c\ub4dc \ubc38\ub7f0\uc2f1\uc758 \uc7a5\uc810"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\uac00\uc6a9\uc131")),(0,a.kt)("p",null,"\uc11c\ubc84\uc5d0 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 \ub85c\ub4dc\ubc38\ub7f0\uc11c\uac00 \ud574\ub2f9 \ubb38\uc81c\ub97c \uac10\uc9c0(\ud5ec\uc2a4 \uccb4\ud06c\ub97c \ud1b5\ud574)\ud558\uc5ec \ud2b8\ub798\ud53d\uc744 \uc0ac\uc6a9 \uac00\ub2a5\ud55c \uc11c\ubc84\ub85c \ubcf4\ub0c4\uc73c\ub85c\uc368 \ub0b4\uacb0\ud568\uc131\uc744 \ub192\ud78c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ub85c\ub4dc\ubc38\ub7f0\uc11c\ub97c \ud65c\uc6a9\ud55c \ubb34\uc911\ub2e8 \ubc30\ud3ec "),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\ud655\uc7a5\uc131")),(0,a.kt)("p",null,"\uc218\ud3c9 \ud655\uc7a5\uc131(Horizontal Scalability) \uc6a9\uc774",(0,a.kt)("br",{parentName:"p"}),"\n","\ud2b8\ub798\ud53d\uc774 \uc99d\uac00\ud558\ub294 \uacbd\uc6b0 AutoScaling\uc744 \ud1b5\ud574 \uc11c\ubc84\ub97c \uc790\ub3d9\uc73c\ub85c \ud655\uc7a5(Scale-out)",(0,a.kt)("br",{parentName:"p"}),"\n","\ud2b8\ub798\ud53d\uc774 \uac10\uc18c\ud558\ub294 \uacbd\uc6b0 \ucd95\uc18c(Scale-in) "),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\ubcf4\uc548")),(0,a.kt)("p",null,"\ubd84\uc0b0 \uc11c\ube44\uc2a4 \uac70\ubd80 \uacf5\uaca9(DDoS)\uc774 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 \ud574\ub2f9 \ud2b8\ub798\ud53d\uc744 \ubd80\ud558 \ubd84\uc0b0\ud560 \uc218 \uc788\ub2e4. "),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\uc131\ub2a5")),(0,a.kt)("p",null,"\uc11c\ubc84 \uac04 \ub85c\ub4dc\ub97c \uade0\ub4f1\ud558\uac8c \ubc30\ud3ec\ud558\uc5ec \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc758 \uc131\ub2a5\uc744 \ud5a5\uc0c1 \uc2dc\ud0a8\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ud074\ub77c\uc774\uc5b8\ud2b8 \uc694\uccad\uc744 \uc9c0\ub9ac\uc801\uc73c\ub85c \ub354 \uac00\uae4c\uc6b4 \uc11c\ubc84\ub85c \ub9ac\ub2e4\uc774\ub809\uc158\ud558\uc5ec \uc9c0\uc5f0 \uc2dc\uac04\uc744 \ub2e8\ucd95\uc2dc\ud0a8\ub2e4. "),(0,a.kt)("admonition",{title:"\ub0b4\uacb0\ud568\uc131(Fault Tolerance)\uacfc \ud655\uc7a5\uc131(Scalability)",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"\ub0b4\uacb0\ud568\uc131: \uc2dc\uc2a4\ud15c\uc758 \uc77c\ubd80 \uad6c\uc131 \uc694\uc18c\uac00 \uc791\ub3d9\ud558\uc9c0 \uc54a\ub354\ub77c\ub3c4 \uacc4\uc18d \uc791\ub3d9\ud560 \uc218 \uc788\ub294 \uae30\ub2a5",(0,a.kt)("br",{parentName:"p"}),"\n","\ud655\uc7a5\uc131: \ub300\uaddc\ubaa8\uc801\uc778 \uc7ac\uc124\uacc4/\uc7ac\uc124\uce58 \ub4f1\uc758 \ud544\uc694\uc5c6\uc774 \ud655\uc7a5\uc774 \uc5bc\ub9c8\ub098 \uc27d\uace0 \uac00\ub2a5\ud55c\uac00\uc5d0 \ub300\ud55c \uc6a9\uc774\uc131")),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://aws.amazon.com/ko/what-is/load-balancing/"},"load balancing, AWS")))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/cc3bdf2f.5664f42d.js b/assets/js/cc3bdf2f.5664f42d.js new file mode 100644 index 000000000..83502756c --- /dev/null +++ b/assets/js/cc3bdf2f.5664f42d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[8817],{11135:e=>{e.exports=JSON.parse('{"label":"exception","permalink":"/tags/exception","allTagsPath":"/tags","count":1}')}}]); \ No newline at end of file diff --git a/assets/js/cd68cda1.68c11170.js b/assets/js/cd68cda1.68c11170.js new file mode 100644 index 000000000..8d63de95c --- /dev/null +++ b/assets/js/cd68cda1.68c11170.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[8683],{3905:(e,n,t)=>{t.d(n,{Zo:()=>s,kt:()=>g});var r=t(67294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function c(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var i=r.createContext({}),p=function(e){var n=r.useContext(i),t=n;return e&&(t="function"==typeof e?e(n):c(c({},n),e)),t},s=function(e){var n=p(e.components);return r.createElement(i.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},d=r.forwardRef((function(e,n){var t=e.components,a=e.mdxType,o=e.originalType,i=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),d=p(t),g=a,m=d["".concat(i,".").concat(g)]||d[g]||u[g]||o;return t?r.createElement(m,c(c({ref:n},s),{},{components:t})):r.createElement(m,c({ref:n},s))}));function g(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var o=t.length,c=new Array(o);c[0]=d;var l={};for(var i in n)hasOwnProperty.call(n,i)&&(l[i]=n[i]);l.originalType=e,l.mdxType="string"==typeof e?e:a,c[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>i,contentTitle:()=>c,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var r=t(87462),a=(t(67294),t(3905));const o={title:"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac",slug:"async-exception",tags:["async","exception"]},c=void 0,l={permalink:"/async-exception",editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac.mdx",source:"@site/blog/2023-3/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac.mdx",title:"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac",description:"\uac1c\uc694",date:"2023-09-18T00:00:00.000Z",formattedDate:"2023\ub144 9\uc6d4 18\uc77c",tags:[{label:"async",permalink:"/tags/async"},{label:"exception",permalink:"/tags/exception"}],readingTime:3.15,hasTruncateMarker:!1,authors:[],frontMatter:{title:"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac",slug:"async-exception",tags:["async","exception"]},nextItem:{title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",permalink:"/tomcat-retrospective"}},i={authorsImageUrls:[]},p=[{value:"\uac1c\uc694",id:"\uac1c\uc694",level:3},{value:"\ube44\ub3d9\uae30 \uc608\uc678\ucc98\ub9ac",id:"\ube44\ub3d9\uae30-\uc608\uc678\ucc98\ub9ac",level:3},{value:"MDC \uc815\ubcf4 \uc5f0\ub3d9 \ubb38\uc81c",id:"mdc-\uc815\ubcf4-\uc5f0\ub3d9-\ubb38\uc81c",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],s={toc:p};function u(e){let{components:n,...o}=e;return(0,a.kt)("wrapper",(0,r.Z)({},s,o,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\uac1c\uc694"},"\uac1c\uc694"),(0,a.kt)("p",null,"\ud604\uc7ac \ud2b8\ub9bd\ub4dc\ub85c\uc6b0\uc758 \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uae30\ub2a5\uc740 \ube44\ub3d9\uae30\ub85c \ucc98\ub9ac\ub418\uace0 \uc788\ub2e4. \ub85c\uadf8\ub97c \ud655\uc778\ud558\ub294 \ub3c4\uc911 ",(0,a.kt)("inlineCode",{parentName:"p"},"@Async"),"\uac00 \uc801\uc6a9\ub41c \uba54\uc11c\ub4dc\uc5d0\uc11c \uc608\uc678\uac00 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 \ub85c\uadf8\uac00 \uc815\uc0c1\uc801\uc73c\ub85c \ucd9c\ub825\ub418\uc9c0 \uc54a\ub294 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud588\ub2e4. "),(0,a.kt)("p",null,"\ud655\uc778\ud574 \ubcf4\ub2c8 Spring\uc758 ",(0,a.kt)("inlineCode",{parentName:"p"},"@ControllerAdvice")," + ",(0,a.kt)("inlineCode",{parentName:"p"},"@ExceptionHandler"),"\uc758 \uacbd\uc6b0 \ub3d9\uae30 \uc608\uc678\ub9cc \ucc98\ub9ac\ud558\uace0, \ube44\ub3d9\uae30 \uc608\uc678\ub97c \ucc98\ub9ac\ud558\uc9c0 \uc54a\uc558\ub2e4. \ub530\ub77c\uc11c Spring\uc5d0\uc11c \uc9c0\uc6d0\ud574 \uc8fc\ub294 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncUncaughtExceptionHandler")," \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud574\uc11c \uc608\uc678\ub97c \ucc98\ub9ac\ud558\ub294 \ud074\ub798\uc2a4\ub97c \uc0dd\uc131\ud588\ub2e4. "),(0,a.kt)("h3",{id:"\ube44\ub3d9\uae30-\uc608\uc678\ucc98\ub9ac"},"\ube44\ub3d9\uae30 \uc608\uc678\ucc98\ub9ac"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java",metastring:"title=AsyncExceptionHandler",title:"AsyncExceptionHandler"},'@Slf4j\npublic class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {\n\n private static final String LOG_FORMAT = "[%s] %s";\n\n @Override\n public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {\n log.info(String.format(LOG_FORMAT, MDC.get(REQUEST_ID.key()), throwable.getMessage()), throwable);\n }\n}\n')),(0,a.kt)("p",null,"\ud574\ub2f9 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncExceptionHandler"),"\uc758 \uacbd\uc6b0 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncConfigurer"),"\ub97c \uad6c\ud604\ud55c Configuration \ud074\ub798\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub4f1\ub85d\ud560 \uc218 \uc788\ub2e4. ",(0,a.kt)("inlineCode",{parentName:"p"},"getAsyncUncaughtExceptionHandler")," \uba54\uc11c\ub4dc\ub97c \uc624\ubc84\ub77c\uc774\ub529\ud558\uc5ec \uc774\uc804\uc5d0 \uc0dd\uc131\ud574 \uc900 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncExceptionHandler"),"\ub97c \ubc18\ud658\ud558\ub3c4\ub85d \uc124\uc815\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc774\ub807\uac8c \uc124\uc815\ud55c\ub2e4\uba74 \uc608\uc678\uac00 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncUncaughtExceptionHandler"),"\uc758 \uad6c\ud604\uccb4\uc778 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncExceptionHandler"),"\uac00 \uc608\uc678\ub97c \uc7a1\uc544\uc11c \ucc98\ub9ac\ub97c \ud574\uc900\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java",metastring:"title=AsyncConfig",title:"AsyncConfig"},"@EnableAsync\n@Configuration\npublic class AsyncConfig implements AsyncConfigurer {\n\n @Override\n public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {\n return new AsyncExceptionHandler();\n }\n}\n")),(0,a.kt)("h3",{id:"mdc-\uc815\ubcf4-\uc5f0\ub3d9-\ubb38\uc81c"},"MDC \uc815\ubcf4 \uc5f0\ub3d9 \ubb38\uc81c"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"./mdc-null.png",src:t(81450).Z,width:"2236",height:"426"})),(0,a.kt)("p",null,"\uae30\uc874 \uc608\uc678\uac00 \ubc1c\uc0dd\ud560 \ub54c \uc2e4\ud589 \ud750\ub984\uc744 \ucd94\uc801\ud558\uae30 \uc704\ud574 MDC(Mapped Diagnostic Context)\ub97c \uc0ac\uc6a9\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ube44\ub3d9\uae30 \ucc98\ub9ac\uc758 \uacbd\uc6b0 \ubcc4\ub3c4\uc758 \uc2a4\ub808\ub4dc\uc5d0\uc11c \ub3d9\uc791\ud558\uae30 \ub54c\ubb38\uc5d0 ThreadLocal \uae30\ubc18\uc73c\ub85c \ub3d9\uc791\ud558\ub294 MDC\uc758 \uc815\ubcf4\ub97c \uc5bb\uc5b4\uc62c \uc218 \uc5c6\uc5c8\ub2e4. "),(0,a.kt)("p",null,"\uc774\ub97c \uc801\uc808\ud558\uac8c Decorator \ud074\ub798\uc2a4\ub97c \uc124\uc815\ud558\uc5ec MDC\uc758 \uc815\ubcf4\ub97c \ubcf5\uc0ac\ud574\uc11c \ub118\uaca8\uc904 \uc218 \uc788\ub2e4. "),(0,a.kt)("p",null,"\ub2e4\uc74c\uacfc \uac19\uc774 TaskDecorator\ub97c \uad6c\ud604\ud55c \ud074\ub798\uc2a4\ub97c \ud558\ub098 \uc0dd\uc131\ud558\uace0, Task\uac00 \uc2e4\ud589\ub418\uae30 \uc804 MDC\uc758 \uc815\ubcf4\ub97c \ubcf5\uc0ac\ud558\ub3c4\ub85d \uc124\uc815\ud588\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java",metastring:"title=MdcTaskDecorator",title:"MdcTaskDecorator"},"public class MdcTaskDecorator implements TaskDecorator {\n\n @Override\n public Runnable decorate(final Runnable runnable) {\n Map threadContext = MDC.getCopyOfContextMap();\n return () -> {\n MDC.setContextMap(threadContext);\n runnable.run();\n };\n }\n}\n")),(0,a.kt)("p",null,"\ud574\ub2f9 Decorator \ud074\ub798\uc2a4\ub97c \uc124\uc815 \ud30c\uc77c\uc5d0 \ub4f1\ub85d\ud574 \uc900\ub2e4."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java",metastring:"title=AsyncConfig",title:"AsyncConfig"},"@RequiredArgsConstructor\n@EnableAsync\n@Configuration\npublic class AsyncConfig implements AsyncConfigurer {\n\n private final AsyncConfigurationProperties properties;\n\n @Bean\n public ThreadPoolTaskExecutor taskExecutor() {\n ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\n executor.setCorePoolSize(properties.coreSize());\n executor.setMaxPoolSize(properties.maxSize());\n executor.setQueueCapacity(properties.queueCapacity());\n \n // highlight-next-line\n executor.setTaskDecorator(new MdcTaskDecorator());\n executor.setWaitForTasksToCompleteOnShutdown(true);\n executor.initialize();\n return executor;\n }\n\n @Override\n public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {\n return new AsyncExceptionHandler();\n }\n}\n")),(0,a.kt)("p",null,"\uc124\uc815 \ud6c4\uc5d0\ub294 \uc815\uc0c1\uc801\uc73c\ub85c MDC\uc5d0 \ub4e4\uc5b4\uac00 \uc788\ub294 UUID\uac00 \ucd9c\ub825\ub418\ub294 \uac83\uc744 \ubcfc \uc218 \uc788\ub2e4."),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"./mdc-not-null.png",src:t(97754).Z,width:"2620",height:"440"})),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://www.baeldung.com/spring-async"},"spring async, baeldung"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://stackoverflow.com/questions/61885358/async-will-not-call-by-controlleradvice-for-global-exception"},"@Async will not call by @ControllerAdvice for global exception"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://blog.gangnamunni.com/post/mdc-context-task-decorator/"},"Spring \uc758 \ub3d9\uae30, \ube44\ub3d9\uae30, \ubc30\uce58 \ucc98\ub9ac\uc2dc \ud56d\uc0c1 context \ub97c \uc720\uc9c0\ud558\uace0 \ub85c\uae45\ud558\uae30, \uac15\ub0a8\uc5b8\ub2c8")))}u.isMDXComponent=!0},97754:(e,n,t)=>{t.d(n,{Z:()=>r});const r=t.p+"assets/images/mdc-not-null-2b12c13f4f420a335c9e55dbea503f1b.png"},81450:(e,n,t)=>{t.d(n,{Z:()=>r});const r=t.p+"assets/images/mdc-null-95b3bbdce99ef36ba843986413e0421a.png"}}]); \ No newline at end of file diff --git a/assets/js/d0e4cdf1.c290633a.js b/assets/js/d0e4cdf1.421afec6.js similarity index 57% rename from assets/js/d0e4cdf1.c290633a.js rename to assets/js/d0e4cdf1.421afec6.js index 91515836a..f705b25ca 100644 --- a/assets/js/d0e4cdf1.c290633a.js +++ b/assets/js/d0e4cdf1.421afec6.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5465],{64020:e=>{e.exports=JSON.parse('{"permalink":"/page/7","page":7,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/6","nextPage":"/page/8","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5465],{64020:e=>{e.exports=JSON.parse('{"permalink":"/page/7","page":7,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/6","nextPage":"/page/8","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/d1cef389.6c21f19a.js b/assets/js/d1cef389.a646f88e.js similarity index 57% rename from assets/js/d1cef389.6c21f19a.js rename to assets/js/d1cef389.a646f88e.js index 22b5da720..b95664795 100644 --- a/assets/js/d1cef389.6c21f19a.js +++ b/assets/js/d1cef389.a646f88e.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9310],{40836:e=>{e.exports=JSON.parse('{"permalink":"/page/17","page":17,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/16","nextPage":"/page/18","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9310],{40836:e=>{e.exports=JSON.parse('{"permalink":"/page/17","page":17,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/16","nextPage":"/page/18","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/d2611248.4b920029.js b/assets/js/d2611248.928af5e7.js similarity index 57% rename from assets/js/d2611248.4b920029.js rename to assets/js/d2611248.928af5e7.js index 8eb3b0712..ce1e3a285 100644 --- a/assets/js/d2611248.4b920029.js +++ b/assets/js/d2611248.928af5e7.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[8561],{81667:e=>{e.exports=JSON.parse('{"permalink":"/page/43","page":43,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/42","nextPage":"/page/44","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[8561],{81667:e=>{e.exports=JSON.parse('{"permalink":"/page/43","page":43,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/42","nextPage":"/page/44","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/d361ad2d.2e82e064.js b/assets/js/d361ad2d.f08f5569.js similarity index 97% rename from assets/js/d361ad2d.2e82e064.js rename to assets/js/d361ad2d.f08f5569.js index d273f7e6f..971f29e12 100644 --- a/assets/js/d361ad2d.2e82e064.js +++ b/assets/js/d361ad2d.f08f5569.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2247],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>g});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function p(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function u(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var p=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var i=n.createContext({}),l=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):u(u({},t),e)),r},c=function(e){var t=l(e.components);return n.createElement(i.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},h=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,p=e.originalType,i=e.parentName,c=a(e,["components","mdxType","originalType","parentName"]),h=l(r),g=o,m=h["".concat(i,".").concat(g)]||h[g]||s[g]||p;return r?n.createElement(m,u(u({ref:t},c),{},{components:r})):n.createElement(m,u({ref:t},c))}));function g(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var p=r.length,u=new Array(p);u[0]=h;var a={};for(var i in t)hasOwnProperty.call(t,i)&&(a[i]=t[i]);a.originalType=e,a.mdxType="string"==typeof e?e:o,u[1]=a;for(var l=2;l{r.r(t),r.d(t,{assets:()=>i,contentTitle:()=>u,default:()=>s,frontMatter:()=>p,metadata:()=>a,toc:()=>l});var n=r(87462),o=(r(67294),r(3905));const p={title:"Throughput \ubaa9\ud46f\uac12",slug:"/performance/throughput",tags:["throughput"]},u=void 0,a={unversionedId:"\uc131\ub2a5/Throughput \ubaa9\ud46f\uac12",id:"\uc131\ub2a5/Throughput \ubaa9\ud46f\uac12",title:"Throughput \ubaa9\ud46f\uac12",description:"Throughput",source:"@site/docs/\uc131\ub2a5/Throughput \ubaa9\ud46f\uac12.mdx",sourceDirName:"\uc131\ub2a5",slug:"/performance/throughput",permalink:"/docs/performance/throughput",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uc131\ub2a5/Throughput \ubaa9\ud46f\uac12.mdx",tags:[{label:"throughput",permalink:"/docs/tags/throughput"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"Throughput \ubaa9\ud46f\uac12",slug:"/performance/throughput",tags:["throughput"]},sidebar:"tutorialSidebar",previous:{title:"\ud328\ud0a4\uc9c0",permalink:"/docs/design/package"},next:{title:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",permalink:"/docs/performance/throughput-latency"}},i={},l=[{value:"Throughput",id:"throughput",level:3},{value:"\uacc4\uc0b0 \uc608\uc2dc",id:"\uacc4\uc0b0-\uc608\uc2dc",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],c={toc:l};function s(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h3",{id:"throughput"},"Throughput"),(0,o.kt)("p",null,"1\uc77c \uc0ac\uc6a9\uc790 \uc218, 1\uba85\ub2f9 1\uc77c \ud3c9\uade0 \uc811\uc18d \uc218, 1\uc77c \ud3c9\uade0 \uc811\uc18d \uc218\uc5d0 \ub300\ud55c \ud53c\ud06c \ub54c\uc758 \ubc30\uc728\uc744 \ud655\uc778\ud55c\ub2e4. "),(0,o.kt)("admonition",{title:"throughput",type:"note"},(0,o.kt)("p",{parentName:"admonition"},"1\uc77c \ucd1d \uc811\uc18d \uc218: 1\uc77c \uc0ac\uc6a9\uc790 \uc218 x 1\uc778\ub2f9 1\uc77c \ud3c9\uade0 \uc811\uc18d \uc218",(0,o.kt)("br",{parentName:"p"}),"\n","1\uc77c \ud3c9\uade0 rps: 1\uc77c \ucd1d \uc811\uc18d \uc218 / 86400",(0,o.kt)("br",{parentName:"p"}),"\n","\ucd5c\ub300 rps: 1\uc77c \ud3c9\uade0 rps x \ucd5c\ub300 \ud53c\ud06c \ub54c\uc758 \ube44\uc728(\ud3c9\uade0 \uc811\uc18d \uc218 \ub300\ube44)")),(0,o.kt)("p",null,"\ucd5c\ub300 rps * \uc548\uc804\uacc4\uc218(2 ~ 3\ubc30)\ub97c Throughput\uc758 \ubaa9\ud45c\uce58\ub85c \ud55c\ub2e4. "),(0,o.kt)("h3",{id:"\uacc4\uc0b0-\uc608\uc2dc"},"\uacc4\uc0b0 \uc608\uc2dc"),(0,o.kt)("p",null,"DAU: 10\ub9cc\uba85",(0,o.kt)("br",{parentName:"p"}),"\n","1\uba85\ub2f9 \ud3c9\uade0 \uc811\uc18d\uc218: 10\ud68c",(0,o.kt)("br",{parentName:"p"}),"\n","\ud3c9\uade0 \uc811\uc18d \uc218 \ub300\ube44 \ucd5c\ub300 \ud53c\ud06c \ube44\uc728: 4\ubc30",(0,o.kt)("br",{parentName:"p"}),"\n","\uc548\uc804\uacc4\uc218: 3\ubc30 "),(0,o.kt)("p",null,"100000 x 10 / 86400 x 4 x 3 -> \uc57d 138rps "),(0,o.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,o.kt)("p",null,"\uc544\ub9c8\uc874 \uc6f9 \uc11c\ube44\uc2a4 \ubd80\ud558 \ud14c\uc2a4\ud2b8 \uc785\ubb38 - \ub098\uce74\uac00\uc640 \ud0c0\ub8e8\ud558\uce58, \ubaa8\ub9ac\uc2dc\ud0c0 \ucf04"))}s.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2247],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>g});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function p(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function u(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var p=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var i=n.createContext({}),l=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):u(u({},t),e)),r},c=function(e){var t=l(e.components);return n.createElement(i.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},h=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,p=e.originalType,i=e.parentName,c=a(e,["components","mdxType","originalType","parentName"]),h=l(r),g=o,m=h["".concat(i,".").concat(g)]||h[g]||s[g]||p;return r?n.createElement(m,u(u({ref:t},c),{},{components:r})):n.createElement(m,u({ref:t},c))}));function g(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var p=r.length,u=new Array(p);u[0]=h;var a={};for(var i in t)hasOwnProperty.call(t,i)&&(a[i]=t[i]);a.originalType=e,a.mdxType="string"==typeof e?e:o,u[1]=a;for(var l=2;l{r.r(t),r.d(t,{assets:()=>i,contentTitle:()=>u,default:()=>s,frontMatter:()=>p,metadata:()=>a,toc:()=>l});var n=r(87462),o=(r(67294),r(3905));const p={title:"Throughput \ubaa9\ud46f\uac12",slug:"/performance/throughput",tags:["throughput"]},u=void 0,a={unversionedId:"\uc131\ub2a5/Throughput \ubaa9\ud46f\uac12",id:"\uc131\ub2a5/Throughput \ubaa9\ud46f\uac12",title:"Throughput \ubaa9\ud46f\uac12",description:"Throughput",source:"@site/docs/\uc131\ub2a5/Throughput \ubaa9\ud46f\uac12.mdx",sourceDirName:"\uc131\ub2a5",slug:"/performance/throughput",permalink:"/docs/performance/throughput",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uc131\ub2a5/Throughput \ubaa9\ud46f\uac12.mdx",tags:[{label:"throughput",permalink:"/docs/tags/throughput"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"Throughput \ubaa9\ud46f\uac12",slug:"/performance/throughput",tags:["throughput"]},sidebar:"tutorialSidebar",previous:{title:"\ud328\ud0a4\uc9c0",permalink:"/docs/design/package"},next:{title:"\uc2dc\uc2a4\ud15c \uc131\ub2a5 \uc9c0\ud45c",permalink:"/docs/performance/throughput-latency"}},i={},l=[{value:"Throughput",id:"throughput",level:3},{value:"\uacc4\uc0b0 \uc608\uc2dc",id:"\uacc4\uc0b0-\uc608\uc2dc",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],c={toc:l};function s(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h3",{id:"throughput"},"Throughput"),(0,o.kt)("p",null,"1\uc77c \uc0ac\uc6a9\uc790 \uc218, 1\uba85\ub2f9 1\uc77c \ud3c9\uade0 \uc811\uc18d \uc218, 1\uc77c \ud3c9\uade0 \uc811\uc18d \uc218\uc5d0 \ub300\ud55c \ud53c\ud06c \ub54c\uc758 \ubc30\uc728\uc744 \ud655\uc778\ud55c\ub2e4. "),(0,o.kt)("admonition",{title:"throughput",type:"note"},(0,o.kt)("p",{parentName:"admonition"},"1\uc77c \ucd1d \uc811\uc18d \uc218: 1\uc77c \uc0ac\uc6a9\uc790 \uc218 x 1\uc778\ub2f9 1\uc77c \ud3c9\uade0 \uc811\uc18d \uc218",(0,o.kt)("br",{parentName:"p"}),"\n","1\uc77c \ud3c9\uade0 rps: 1\uc77c \ucd1d \uc811\uc18d \uc218 / 86400",(0,o.kt)("br",{parentName:"p"}),"\n","\ucd5c\ub300 rps: 1\uc77c \ud3c9\uade0 rps x \ucd5c\ub300 \ud53c\ud06c \ub54c\uc758 \ube44\uc728(\ud3c9\uade0 \uc811\uc18d \uc218 \ub300\ube44)")),(0,o.kt)("p",null,"\ucd5c\ub300 rps * \uc548\uc804\uacc4\uc218(2 ~ 3\ubc30)\ub97c Throughput\uc758 \ubaa9\ud45c\uce58\ub85c \ud55c\ub2e4. "),(0,o.kt)("h3",{id:"\uacc4\uc0b0-\uc608\uc2dc"},"\uacc4\uc0b0 \uc608\uc2dc"),(0,o.kt)("p",null,"DAU: 10\ub9cc\uba85",(0,o.kt)("br",{parentName:"p"}),"\n","1\uba85\ub2f9 \ud3c9\uade0 \uc811\uc18d\uc218: 10\ud68c",(0,o.kt)("br",{parentName:"p"}),"\n","\ud3c9\uade0 \uc811\uc18d \uc218 \ub300\ube44 \ucd5c\ub300 \ud53c\ud06c \ube44\uc728: 4\ubc30",(0,o.kt)("br",{parentName:"p"}),"\n","\uc548\uc804\uacc4\uc218: 3\ubc30 "),(0,o.kt)("p",null,"100000 x 10 / 86400 x 4 x 3 -> \uc57d 138rps "),(0,o.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,o.kt)("p",null,"\uc544\ub9c8\uc874 \uc6f9 \uc11c\ube44\uc2a4 \ubd80\ud558 \ud14c\uc2a4\ud2b8 \uc785\ubb38 - \ub098\uce74\uac00\uc640 \ud0c0\ub8e8\ud558\uce58, \ubaa8\ub9ac\uc2dc\ud0c0 \ucf04"))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/d50fd269.00a8a343.js b/assets/js/d50fd269.9ae96e63.js similarity index 57% rename from assets/js/d50fd269.00a8a343.js rename to assets/js/d50fd269.9ae96e63.js index f24174821..70b3a9212 100644 --- a/assets/js/d50fd269.00a8a343.js +++ b/assets/js/d50fd269.9ae96e63.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[100],{38132:e=>{e.exports=JSON.parse('{"permalink":"/page/31","page":31,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/30","nextPage":"/page/32","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[100],{38132:e=>{e.exports=JSON.parse('{"permalink":"/page/31","page":31,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/30","nextPage":"/page/32","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/d86f7a37.b39dc39a.js b/assets/js/d86f7a37.bbe448b1.js similarity index 97% rename from assets/js/d86f7a37.b39dc39a.js rename to assets/js/d86f7a37.bbe448b1.js index e8f81e001..407bd6203 100644 --- a/assets/js/d86f7a37.b39dc39a.js +++ b/assets/js/d86f7a37.bbe448b1.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3392],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),c=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},g=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),g=c(n),m=a,f=g["".concat(s,".").concat(m)]||g[m]||u[m]||i;return n?r.createElement(f,o(o({ref:t},p),{},{components:n})):r.createElement(f,o({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=g;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var r=n(87462),a=(n(67294),n(3905));const i={title:"\uc815\uc801 \ucee8\ud150\uce20 \uc81c\uacf5",slug:"/nginx/static-file",tags:["nginx"]},o=void 0,l={unversionedId:"Nginx/\uc815\uc801_\ucee8\ud150\uce20_\uc81c\uacf5",id:"Nginx/\uc815\uc801_\ucee8\ud150\uce20_\uc81c\uacf5",title:"\uc815\uc801 \ucee8\ud150\uce20 \uc81c\uacf5",description:"root",source:"@site/docs/Nginx/\uc815\uc801_\ucee8\ud150\uce20_\uc81c\uacf5.mdx",sourceDirName:"Nginx",slug:"/nginx/static-file",permalink:"/docs/nginx/static-file",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/Nginx/\uc815\uc801_\ucee8\ud150\uce20_\uc81c\uacf5.mdx",tags:[{label:"nginx",permalink:"/docs/tags/nginx"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\uc815\uc801 \ucee8\ud150\uce20 \uc81c\uacf5",slug:"/nginx/static-file",tags:["nginx"]},sidebar:"tutorialSidebar",previous:{title:"\uad6c\uc870 \ubc0f \uba85\ub839\uc5b4",permalink:"/docs/nginx/command"},next:{title:"\ubb38\uc11c",permalink:"/docs/"}},s={},c=[{value:"root",id:"root",level:3},{value:"alias",id:"alias",level:3},{value:"try_files",id:"try_files",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"root"},"root"),(0,a.kt)("p",null,"\ud074\ub77c\uc774\uc5b8\ud2b8\uc5d0\uac8c \ud30c\uc77c\uc744 \uc81c\uacf5\ud560 \ub54c \uc0ac\uc6a9\ub418\ub294 \uacbd\ub85c\ub97c \uc9c0\uc815\ud558\ub294 \ub370 \uc0ac\uc6a9\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","root\uc758 \uacbd\uc6b0 locaiton\uc73c\ub85c \ub118\uc5b4\uc628 \uacbd\ub85c\ub97c root \uacbd\ub85c \ub4a4\uc5d0 \ucd94\uac00\ud55c\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash",metastring:"title=root",title:"root"},"# localhost/images/1.png \ud638\ucd9c /var/www/images/images/1.png \uac80\uc0c9\nlocation /images/ {\n root /var/www/images;\n}\n")),(0,a.kt)("h3",{id:"alias"},"alias"),(0,a.kt)("p",null,"location\uc73c\ub85c \ub9e4\uce6d\ub41c \ubd80\ubd84\uc744 \uc81c\uac70\ud55c\ub2e4."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash",metastring:"title=alias",title:"alias"},"# localhost/images/1.png \ud638\ucd9c /var/www/images/1.png \uac80\uc0c9\nlocation /images/ {\n alias /var/www/images;\n}\n")),(0,a.kt)("h3",{id:"try_files"},"try_files"),(0,a.kt)("p",null,"try_files \ub514\ub809\ud2f0\ube0c\ub97c \uc774\uc6a9\ud574\uc11c \ud30c\uc77c\uc774 \uc874\uc7ac\ud558\uc9c0 \uc54a\uc73c\uba74 \uc801\uc808\ud55c \uac12\uc744 \ubc18\ud658\ud560 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc124\uc815\ud558\uc9c0 \uc54a\uc73c\uba74 \uae30\ubcf8\uc73c\ub85c 404\ub97c \ubc18\ud658\ud55c\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"location /images/ {\n alias /var/www/images;\n try_files $uri $uri/ =404;\n}\n")),(0,a.kt)("p",null,"\ub2e4\uc74c\uacfc \uac19\uc774 proxy \uc124\uc815\uc73c\ub85c\ub3c4 \uad6c\uc131\ud560 \uc218\ub3c4 \uc788\ub2e4."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"location /images/ {\n root /root;\n try_files $uri $uri/ default-image;\n}\n\nlocation default-image {\n proxy_pass http://localhost/images/default_image.jpg;\n}\n")),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/"},"Serving Static Content")))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3392],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),c=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},g=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),g=c(n),m=a,f=g["".concat(s,".").concat(m)]||g[m]||u[m]||i;return n?r.createElement(f,o(o({ref:t},p),{},{components:n})):r.createElement(f,o({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=g;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var r=n(87462),a=(n(67294),n(3905));const i={title:"\uc815\uc801 \ucee8\ud150\uce20 \uc81c\uacf5",slug:"/nginx/static-file",tags:["nginx"]},o=void 0,l={unversionedId:"Nginx/\uc815\uc801_\ucee8\ud150\uce20_\uc81c\uacf5",id:"Nginx/\uc815\uc801_\ucee8\ud150\uce20_\uc81c\uacf5",title:"\uc815\uc801 \ucee8\ud150\uce20 \uc81c\uacf5",description:"root",source:"@site/docs/Nginx/\uc815\uc801_\ucee8\ud150\uce20_\uc81c\uacf5.mdx",sourceDirName:"Nginx",slug:"/nginx/static-file",permalink:"/docs/nginx/static-file",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/Nginx/\uc815\uc801_\ucee8\ud150\uce20_\uc81c\uacf5.mdx",tags:[{label:"nginx",permalink:"/docs/tags/nginx"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\uc815\uc801 \ucee8\ud150\uce20 \uc81c\uacf5",slug:"/nginx/static-file",tags:["nginx"]},sidebar:"tutorialSidebar",previous:{title:"\uad6c\uc870 \ubc0f \uba85\ub839\uc5b4",permalink:"/docs/nginx/command"},next:{title:"\ubb38\uc11c",permalink:"/docs/"}},s={},c=[{value:"root",id:"root",level:3},{value:"alias",id:"alias",level:3},{value:"try_files",id:"try_files",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"root"},"root"),(0,a.kt)("p",null,"\ud074\ub77c\uc774\uc5b8\ud2b8\uc5d0\uac8c \ud30c\uc77c\uc744 \uc81c\uacf5\ud560 \ub54c \uc0ac\uc6a9\ub418\ub294 \uacbd\ub85c\ub97c \uc9c0\uc815\ud558\ub294 \ub370 \uc0ac\uc6a9\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","root\uc758 \uacbd\uc6b0 locaiton\uc73c\ub85c \ub118\uc5b4\uc628 \uacbd\ub85c\ub97c root \uacbd\ub85c \ub4a4\uc5d0 \ucd94\uac00\ud55c\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash",metastring:"title=root",title:"root"},"# localhost/images/1.png \ud638\ucd9c /var/www/images/images/1.png \uac80\uc0c9\nlocation /images/ {\n root /var/www/images;\n}\n")),(0,a.kt)("h3",{id:"alias"},"alias"),(0,a.kt)("p",null,"location\uc73c\ub85c \ub9e4\uce6d\ub41c \ubd80\ubd84\uc744 \uc81c\uac70\ud55c\ub2e4."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash",metastring:"title=alias",title:"alias"},"# localhost/images/1.png \ud638\ucd9c /var/www/images/1.png \uac80\uc0c9\nlocation /images/ {\n alias /var/www/images;\n}\n")),(0,a.kt)("h3",{id:"try_files"},"try_files"),(0,a.kt)("p",null,"try_files \ub514\ub809\ud2f0\ube0c\ub97c \uc774\uc6a9\ud574\uc11c \ud30c\uc77c\uc774 \uc874\uc7ac\ud558\uc9c0 \uc54a\uc73c\uba74 \uc801\uc808\ud55c \uac12\uc744 \ubc18\ud658\ud560 \uc218 \uc788\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc124\uc815\ud558\uc9c0 \uc54a\uc73c\uba74 \uae30\ubcf8\uc73c\ub85c 404\ub97c \ubc18\ud658\ud55c\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"location /images/ {\n alias /var/www/images;\n try_files $uri $uri/ =404;\n}\n")),(0,a.kt)("p",null,"\ub2e4\uc74c\uacfc \uac19\uc774 proxy \uc124\uc815\uc73c\ub85c\ub3c4 \uad6c\uc131\ud560 \uc218\ub3c4 \uc788\ub2e4."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"location /images/ {\n root /root;\n try_files $uri $uri/ default-image;\n}\n\nlocation default-image {\n proxy_pass http://localhost/images/default_image.jpg;\n}\n")),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/"},"Serving Static Content")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/d8cdf5ef.fe2623c5.js b/assets/js/d8cdf5ef.4c076bff.js similarity index 98% rename from assets/js/d8cdf5ef.fe2623c5.js rename to assets/js/d8cdf5ef.4c076bff.js index 009fc477a..675398240 100644 --- a/assets/js/d8cdf5ef.fe2623c5.js +++ b/assets/js/d8cdf5ef.4c076bff.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5919],{3905:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>d});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var p=n.createContext({}),l=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},s=function(e){var t=l(e.components);return n.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),m=l(r),d=a,f=m["".concat(p,".").concat(d)]||m[d]||u[d]||o;return r?n.createElement(f,i(i({ref:t},s),{},{components:r})):n.createElement(f,i({ref:t},s))}));function d(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,i=new Array(o);i[0]=m;var c={};for(var p in t)hasOwnProperty.call(t,p)&&(c[p]=t[p]);c.originalType=e,c.mdxType="string"==typeof e?e:a,i[1]=c;for(var l=2;l{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var n=r(87462),a=(r(67294),r(3905));const o={title:"\uacbd\ud5d8\uacfc \uc9c8\ubb38",slug:"/etc/experience-and-self-question",tags:["etc"]},i=void 0,c={unversionedId:"\uae30\ud0c0/\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8",id:"\uae30\ud0c0/\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8",title:"\uacbd\ud5d8\uacfc \uc9c8\ubb38",description:"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 2023\ub144 10\uc6d4 6\uc77c",source:"@site/docs/\uae30\ud0c0/\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8.mdx",sourceDirName:"\uae30\ud0c0",slug:"/etc/experience-and-self-question",permalink:"/docs/etc/experience-and-self-question",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uae30\ud0c0/\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8.mdx",tags:[{label:"etc",permalink:"/docs/tags/etc"}],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{title:"\uacbd\ud5d8\uacfc \uc9c8\ubb38",slug:"/etc/experience-and-self-question",tags:["etc"]},sidebar:"tutorialSidebar",previous:{title:"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",permalink:"/docs/etc/develop-with-spring"},next:{title:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",permalink:"/docs/etc/communication"}},p={},l=[{value:"\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8",id:"\uc790\uae30-\uc8fc\ub3c4\uc801\uc73c\ub85c-\ubb38\uc81c\ub97c-\ud574\uacb0\ud558\uae30-\uc704\ud55c-\ub3c4\uc804-\uacbd\ud5d8",level:3},{value:"\uc790\uc2e0\uc5d0\uac8c \ub358\uc838\ubd10\uc57c\ud560 \uc9c8\ubb38",id:"\uc790\uc2e0\uc5d0\uac8c-\ub358\uc838\ubd10\uc57c\ud560-\uc9c8\ubb38",level:3}],s={toc:l};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},s,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 2023\ub144 10\uc6d4 6\uc77c",(0,a.kt)("br",{parentName:"p"}),"\n","\ud3ec\ube44 \ud2b9\uac15"),(0,a.kt)("h3",{id:"\uc790\uae30-\uc8fc\ub3c4\uc801\uc73c\ub85c-\ubb38\uc81c\ub97c-\ud574\uacb0\ud558\uae30-\uc704\ud55c-\ub3c4\uc804-\uacbd\ud5d8"},"\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8"),(0,a.kt)("p",null,"\ud300 \ud504\ub85c\uc81d\ud2b8\uc5d0\uc11c \ubc1c\uc0dd\ud55c \ud300\uc6d0 \uac04\uc758 \uac08\ub4f1\uc744 \ud574\uacb0\ud558\uae30 \uc704\ud574 \ub3c4\uc804\ud55c \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\ud300 \ud504\ub85c\uc81d\ud2b8\uc5d0\uc11c \uc2e4 \uc0ac\uc6a9\uc790\ub97c \ubaa8\uc9d1\ud558\uae30 \uc704\ud574 \ub3c4\uc804\ud55c \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\ud300\uc5d0\uc11c \uad00\uc2ec\uc744 \uac00\uc9c0\uc9c0 \uc54a\ub294 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud574 \ub05d\uae4c\uc9c0 \ub3c4\uc804\ud55c \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\ub0b4\uac00 \ub04c\ub9ac\ub294 \uc8fc\uc81c\uc5d0 \ub300\ud574 \uae4a\uc774 \uc788\uac8c \ud559\uc2b5\ud558\uace0 \ud504\ub85c\uc81d\ud2b8\uc5d0 \uc801\uc6a9\ud55c \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0ac\uc6a9\uc790 \uacbd\ud5d8\uc744 \ud55c \ub2e8\uacc4 \ub354 \ub192\uc774\uae30 \uc704\ud574 \ub3c4\uc804\ud55c \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2f9\uc5f0\ud558\ub2e4 \uc0dd\uac01\ud558\ub294 \ud574\uacb0\ucc45\uc5d0 \uc758\uad6c\uc2ec\uc744 \uac00\uc9c0\uace0 \uc0c8\ub85c\uc6b4 \uc811\uadfc \ubc29\uc2dd\uc73c\ub85c \ub3c4\uc804\ud55c \uacbd\ud5d8 "),(0,a.kt)("h3",{id:"\uc790\uc2e0\uc5d0\uac8c-\ub358\uc838\ubd10\uc57c\ud560-\uc9c8\ubb38"},"\uc790\uc2e0\uc5d0\uac8c \ub358\uc838\ubd10\uc57c\ud560 \uc9c8\ubb38"),(0,a.kt)("p",null,"\ub098\ub294 \ud504\ub85c\uadf8\ub798\ubc0d \uc790\uccb4\ub97c \uc990\uae30\uace0 \uc788\ub294\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\ub294 \uc65c \ud504\ub85c\uadf8\ub798\uba38\uac00 \ub418\ub824\uace0 \ud558\ub294\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\ub294 \ub098\ub2f5\uac8c \uc0b4\uace0 \uc788\ub098?",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\ub294 \uc8fc\ub3c4\uc801\uc73c\ub85c \uc0b4\uace0 \uc788\ub098?"))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[5919],{3905:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>d});var n=r(67294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var p=n.createContext({}),l=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},s=function(e){var t=l(e.components);return n.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),m=l(r),d=a,f=m["".concat(p,".").concat(d)]||m[d]||u[d]||o;return r?n.createElement(f,i(i({ref:t},s),{},{components:r})):n.createElement(f,i({ref:t},s))}));function d(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,i=new Array(o);i[0]=m;var c={};for(var p in t)hasOwnProperty.call(t,p)&&(c[p]=t[p]);c.originalType=e,c.mdxType="string"==typeof e?e:a,i[1]=c;for(var l=2;l{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var n=r(87462),a=(r(67294),r(3905));const o={title:"\uacbd\ud5d8\uacfc \uc9c8\ubb38",slug:"/etc/experience-and-self-question",tags:["etc"]},i=void 0,c={unversionedId:"\uae30\ud0c0/\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8",id:"\uae30\ud0c0/\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8",title:"\uacbd\ud5d8\uacfc \uc9c8\ubb38",description:"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 2023\ub144 10\uc6d4 6\uc77c",source:"@site/docs/\uae30\ud0c0/\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8.mdx",sourceDirName:"\uae30\ud0c0",slug:"/etc/experience-and-self-question",permalink:"/docs/etc/experience-and-self-question",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/\uae30\ud0c0/\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8.mdx",tags:[{label:"etc",permalink:"/docs/tags/etc"}],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{title:"\uacbd\ud5d8\uacfc \uc9c8\ubb38",slug:"/etc/experience-and-self-question",tags:["etc"]},sidebar:"tutorialSidebar",previous:{title:"\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30",permalink:"/docs/etc/develop-with-spring"},next:{title:"\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00",permalink:"/docs/etc/communication"}},p={},l=[{value:"\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8",id:"\uc790\uae30-\uc8fc\ub3c4\uc801\uc73c\ub85c-\ubb38\uc81c\ub97c-\ud574\uacb0\ud558\uae30-\uc704\ud55c-\ub3c4\uc804-\uacbd\ud5d8",level:3},{value:"\uc790\uc2e0\uc5d0\uac8c \ub358\uc838\ubd10\uc57c\ud560 \uc9c8\ubb38",id:"\uc790\uc2e0\uc5d0\uac8c-\ub358\uc838\ubd10\uc57c\ud560-\uc9c8\ubb38",level:3}],s={toc:l};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},s,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 2023\ub144 10\uc6d4 6\uc77c",(0,a.kt)("br",{parentName:"p"}),"\n","\ud3ec\ube44 \ud2b9\uac15"),(0,a.kt)("h3",{id:"\uc790\uae30-\uc8fc\ub3c4\uc801\uc73c\ub85c-\ubb38\uc81c\ub97c-\ud574\uacb0\ud558\uae30-\uc704\ud55c-\ub3c4\uc804-\uacbd\ud5d8"},"\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8"),(0,a.kt)("p",null,"\ud300 \ud504\ub85c\uc81d\ud2b8\uc5d0\uc11c \ubc1c\uc0dd\ud55c \ud300\uc6d0 \uac04\uc758 \uac08\ub4f1\uc744 \ud574\uacb0\ud558\uae30 \uc704\ud574 \ub3c4\uc804\ud55c \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\ud300 \ud504\ub85c\uc81d\ud2b8\uc5d0\uc11c \uc2e4 \uc0ac\uc6a9\uc790\ub97c \ubaa8\uc9d1\ud558\uae30 \uc704\ud574 \ub3c4\uc804\ud55c \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\ud300\uc5d0\uc11c \uad00\uc2ec\uc744 \uac00\uc9c0\uc9c0 \uc54a\ub294 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud574 \ub05d\uae4c\uc9c0 \ub3c4\uc804\ud55c \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\ub0b4\uac00 \ub04c\ub9ac\ub294 \uc8fc\uc81c\uc5d0 \ub300\ud574 \uae4a\uc774 \uc788\uac8c \ud559\uc2b5\ud558\uace0 \ud504\ub85c\uc81d\ud2b8\uc5d0 \uc801\uc6a9\ud55c \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\uc0ac\uc6a9\uc790 \uacbd\ud5d8\uc744 \ud55c \ub2e8\uacc4 \ub354 \ub192\uc774\uae30 \uc704\ud574 \ub3c4\uc804\ud55c \uacbd\ud5d8",(0,a.kt)("br",{parentName:"p"}),"\n","\ub2f9\uc5f0\ud558\ub2e4 \uc0dd\uac01\ud558\ub294 \ud574\uacb0\ucc45\uc5d0 \uc758\uad6c\uc2ec\uc744 \uac00\uc9c0\uace0 \uc0c8\ub85c\uc6b4 \uc811\uadfc \ubc29\uc2dd\uc73c\ub85c \ub3c4\uc804\ud55c \uacbd\ud5d8 "),(0,a.kt)("h3",{id:"\uc790\uc2e0\uc5d0\uac8c-\ub358\uc838\ubd10\uc57c\ud560-\uc9c8\ubb38"},"\uc790\uc2e0\uc5d0\uac8c \ub358\uc838\ubd10\uc57c\ud560 \uc9c8\ubb38"),(0,a.kt)("p",null,"\ub098\ub294 \ud504\ub85c\uadf8\ub798\ubc0d \uc790\uccb4\ub97c \uc990\uae30\uace0 \uc788\ub294\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\ub294 \uc65c \ud504\ub85c\uadf8\ub798\uba38\uac00 \ub418\ub824\uace0 \ud558\ub294\uac00?",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\ub294 \ub098\ub2f5\uac8c \uc0b4\uace0 \uc788\ub098?",(0,a.kt)("br",{parentName:"p"}),"\n","\ub098\ub294 \uc8fc\ub3c4\uc801\uc73c\ub85c \uc0b4\uace0 \uc788\ub098?"))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/e2326195.ed359df7.js b/assets/js/e2326195.ed359df7.js new file mode 100644 index 000000000..d1d78baa5 --- /dev/null +++ b/assets/js/e2326195.ed359df7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9467],{3905:(e,n,t)=>{t.d(n,{Zo:()=>s,kt:()=>g});var r=t(67294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function c(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var i=r.createContext({}),p=function(e){var n=r.useContext(i),t=n;return e&&(t="function"==typeof e?e(n):c(c({},n),e)),t},s=function(e){var n=p(e.components);return r.createElement(i.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},d=r.forwardRef((function(e,n){var t=e.components,a=e.mdxType,o=e.originalType,i=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),d=p(t),g=a,m=d["".concat(i,".").concat(g)]||d[g]||u[g]||o;return t?r.createElement(m,c(c({ref:n},s),{},{components:t})):r.createElement(m,c({ref:n},s))}));function g(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var o=t.length,c=new Array(o);c[0]=d;var l={};for(var i in n)hasOwnProperty.call(n,i)&&(l[i]=n[i]);l.originalType=e,l.mdxType="string"==typeof e?e:a,c[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>i,contentTitle:()=>c,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var r=t(87462),a=(t(67294),t(3905));const o={title:"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac",slug:"async-exception",tags:["async","exception"]},c=void 0,l={permalink:"/async-exception",editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/blog/2023-3/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac.mdx",source:"@site/blog/2023-3/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac/2023-09-18-\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac.mdx",title:"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac",description:"\uac1c\uc694",date:"2023-09-18T00:00:00.000Z",formattedDate:"2023\ub144 9\uc6d4 18\uc77c",tags:[{label:"async",permalink:"/tags/async"},{label:"exception",permalink:"/tags/exception"}],readingTime:3.15,hasTruncateMarker:!1,authors:[],frontMatter:{title:"\ube44\ub3d9\uae30 \uc608\uc678 \ucc98\ub9ac",slug:"async-exception",tags:["async","exception"]},nextItem:{title:"\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0",permalink:"/tomcat-retrospective"}},i={authorsImageUrls:[]},p=[{value:"\uac1c\uc694",id:"\uac1c\uc694",level:3},{value:"\ube44\ub3d9\uae30 \uc608\uc678\ucc98\ub9ac",id:"\ube44\ub3d9\uae30-\uc608\uc678\ucc98\ub9ac",level:3},{value:"MDC \uc815\ubcf4 \uc5f0\ub3d9 \ubb38\uc81c",id:"mdc-\uc815\ubcf4-\uc5f0\ub3d9-\ubb38\uc81c",level:3},{value:"\ucc38\uace0 \uc790\ub8cc",id:"\ucc38\uace0-\uc790\ub8cc",level:3}],s={toc:p};function u(e){let{components:n,...o}=e;return(0,a.kt)("wrapper",(0,r.Z)({},s,o,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\uac1c\uc694"},"\uac1c\uc694"),(0,a.kt)("p",null,"\ud604\uc7ac \ud2b8\ub9bd\ub4dc\ub85c\uc6b0\uc758 \uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131 \uae30\ub2a5\uc740 \ube44\ub3d9\uae30\ub85c \ucc98\ub9ac\ub418\uace0 \uc788\ub2e4. \ub85c\uadf8\ub97c \ud655\uc778\ud558\ub294 \ub3c4\uc911 ",(0,a.kt)("inlineCode",{parentName:"p"},"@Async"),"\uac00 \uc801\uc6a9\ub41c \uba54\uc11c\ub4dc\uc5d0\uc11c \uc608\uc678\uac00 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 \ub85c\uadf8\uac00 \uc815\uc0c1\uc801\uc73c\ub85c \ucd9c\ub825\ub418\uc9c0 \uc54a\ub294 \ubb38\uc81c\uac00 \ubc1c\uc0dd\ud588\ub2e4. "),(0,a.kt)("p",null,"\ud655\uc778\ud574 \ubcf4\ub2c8 Spring\uc758 ",(0,a.kt)("inlineCode",{parentName:"p"},"@ControllerAdvice")," + ",(0,a.kt)("inlineCode",{parentName:"p"},"@ExceptionHandler"),"\uc758 \uacbd\uc6b0 \ub3d9\uae30 \uc608\uc678\ub9cc \ucc98\ub9ac\ud558\uace0, \ube44\ub3d9\uae30 \uc608\uc678\ub97c \ucc98\ub9ac\ud558\uc9c0 \uc54a\uc558\ub2e4. \ub530\ub77c\uc11c Spring\uc5d0\uc11c \uc9c0\uc6d0\ud574 \uc8fc\ub294 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncUncaughtExceptionHandler")," \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud574\uc11c \uc608\uc678\ub97c \ucc98\ub9ac\ud558\ub294 \ud074\ub798\uc2a4\ub97c \uc0dd\uc131\ud588\ub2e4. "),(0,a.kt)("h3",{id:"\ube44\ub3d9\uae30-\uc608\uc678\ucc98\ub9ac"},"\ube44\ub3d9\uae30 \uc608\uc678\ucc98\ub9ac"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java",metastring:"title=AsyncExceptionHandler",title:"AsyncExceptionHandler"},'@Slf4j\npublic class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {\n\n private static final String LOG_FORMAT = "[%s] %s";\n\n @Override\n public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {\n log.info(String.format(LOG_FORMAT, MDC.get(REQUEST_ID.key()), throwable.getMessage()), throwable);\n }\n}\n')),(0,a.kt)("p",null,"\ud574\ub2f9 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncExceptionHandler"),"\uc758 \uacbd\uc6b0 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncConfigurer"),"\ub97c \uad6c\ud604\ud55c Configuration \ud074\ub798\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub4f1\ub85d\ud560 \uc218 \uc788\ub2e4. ",(0,a.kt)("inlineCode",{parentName:"p"},"getAsyncUncaughtExceptionHandler")," \uba54\uc11c\ub4dc\ub97c \uc624\ubc84\ub77c\uc774\ub529\ud558\uc5ec \uc774\uc804\uc5d0 \uc0dd\uc131\ud574 \uc900 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncExceptionHandler"),"\ub97c \ubc18\ud658\ud558\ub3c4\ub85d \uc124\uc815\ud588\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\uc774\ub807\uac8c \uc124\uc815\ud55c\ub2e4\uba74 \uc608\uc678\uac00 \ubc1c\uc0dd\ud558\ub294 \uacbd\uc6b0 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncUncaughtExceptionHandler"),"\uc758 \uad6c\ud604\uccb4\uc778 ",(0,a.kt)("inlineCode",{parentName:"p"},"AsyncExceptionHandler"),"\uac00 \uc608\uc678\ub97c \uc7a1\uc544\uc11c \ucc98\ub9ac\ub97c \ud574\uc900\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java",metastring:"title=AsyncConfig",title:"AsyncConfig"},"@EnableAsync\n@Configuration\npublic class AsyncConfig implements AsyncConfigurer {\n\n @Override\n public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {\n return new AsyncExceptionHandler();\n }\n}\n")),(0,a.kt)("h3",{id:"mdc-\uc815\ubcf4-\uc5f0\ub3d9-\ubb38\uc81c"},"MDC \uc815\ubcf4 \uc5f0\ub3d9 \ubb38\uc81c"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"./mdc-null.png",src:t(81450).Z,width:"2236",height:"426"})),(0,a.kt)("p",null,"\uae30\uc874 \uc608\uc678\uac00 \ubc1c\uc0dd\ud560 \ub54c \uc2e4\ud589 \ud750\ub984\uc744 \ucd94\uc801\ud558\uae30 \uc704\ud574 MDC(Mapped Diagnostic Context)\ub97c \uc0ac\uc6a9\ud55c\ub2e4.",(0,a.kt)("br",{parentName:"p"}),"\n","\ube44\ub3d9\uae30 \ucc98\ub9ac\uc758 \uacbd\uc6b0 \ubcc4\ub3c4\uc758 \uc2a4\ub808\ub4dc\uc5d0\uc11c \ub3d9\uc791\ud558\uae30 \ub54c\ubb38\uc5d0 ThreadLocal \uae30\ubc18\uc73c\ub85c \ub3d9\uc791\ud558\ub294 MDC\uc758 \uc815\ubcf4\ub97c \uc5bb\uc5b4\uc62c \uc218 \uc5c6\uc5c8\ub2e4. "),(0,a.kt)("p",null,"\uc774\ub97c \uc801\uc808\ud558\uac8c Decorator \ud074\ub798\uc2a4\ub97c \uc124\uc815\ud558\uc5ec MDC\uc758 \uc815\ubcf4\ub97c \ubcf5\uc0ac\ud574\uc11c \ub118\uaca8\uc904 \uc218 \uc788\ub2e4. "),(0,a.kt)("p",null,"\ub2e4\uc74c\uacfc \uac19\uc774 TaskDecorator\ub97c \uad6c\ud604\ud55c \ud074\ub798\uc2a4\ub97c \ud558\ub098 \uc0dd\uc131\ud558\uace0, Task\uac00 \uc2e4\ud589\ub418\uae30 \uc804 MDC\uc758 \uc815\ubcf4\ub97c \ubcf5\uc0ac\ud558\ub3c4\ub85d \uc124\uc815\ud588\ub2e4. "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java",metastring:"title=MdcTaskDecorator",title:"MdcTaskDecorator"},"public class MdcTaskDecorator implements TaskDecorator {\n\n @Override\n public Runnable decorate(final Runnable runnable) {\n Map threadContext = MDC.getCopyOfContextMap();\n return () -> {\n MDC.setContextMap(threadContext);\n runnable.run();\n };\n }\n}\n")),(0,a.kt)("p",null,"\ud574\ub2f9 Decorator \ud074\ub798\uc2a4\ub97c \uc124\uc815 \ud30c\uc77c\uc5d0 \ub4f1\ub85d\ud574 \uc900\ub2e4."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-java",metastring:"title=AsyncConfig",title:"AsyncConfig"},"@RequiredArgsConstructor\n@EnableAsync\n@Configuration\npublic class AsyncConfig implements AsyncConfigurer {\n\n private final AsyncConfigurationProperties properties;\n\n @Bean\n public ThreadPoolTaskExecutor taskExecutor() {\n ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\n executor.setCorePoolSize(properties.coreSize());\n executor.setMaxPoolSize(properties.maxSize());\n executor.setQueueCapacity(properties.queueCapacity());\n \n // highlight-next-line\n executor.setTaskDecorator(new MdcTaskDecorator());\n executor.setWaitForTasksToCompleteOnShutdown(true);\n executor.initialize();\n return executor;\n }\n\n @Override\n public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {\n return new AsyncExceptionHandler();\n }\n}\n")),(0,a.kt)("p",null,"\uc124\uc815 \ud6c4\uc5d0\ub294 \uc815\uc0c1\uc801\uc73c\ub85c MDC\uc5d0 \ub4e4\uc5b4\uac00 \uc788\ub294 UUID\uac00 \ucd9c\ub825\ub418\ub294 \uac83\uc744 \ubcfc \uc218 \uc788\ub2e4."),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"./mdc-not-null.png",src:t(97754).Z,width:"2620",height:"440"})),(0,a.kt)("h3",{id:"\ucc38\uace0-\uc790\ub8cc"},"\ucc38\uace0 \uc790\ub8cc"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://www.baeldung.com/spring-async"},"spring async, baeldung"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://stackoverflow.com/questions/61885358/async-will-not-call-by-controlleradvice-for-global-exception"},"@Async will not call by @ControllerAdvice for global exception"),(0,a.kt)("br",{parentName:"p"}),"\n",(0,a.kt)("a",{parentName:"p",href:"https://blog.gangnamunni.com/post/mdc-context-task-decorator/"},"Spring \uc758 \ub3d9\uae30, \ube44\ub3d9\uae30, \ubc30\uce58 \ucc98\ub9ac\uc2dc \ud56d\uc0c1 context \ub97c \uc720\uc9c0\ud558\uace0 \ub85c\uae45\ud558\uae30, \uac15\ub0a8\uc5b8\ub2c8")))}u.isMDXComponent=!0},97754:(e,n,t)=>{t.d(n,{Z:()=>r});const r=t.p+"assets/images/mdc-not-null-2b12c13f4f420a335c9e55dbea503f1b.png"},81450:(e,n,t)=>{t.d(n,{Z:()=>r});const r=t.p+"assets/images/mdc-null-95b3bbdce99ef36ba843986413e0421a.png"}}]); \ No newline at end of file diff --git a/assets/js/e4ebfe18.8b575fa3.js b/assets/js/e4ebfe18.985f0369.js similarity index 57% rename from assets/js/e4ebfe18.8b575fa3.js rename to assets/js/e4ebfe18.985f0369.js index 3add6bb87..67ad291a4 100644 --- a/assets/js/e4ebfe18.8b575fa3.js +++ b/assets/js/e4ebfe18.985f0369.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9940],{57954:e=>{e.exports=JSON.parse('{"permalink":"/page/3","page":3,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/2","nextPage":"/page/4","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9940],{57954:e=>{e.exports=JSON.parse('{"permalink":"/page/3","page":3,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/2","nextPage":"/page/4","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/eec33099.50f7c47e.js b/assets/js/eec33099.8543c571.js similarity index 57% rename from assets/js/eec33099.50f7c47e.js rename to assets/js/eec33099.8543c571.js index 22666fb8e..fde63b9be 100644 --- a/assets/js/eec33099.50f7c47e.js +++ b/assets/js/eec33099.8543c571.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[4953],{80133:e=>{e.exports=JSON.parse('{"permalink":"/page/40","page":40,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/39","nextPage":"/page/41","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[4953],{80133:e=>{e.exports=JSON.parse('{"permalink":"/page/40","page":40,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/39","nextPage":"/page/41","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/ef5b2427.9c5560c3.js b/assets/js/ef5b2427.51a774af.js similarity index 57% rename from assets/js/ef5b2427.9c5560c3.js rename to assets/js/ef5b2427.51a774af.js index f3b7f3774..afb92564d 100644 --- a/assets/js/ef5b2427.9c5560c3.js +++ b/assets/js/ef5b2427.51a774af.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9606],{50195:e=>{e.exports=JSON.parse('{"permalink":"/page/22","page":22,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/21","nextPage":"/page/23","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[9606],{50195:e=>{e.exports=JSON.parse('{"permalink":"/page/22","page":22,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/21","nextPage":"/page/23","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/f332d221.a33f27f9.js b/assets/js/f332d221.b5f875cb.js similarity index 57% rename from assets/js/f332d221.a33f27f9.js rename to assets/js/f332d221.b5f875cb.js index 1e2b95267..a7d965c57 100644 --- a/assets/js/f332d221.a33f27f9.js +++ b/assets/js/f332d221.b5f875cb.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2717],{99371:e=>{e.exports=JSON.parse('{"permalink":"/page/10","page":10,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/9","nextPage":"/page/11","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[2717],{99371:e=>{e.exports=JSON.parse('{"permalink":"/page/10","page":10,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/9","nextPage":"/page/11","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/f3e308ad.77e4f1b1.js b/assets/js/f3e308ad.4ced4843.js similarity index 57% rename from assets/js/f3e308ad.77e4f1b1.js rename to assets/js/f3e308ad.4ced4843.js index 2e5eba3ed..d1c78547f 100644 --- a/assets/js/f3e308ad.77e4f1b1.js +++ b/assets/js/f3e308ad.4ced4843.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6123],{16240:e=>{e.exports=JSON.parse('{"permalink":"/page/33","page":33,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/32","nextPage":"/page/34","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6123],{16240:e=>{e.exports=JSON.parse('{"permalink":"/page/33","page":33,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/32","nextPage":"/page/34","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/f4f49e13.f75cc00c.js b/assets/js/f4f49e13.a45671c0.js similarity index 57% rename from assets/js/f4f49e13.f75cc00c.js rename to assets/js/f4f49e13.a45671c0.js index 78d449260..1d1723edb 100644 --- a/assets/js/f4f49e13.f75cc00c.js +++ b/assets/js/f4f49e13.a45671c0.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6887],{26329:e=>{e.exports=JSON.parse('{"permalink":"/page/12","page":12,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/11","nextPage":"/page/13","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6887],{26329:e=>{e.exports=JSON.parse('{"permalink":"/page/12","page":12,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/11","nextPage":"/page/13","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/f75a8651.6bd03765.js b/assets/js/f75a8651.e89d591b.js similarity index 57% rename from assets/js/f75a8651.6bd03765.js rename to assets/js/f75a8651.e89d591b.js index a31047bb9..35d98d383 100644 --- a/assets/js/f75a8651.6bd03765.js +++ b/assets/js/f75a8651.e89d591b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[8882],{44633:e=>{e.exports=JSON.parse('{"permalink":"/page/8","page":8,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/7","nextPage":"/page/9","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[8882],{44633:e=>{e.exports=JSON.parse('{"permalink":"/page/8","page":8,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/7","nextPage":"/page/9","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/f8409a7e.e092ca80.js b/assets/js/f8409a7e.48009fa4.js similarity index 98% rename from assets/js/f8409a7e.e092ca80.js rename to assets/js/f8409a7e.48009fa4.js index e7034b49c..7a2a9fbb4 100644 --- a/assets/js/f8409a7e.e092ca80.js +++ b/assets/js/f8409a7e.48009fa4.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3206],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>d});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),p=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),f=p(r),d=o,m=f["".concat(l,".").concat(d)]||f[d]||s[d]||i;return r?n.createElement(m,a(a({ref:t},u),{},{components:r})):n.createElement(m,a({ref:t},u))}));function d(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=f;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c.mdxType="string"==typeof e?e:o,a[1]=c;for(var p=2;p{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>s,frontMatter:()=>i,metadata:()=>c,toc:()=>p});var n=r(87462),o=(r(67294),r(3905));const i={id:"intro",title:"\ubb38\uc11c",slug:"/"},a=void 0,c={unversionedId:"intro",id:"intro",title:"\ubb38\uc11c",description:"\ub9c8\uc74c\uc5d0 \ub4e0 \ud0a4\uc6cc\ub4dc \uc815\ub9ac",source:"@site/docs/intro.mdx",sourceDirName:".",slug:"/",permalink:"/docs/",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/intro.mdx",tags:[],version:"current",lastUpdatedAt:1694788431,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 15\uc77c",frontMatter:{id:"intro",title:"\ubb38\uc11c",slug:"/"},sidebar:"tutorialSidebar",previous:{title:"\uc815\uc801 \ucee8\ud150\uce20 \uc81c\uacf5",permalink:"/docs/nginx/static-file"},next:{title:"\uac74\uac15\ud558\uac8c \ub098\uc544\uc9c0\uae30",permalink:"/docs/etc/healthful-growth"}},l={},p=[],u={toc:p};function s(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"\ub9c8\uc74c\uc5d0 \ub4e0 \ud0a4\uc6cc\ub4dc \uc815\ub9ac"))}s.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[3206],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>d});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),p=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),f=p(r),d=o,m=f["".concat(l,".").concat(d)]||f[d]||s[d]||i;return r?n.createElement(m,a(a({ref:t},u),{},{components:r})):n.createElement(m,a({ref:t},u))}));function d(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=f;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c.mdxType="string"==typeof e?e:o,a[1]=c;for(var p=2;p{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>s,frontMatter:()=>i,metadata:()=>c,toc:()=>p});var n=r(87462),o=(r(67294),r(3905));const i={id:"intro",title:"\ubb38\uc11c",slug:"/"},a=void 0,c={unversionedId:"intro",id:"intro",title:"\ubb38\uc11c",description:"\ub9c8\uc74c\uc5d0 \ub4e0 \ud0a4\uc6cc\ub4dc \uc815\ub9ac",source:"@site/docs/intro.mdx",sourceDirName:".",slug:"/",permalink:"/docs/",draft:!1,editUrl:"https://github.com/greeng00se/greeng00se.github.io/tree/main/docs/intro.mdx",tags:[],version:"current",lastUpdatedAt:1695023693,formattedLastUpdatedAt:"2023\ub144 9\uc6d4 18\uc77c",frontMatter:{id:"intro",title:"\ubb38\uc11c",slug:"/"},sidebar:"tutorialSidebar",previous:{title:"\uc815\uc801 \ucee8\ud150\uce20 \uc81c\uacf5",permalink:"/docs/nginx/static-file"},next:{title:"\uac74\uac15\ud558\uac8c \ub098\uc544\uc9c0\uae30",permalink:"/docs/etc/healthful-growth"}},l={},p=[],u={toc:p};function s(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"\ub9c8\uc74c\uc5d0 \ub4e0 \ud0a4\uc6cc\ub4dc \uc815\ub9ac"))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fbd57548.5fe6f500.js b/assets/js/fbd57548.7dba7e8f.js similarity index 57% rename from assets/js/fbd57548.5fe6f500.js rename to assets/js/fbd57548.7dba7e8f.js index f0241b0dc..8bf1f8c1a 100644 --- a/assets/js/fbd57548.5fe6f500.js +++ b/assets/js/fbd57548.7dba7e8f.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6837],{30990:e=>{e.exports=JSON.parse('{"permalink":"/page/11","page":11,"postsPerPage":1,"totalPages":45,"totalCount":45,"previousPage":"/page/10","nextPage":"/page/12","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6837],{30990:e=>{e.exports=JSON.parse('{"permalink":"/page/11","page":11,"postsPerPage":1,"totalPages":46,"totalCount":46,"previousPage":"/page/10","nextPage":"/page/12","blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/main.2e13b410.js b/assets/js/main.2e13b410.js deleted file mode 100644 index e7acf56a2..000000000 --- a/assets/js/main.2e13b410.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! For license information please see main.2e13b410.js.LICENSE.txt */ -(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[179],{20830:(e,t,n)=>{"use strict";n.d(t,{W:()=>r});var a=n(67294);function r(){return a.createElement("svg",{width:"20",height:"20",className:"DocSearch-Search-Icon",viewBox:"0 0 20 20"},a.createElement("path",{d:"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z",stroke:"currentColor",fill:"none",fillRule:"evenodd",strokeLinecap:"round",strokeLinejoin:"round"}))}},723:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var a=n(67294),r=n(87462),o=n(68356),i=n.n(o),s=n(16887);const l={"00931cc3":[()=>n.e(5669).then(n.t.bind(n,92291,19)),"~blog/default/page-30-25c.json",92291],"01a85c17":[()=>Promise.all([n.e(532),n.e(4013)]).then(n.bind(n,24524)),"@theme/BlogTagsListPage",24524],"02689328":[()=>n.e(6346).then(n.t.bind(n,5577,19)),"~blog/default/tags-data-base-page-3-9db.json",5577],"0281109c":[()=>n.e(422).then(n.t.bind(n,25266,19)),"~blog/default/tags-jenkins-2e5-list.json",25266],"0462f8fc":[()=>n.e(4481).then(n.bind(n,37572)),"@site/docs/\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\ucf54\ub4dc\uac00_\uc8fc\ub294_\ud61c\ud0dd.mdx",37572],"04644f5f":[()=>n.e(2625).then(n.bind(n,73787)),"@site/docs/\uae30\ud0c0/\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00.mdx",73787],"0571a526":[()=>n.e(6204).then(n.bind(n,77397)),"@site/blog/2023-3/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8.mdx",77397],"05b907fc":[()=>n.e(734).then(n.t.bind(n,92943,19)),"~blog/default/tags-retrospective-page-8-5ab-list.json",92943],"0746167d":[()=>n.e(1113).then(n.t.bind(n,19758,19)),"~blog/default/tags-elastic-beanstalk-119-list.json",19758],"08726fcf":[()=>n.e(5487).then(n.t.bind(n,38441,19)),"~blog/default/tags-java-page-4-c22-list.json",38441],"087c46fa":[()=>n.e(96).then(n.t.bind(n,25774,19)),"~blog/default/tags-spring-boot-889.json",25774],"08e37dbc":[()=>n.e(1328).then(n.bind(n,10634)),"@site/blog/2023-2/2023-06-04-\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0.mdx?truncated=true",10634],"09fbb6bd":[()=>n.e(5964).then(n.t.bind(n,41679,19)),"~blog/default/page-16-d6c.json",41679],"0a2eaa84":[()=>n.e(8942).then(n.t.bind(n,52930,19)),"~blog/default/tags-data-base-4e8.json",52930],"0c071de2":[()=>n.e(321).then(n.t.bind(n,23125,19)),"~blog/default/page-2-b45.json",23125],"0cb009d1":[()=>n.e(116).then(n.t.bind(n,66643,19)),"~blog/default/tags-event-f04.json",66643],"0d47646f":[()=>n.e(2342).then(n.bind(n,27019)),"@site/docs/\ubaa8\ub2c8\ud130\ub9c1/\ubaa8\ub2c8\ud130\ub9c1_\ud658\uacbd_\uad6c\uc131.mdx",27019],"0e33a907":[()=>n.e(3092).then(n.bind(n,81204)),"@site/blog/2023-3/2023-08-19-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca83 \ud68c\uace0/2023-08-19-\ub808\ubca8 3 \ud68c\uace0.mdx?truncated=true",81204],"101b58de":[()=>n.e(6084).then(n.bind(n,67365)),"@site/blog/2023-1/2023-01-08-JSR-310.mdx",67365],"101cf32b":[()=>n.e(9239).then(n.t.bind(n,30880,19)),"~blog/default/page-45-7ec.json",30880],"1236fad7":[()=>n.e(6515).then(n.bind(n,25088)),"@site/blog/2023-2/2023-06-18-Docusaurus/2023-06-18-Docusaurus.mdx",25088],"1251d98b":[()=>n.e(6276).then(n.bind(n,25075)),"@site/blog/2023-1/2023-03-31-\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0.mdx?truncated=true",25075],"12cbeba7":[()=>n.e(6508).then(n.t.bind(n,16134,19)),"~blog/default/page-29-e3c.json",16134],"130df38c":[()=>n.e(6493).then(n.bind(n,46963)),"@site/blog/2023-2/2023-04-07-InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx",46963],14164549:[()=>n.e(7268).then(n.t.bind(n,11279,19)),"~blog/default/tags-book-baf-list.json",11279],"14dc1923":[()=>n.e(7403).then(n.bind(n,25731)),"@site/blog/2023-1/2023-01-30-IntelliJ \uc124\uc815.mdx/index.mdx",25731],"16cc6f3a":[()=>n.e(425).then(n.t.bind(n,12946,19)),"~blog/default/tags-retrospective-page-15-26b.json",12946],"16f719ab":[()=>n.e(9875).then(n.bind(n,83377)),"@site/blog/2023-2/2023-05-25-\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0.mdx",83377],"1781b1c4":[()=>n.e(5785).then(n.bind(n,10945)),"@site/blog/2023-2/2023-05-25-\uc9c0\ud558\ucca0 \ubbf8\uc158 \ud68c\uace0.mdx?truncated=true",10945],17896441:[()=>Promise.all([n.e(532),n.e(4659),n.e(7918)]).then(n.bind(n,78945)),"@theme/DocItem",78945],"1893cb59":[()=>n.e(286).then(n.t.bind(n,16269,19)),"~blog/default/tags-java-page-2-8c6.json",16269],"18c69d70":[()=>n.e(9171).then(n.t.bind(n,7085,19)),"/home/runner/work/greeng00se.github.io/greeng00se.github.io/.docusaurus/docusaurus-theme-search-algolia/default/plugin-route-context-module-100.json",7085],"198f8d8a":[()=>n.e(9059).then(n.t.bind(n,17238,19)),"~blog/default/tags-java-page-3-b02-list.json",17238],"19f4ae8e":[()=>n.e(8161).then(n.t.bind(n,25680,19)),"~blog/default/tags-log-5ad.json",25680],"1a3abee6":[()=>n.e(5035).then(n.t.bind(n,87753,19)),"~blog/default/tags-retrospective-page-17-636.json",87753],"1a4e3797":[()=>Promise.all([n.e(532),n.e(7920)]).then(n.bind(n,39172)),"@theme/SearchPage",39172],"1a665c6f":[()=>n.e(454).then(n.t.bind(n,28767,19)),"~blog/default/tags-test-435-list.json",28767],"1a6b9123":[()=>n.e(9874).then(n.t.bind(n,14343,19)),"~blog/default/tags-teco-chat-page-3-007.json",14343],"1bb997fc":[()=>n.e(9713).then(n.t.bind(n,66014,19)),"~blog/default/page-44-cef.json",66014],"1be78505":[()=>Promise.all([n.e(532),n.e(9514)]).then(n.bind(n,19963)),"@theme/DocPage",19963],"1c93669b":[()=>n.e(6526).then(n.t.bind(n,37579,19)),"~docs/default/tag-docs-tags-monitoring-149.json",37579],"1d81daa1":[()=>n.e(7681).then(n.t.bind(n,76725,19)),"~blog/default/tags-mock-330.json",76725],"1f05d14a":[()=>Promise.all([n.e(532),n.e(656)]).then(n.bind(n,65945)),"@site/blog/2023-1/2023-01-16-Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95.mdx?truncated=true",65945],"1f61820a":[()=>n.e(2233).then(n.bind(n,16432)),"@site/blog/2023-1/2023-02-12-Parameterized Tests.mdx?truncated=true",16432],"1fbde614":[()=>n.e(8243).then(n.t.bind(n,87304,19)),"~blog/default/tags-monitoring-a8a-list.json",87304],"20e99c2a":[()=>n.e(3530).then(n.t.bind(n,19507,19)),"~blog/default/tags-documentation-ee3-list.json",19507],"211d6170":[()=>n.e(3109).then(n.t.bind(n,41078,19)),"~docs/default/tag-docs-tags-package-d2b.json",41078],"21294bbb":[()=>n.e(9412).then(n.bind(n,86182)),"@site/blog/2023-2/2023-05-02-\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0.mdx?truncated=true",86182],"21d253a0":[()=>n.e(1853).then(n.t.bind(n,63986,19)),"~blog/default/tags-woowahan-techcourse-page-10-f03-list.json",63986],"21e890b0":[()=>n.e(8288).then(n.t.bind(n,551,19)),"~blog/default/tags-retrospective-page-14-99d-list.json",551],"226700de":[()=>n.e(6035).then(n.t.bind(n,41961,19)),"~blog/default/page-25-52d.json",41961],"24b9bc70":[()=>n.e(5798).then(n.bind(n,8518)),"@site/blog/2023-2/2023-04-04-\ud14c\uc2a4\ud2b8 \ub300\uc5ed.mdx",8518],"255134d9":[()=>n.e(8151).then(n.t.bind(n,30753,19)),"~blog/default/tags-composite-240.json",30753],"269a2f75":[()=>n.e(1994).then(n.t.bind(n,52358,19)),"~blog/default/tags-static-b68.json",52358],"26dc40bf":[()=>n.e(5237).then(n.bind(n,97783)),"@site/blog/2023-3/2023-07-24-\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1.mdx?truncated=true",97783],"270346fa":[()=>n.e(7975).then(n.t.bind(n,89424,19)),"~blog/default/page-28-907.json",89424],"274c9143":[()=>n.e(6984).then(n.t.bind(n,90058,19)),"~blog/default/tags-java-a6e.json",90058],"280572f1":[()=>n.e(324).then(n.t.bind(n,77874,19)),"~blog/default/tags-mysql-331.json",77874],"2832e534":[()=>n.e(2476).then(n.t.bind(n,69870,19)),"~blog/default/page-13-99f.json",69870],"28a1570f":[()=>n.e(448).then(n.t.bind(n,92252,19)),"~blog/default/tags-elastic-beanstalk-119.json",92252],29476979:[()=>n.e(4030).then(n.bind(n,68804)),"@site/docs/\ub9ac\ub205\uc2a4/Swap_\uba54\ubaa8\ub9ac_\uc124\uc815.md",68804],"2a8faff0":[()=>n.e(7901).then(n.t.bind(n,1150,19)),"~blog/default/tags-test-435.json",1150],"2b22d492":[()=>n.e(7652).then(n.t.bind(n,56986,19)),"~blog/default/tags-retrospective-page-3-ee4-list.json",56986],"2b479afe":[()=>n.e(9591).then(n.t.bind(n,16973,19)),"~blog/default/tags-mockito-3c0-list.json",16973],"2bfe7c0b":[()=>n.e(1762).then(n.t.bind(n,82670,19)),"~blog/default/tags-book-page-2-bc6.json",82670],"2c9f5501":[()=>n.e(7775).then(n.bind(n,73917)),"@site/blog/2023-1/2023-01-07-\uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574.mdx?truncated=true",73917],"2d3b202f":[()=>n.e(1196).then(n.t.bind(n,42524,19)),"~blog/default/tags-book-baf.json",42524],"2d9296e4":[()=>n.e(3483).then(n.t.bind(n,89429,19)),"~blog/default/tags-pattern-b4e.json",89429],"2e10a69c":[()=>n.e(7581).then(n.t.bind(n,9981,19)),"~blog/default/page-38-d34.json",9981],"2f43e44a":[()=>n.e(6743).then(n.t.bind(n,52396,19)),"~blog/default/tags-grasp-418-list.json",52396],"302370be":[()=>n.e(1883).then(n.bind(n,91867)),"@site/docs/\ud14c\uc2a4\ud2b8/FIRST.mdx",91867],"303c1e60":[()=>n.e(2656).then(n.t.bind(n,39529,19)),"~blog/default/tags-retrospective-page-4-3a3.json",39529],"309173fa":[()=>n.e(1793).then(n.t.bind(n,22684,19)),"~blog/default/tags-data-base-4e8-list.json",22684],"32397cb2":[()=>n.e(548).then(n.t.bind(n,22050,19)),"~blog/default/tags-awt-page-2-eb4-list.json",22050],"327fa616":[()=>n.e(3407).then(n.bind(n,94634)),"@site/blog/2023-1/2023-02-26-\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0.mdx",94634],"32b2299c":[()=>n.e(970).then(n.t.bind(n,5280,19)),"~blog/default/page-41-fe1.json",5280],33736670:[()=>n.e(2742).then(n.t.bind(n,80700,19)),"~blog/default/tags-class-eca.json",80700],"35293ec4":[()=>n.e(7697).then(n.t.bind(n,14,19)),"~blog/default/page-20-038.json",14],"35b2eb5a":[()=>n.e(372).then(n.t.bind(n,97815,19)),"~blog/default/tags-java-page-5-b71-list.json",97815],"366ddb85":[()=>n.e(3691).then(n.bind(n,2406)),"@site/docs/\uae30\ud0c0/\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30.mdx",2406],"3720c009":[()=>Promise.all([n.e(532),n.e(3751)]).then(n.bind(n,10727)),"@theme/DocTagsListPage",10727],"372ccfe9":[()=>n.e(9111).then(n.bind(n,50673)),"@site/blog/2023-1/2023-01-01-\uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.mdx?truncated=true",50673],"38bf29ad":[()=>n.e(1285).then(n.t.bind(n,26514,19)),"~docs/default/tag-docs-tags-load-balancing-180.json",26514],"38d8699e":[()=>n.e(471).then(n.t.bind(n,97481,19)),"~blog/default/page-15-208.json",97481],"3972c49f":[()=>n.e(6629).then(n.t.bind(n,91782,19)),"~blog/default/tags-web-socket-c6e-list.json",91782],"39ee6679":[()=>n.e(5717).then(n.t.bind(n,83636,19)),"~blog/default/tags-woowahan-techcourse-b50.json",83636],"3b0d95bc":[()=>n.e(5140).then(n.bind(n,38973)),"@site/docs/JPA/\uae30\ubcf8_\ud0a4_\ub9e4\ud551.mdx",38973],"3b0f99e8":[()=>n.e(3553).then(n.t.bind(n,20034,19)),"~blog/default/tags-jenkins-2e5.json",20034],"3b18521e":[()=>n.e(2773).then(n.t.bind(n,8086,19)),"~blog/default/tags-mockito-3c0.json",8086],"3c5aea38":[()=>n.e(6250).then(n.t.bind(n,56516,19)),"~blog/default/tags-retrospective-page-12-8cf.json",56516],"3d6c40c1":[()=>n.e(8509).then(n.t.bind(n,3440,19)),"~blog/default/tags-monitoring-a8a.json",3440],"3dd4d232":[()=>n.e(7727).then(n.bind(n,74135)),"@site/blog/2023-1/2023-03-14-\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0.mdx",74135],"3ed04b60":[()=>n.e(7157).then(n.t.bind(n,84792,19)),"~blog/default/tags-spring-de1.json",84792],"3f6ea930":[()=>n.e(5186).then(n.bind(n,42096)),"@site/blog/2023-1/2023-02-12-Parameterized Tests.mdx",42096],"3fc16fd0":[()=>n.e(3886).then(n.bind(n,42929)),"@site/docs/\ub3c4\uc11c/\uc0c1\uc790_\ubc16\uc73c\ub85c_\ud0c8\ucd9c\ud558\uae30.mdx",42929],"41b4728f":[()=>n.e(8628).then(n.t.bind(n,30171,19)),"~blog/default/tags-spring-boot-889-list.json",30171],"42957a8d":[()=>n.e(9312).then(n.bind(n,1335)),"@site/blog/2023-3/2023-07-30-Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30.mdx",1335],"43a97218":[()=>n.e(4815).then(n.t.bind(n,65215,19)),"~blog/default/tags-retrospective-page-5-22d-list.json",65215],"43fcf0e9":[()=>n.e(6468).then(n.t.bind(n,94822,19)),"~blog/default/tags-woowahan-techcourse-page-9-065.json",94822],"4485017c":[()=>n.e(1906).then(n.bind(n,23224)),"@site/blog/2023-1/2023-03-14-\ube14\ub799\uc7ad \ubbf8\uc158 \ud68c\uace0.mdx?truncated=true",23224],"454a6d0d":[()=>n.e(4104).then(n.bind(n,33482)),"@site/blog/2023-2/2023-05-01-\ud14c\ucf54\ucc57 2. \ubc30\ud3ec.mdx",33482],"459bd227":[()=>n.e(9094).then(n.bind(n,6693)),"@site/docs/\uae30\ud0c0/\uac74\uac15\ud558\uac8c_\ub098\uc544\uc9c0\uae30.mdx",6693],"489347ff":[()=>n.e(2793).then(n.t.bind(n,40526,19)),"~blog/default/tags-web-socket-c6e.json",40526],"48bd1d32":[()=>n.e(9563).then(n.bind(n,1683)),"@site/docs/\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\uc8fc\ub3c4_\uac1c\ubc1c_\uaddc\uce59.mdx",1683],"48faf148":[()=>n.e(7328).then(n.bind(n,59455)),"@site/blog/2023-2/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604.mdx?truncated=true",59455],"492a6565":[()=>n.e(4212).then(n.t.bind(n,44929,19)),"~blog/default/tags-lock-page-2-819-list.json",44929],"494882d1":[()=>n.e(4471).then(n.t.bind(n,2098,19)),"~blog/default/page-37-cb2.json",2098],"4959fc42":[()=>n.e(240).then(n.t.bind(n,80897,19)),"~blog/default/page-14-0a2.json",80897],"49b8d9dd":[()=>n.e(1103).then(n.t.bind(n,64420,19)),"~blog/default/tags-inno-db-59e.json",64420],"49f0f498":[()=>n.e(4433).then(n.t.bind(n,40719,19)),"~blog/default/tags-retrospective-page-16-226.json",40719],"4a1c8300":[()=>n.e(3324).then(n.bind(n,37750)),"@site/blog/2023-3/2023-08-19-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca83 \ud68c\uace0/2023-08-19-\ub808\ubca8 3 \ud68c\uace0.mdx",37750],"4b2fba3e":[()=>n.e(328).then(n.t.bind(n,98234,19)),"~blog/default/tags-image-page-3-942-list.json",98234],"4b79a3c9":[()=>Promise.all([n.e(532),n.e(5838)]).then(n.bind(n,60036)),"@site/blog/2023-3/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac.mdx",60036],"4d43abad":[()=>n.e(3365).then(n.bind(n,4859)),"@site/blog/2023-3/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd.mdx?truncated=true",4859],"5088fe06":[()=>n.e(80).then(n.t.bind(n,86819,19)),"~blog/default/tags-log-5ad-list.json",86819],"509d519c":[()=>n.e(743).then(n.t.bind(n,24469,19)),"/home/runner/work/greeng00se.github.io/greeng00se.github.io/.docusaurus/docusaurus-plugin-content-blog/default/plugin-route-context-module-100.json",24469],"52106a5f":[()=>n.e(9396).then(n.bind(n,63338)),"@site/blog/2023-1/2023-01-30-IntelliJ \uc124\uc815.mdx/index.mdx?truncated=true",63338],"533bfc57":[()=>n.e(5100).then(n.t.bind(n,4371,19)),"~blog/default/tags-retrospective-page-2-e2b-list.json",4371],"537817cb":[()=>n.e(3457).then(n.bind(n,49577)),"@site/blog/2023-1/2023-01-08-JSR-310.mdx?truncated=true",49577],"54150be7":[()=>n.e(5088).then(n.t.bind(n,98707,19)),"~blog/default/tags-java-page-2-8c6-list.json",98707],"546ec22f":[()=>n.e(5635).then(n.t.bind(n,34223,19)),"~blog/default/tags-performance-test-2b7.json",34223],"54cb095e":[()=>n.e(7009).then(n.t.bind(n,95159,19)),"~blog/default/page-26-a44.json",95159],"55960ee5":[()=>n.e(4121).then(n.t.bind(n,88070,19)),"~docs/default/tags-list-current-prop-15a.json",88070],"562496aa":[()=>n.e(6161).then(n.t.bind(n,68146,19)),"~blog/default/tags-image-page-2-cc3.json",68146],"564337ec":[()=>n.e(5649).then(n.t.bind(n,8563,19)),"~blog/default/tags-retrospective-page-7-3e2-list.json",8563],"56e576e5":[()=>n.e(8262).then(n.t.bind(n,86556,19)),"~blog/default/tags-performance-test-2b7-list.json",86556],"5a29fbab":[()=>n.e(7857).then(n.t.bind(n,25381,19)),"~blog/default/tags-woowahan-techcourse-b50-list.json",25381],"5a6c6934":[()=>n.e(5953).then(n.t.bind(n,48630,19)),"~blog/default/tags-dto-cb6.json",48630],"5c38e66e":[()=>n.e(5521).then(n.t.bind(n,28638,19)),"~blog/default/tags-woowahan-techcourse-page-10-f03.json",28638],"5e9f5e1a":[()=>Promise.resolve().then(n.bind(n,36809)),"@generated/docusaurus.config",36809],"5eed1665":[()=>n.e(8142).then(n.t.bind(n,19729,19)),"~blog/default/tags-lock-529-list.json",19729],"5f81b25c":[()=>n.e(4889).then(n.t.bind(n,29492,19)),"~blog/default/page-27-eb3.json",29492],"5ffd2c10":[()=>n.e(2100).then(n.t.bind(n,86515,19)),"~docs/default/tag-docs-tags-jpa-c8c.json",86515],"6093f82b":[()=>n.e(6017).then(n.t.bind(n,30708,19)),"~blog/default/page-9-361.json",30708],"633582b9":[()=>n.e(2448).then(n.t.bind(n,32401,19)),"~blog/default/tags-kotlin-6ac.json",32401],"635a92d5":[()=>n.e(7891).then(n.t.bind(n,72126,19)),"~blog/default/page-24-fbb.json",72126],"6412e40a":[()=>n.e(5421).then(n.t.bind(n,97677,19)),"~blog/default/tags-woowahan-techcourse-page-12-5ba-list.json",97677],"6425a984":[()=>n.e(5467).then(n.t.bind(n,95377,19)),"~blog/default/tags-woowahan-techcourse-page-4-bcd-list.json",95377],"64868a43":[()=>n.e(1501).then(n.t.bind(n,33159,19)),"~blog/default/page-39-76c.json",33159],"64f377d6":[()=>n.e(732).then(n.t.bind(n,62898,19)),"~blog/default/tags-woowahan-techcourse-page-11-6c9-list.json",62898],"6552f31f":[()=>n.e(917).then(n.bind(n,29693)),"@site/blog/2023-2/2023-06-08-\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0.mdx",29693],"6675e9ab":[()=>n.e(1255).then(n.bind(n,72336)),"@site/blog/2023-2/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0.mdx",72336],"66d1c769":[()=>n.e(7476).then(n.t.bind(n,80122,19)),"~blog/default/tags-data-base-page-2-3a7-list.json",80122],"672a376b":[()=>n.e(5753).then(n.t.bind(n,41690,19)),"~blog/default/tags-woowahan-techcourse-page-8-93a.json",41690],"6875c492":[()=>Promise.all([n.e(532),n.e(4659),n.e(6048),n.e(8610)]).then(n.bind(n,41714)),"@theme/BlogTagsPostsPage",41714],"69c28c32":[()=>n.e(1065).then(n.t.bind(n,99263,19)),"~blog/default/page-36-1da.json",99263],"6a19354d":[()=>n.e(693).then(n.t.bind(n,36232,19)),"~blog/default/tags-lock-529.json",36232],"6b54f6a4":[()=>Promise.all([n.e(532),n.e(4558)]).then(n.bind(n,23826)),"@site/blog/2023-2/2023-04-02-\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30.mdx?truncated=true",23826],"6bc709ad":[()=>n.e(9393).then(n.t.bind(n,81399,19)),"~blog/default/tags-retrospective-page-6-594-list.json",81399],"6c674d03":[()=>n.e(7600).then(n.t.bind(n,80372,19)),"~docs/default/tag-docs-tags-network-cb4.json",80372],"6cfe3a99":[()=>n.e(5319).then(n.t.bind(n,91227,19)),"~blog/default/tags-cloudwatch-6c7-list.json",91227],"6dd1c948":[()=>n.e(7064).then(n.t.bind(n,76376,19)),"~blog/default/page-34-16c.json",76376],"6f385a52":[()=>n.e(6608).then(n.bind(n,65468)),"@site/blog/2023-2/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5.mdx?truncated=true",65468],"70275fcd":[()=>n.e(7412).then(n.t.bind(n,81191,19)),"~blog/default/page-42-ca9.json",81191],70834889:[()=>n.e(7344).then(n.bind(n,6777)),"@site/blog/2023-3/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8/2023-09-10-\uc131\ub2a5 \ud14c\uc2a4\ud2b8.mdx?truncated=true",6777],"70a12cc4":[()=>n.e(5682).then(n.t.bind(n,17085,19)),"~blog/default/tags-static-b68-list.json",17085],"7159c7ff":[()=>n.e(3287).then(n.bind(n,92553)),"@site/blog/2023-3/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604.mdx?truncated=true",92553],"72657f57":[()=>n.e(9581).then(n.bind(n,8046)),"@site/blog/2023-2/2023-05-02-\uc6f9 \uc790\ub3d9\ucc28 \ubbf8\uc158 \ud68c\uace0.mdx",8046],"73688d5c":[()=>n.e(6908).then(n.bind(n,60641)),"@site/blog/2023-2/2023-04-05-\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900.mdx?truncated=true",60641],"7405ea58":[()=>n.e(2345).then(n.t.bind(n,40702,19)),"~blog/default/tags-retrospective-page-11-e3c-list.json",40702],"75121fd5":[()=>n.e(5335).then(n.t.bind(n,30674,19)),"~blog/default/tags-image-97d.json",30674],"754fb852":[()=>n.e(988).then(n.t.bind(n,38242,19)),"~blog/default/page-32-596.json",38242],"75f50328":[()=>n.e(7511).then(n.t.bind(n,58695,19)),"~blog/default/tags-mysql-331-list.json",58695],"7762a24e":[()=>n.e(2753).then(n.t.bind(n,55095,19)),"~blog/default/page-4-365.json",55095],"77f5fc5d":[()=>n.e(3625).then(n.t.bind(n,32215,19)),"~blog/default/tags-retrospective-page-16-226-list.json",32215],"79a97f4e":[()=>n.e(6412).then(n.bind(n,82792)),"@site/blog/2023-2/2023-06-11-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca82 \ud68c\uace0.mdx?truncated=true",82792],"7af1d52f":[()=>n.e(2334).then(n.t.bind(n,59565,19)),"~blog/default/page-6-d10.json",59565],"7bbc420e":[()=>n.e(4311).then(n.t.bind(n,41691,19)),"~blog/default/tags-documentation-ee3.json",41691],"7c660760":[()=>n.e(2087).then(n.t.bind(n,91870,19)),"~blog/default/tags-woowahan-techcourse-page-9-065-list.json",91870],"7e4c1ed7":[()=>n.e(1653).then(n.t.bind(n,83297,19)),"~docs/default/tag-docs-tags-postmortem-ede.json",83297],"7e59392d":[()=>n.e(7281).then(n.t.bind(n,33202,19)),"~blog/default/tags-retrospective-page-9-473-list.json",33202],"7fbacf84":[()=>n.e(5797).then(n.t.bind(n,58701,19)),"~blog/default/tags-spring-de1-list.json",58701],"7fd9a574":[()=>n.e(2889).then(n.t.bind(n,5863,19)),"~blog/default/tags-retrospective-page-14-99d.json",5863],"80960b4b":[()=>n.e(7599).then(n.t.bind(n,28386,19)),"~blog/default/page-21-7a8.json",28386],"814f3328":[()=>n.e(2535).then(n.t.bind(n,45641,19)),"~blog/default/blog-post-list-prop-default.json",45641],"829fa7b9":[()=>n.e(9391).then(n.bind(n,22895)),"@site/blog/2023-2/2023-04-08-\uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c.mdx",22895],"86b4da3d":[()=>n.e(952).then(n.t.bind(n,44149,19)),"~blog/default/tags-woowahan-techcourse-page-2-567.json",44149],"87070fc3":[()=>n.e(6124).then(n.bind(n,74161)),"@site/docs/\uc131\ub2a5/\uc131\ub2a5 \ud14c\uc2a4\ud2b8.mdx",74161],"871c1e5a":[()=>n.e(5966).then(n.t.bind(n,71247,19)),"~blog/default/page-23-651.json",71247],"8720c147":[()=>n.e(470).then(n.bind(n,55810)),"@site/blog/2023-2/2023-04-03-\uc790\ubc14 \ud074\ub798\uc2a4\ud30c\uc77c \uad6c\uc870.mdx",55810],"8a24850b":[()=>n.e(741).then(n.bind(n,12401)),"@site/blog/2023-1/2023-03-31-\uccb4\uc2a4 \ubbf8\uc158 \ud68c\uace0.mdx",12401],"8a27aeff":[()=>n.e(71).then(n.bind(n,17442)),"@site/blog/2023-1/2023-01-02-2022\ub144 \ud68c\uace0.mdx?truncated=true",17442],"8ad2f007":[()=>n.e(8360).then(n.bind(n,96431)),"@site/blog/2023-3/2023-07-30-Mockito \uc774\uc6a9\ud574\uc11c static \uba54\uc11c\ub4dc \ubaa8\ud0b9\ud558\uae30.mdx?truncated=true",96431],"8b79a48d":[()=>n.e(9287).then(n.t.bind(n,59070,19)),"~blog/default/tags-retrospective-page-9-473.json",59070],"8c6c0796":[()=>n.e(2816).then(n.t.bind(n,59123,19)),"~blog/default/tags-retrospective-2fb.json",59123],"8d05b77c":[()=>n.e(4149).then(n.t.bind(n,22801,19)),"~blog/default/page-5-264.json",22801],"8d7288fe":[()=>n.e(4801).then(n.t.bind(n,71830,19)),"~blog/default/tags-class-eca-list.json",71830],"8da65e83":[()=>n.e(9427).then(n.t.bind(n,1341,19)),"~blog/default/tags-woowahan-techcourse-page-4-bcd.json",1341],"8dc09bac":[()=>n.e(8338).then(n.t.bind(n,28881,19)),"~blog/default/tags-event-f04-list.json",28881],"8e498bb6":[()=>n.e(1436).then(n.t.bind(n,50257,19)),"~blog/default/tags-java-page-3-b02.json",50257],"8fbd512b":[()=>n.e(5873).then(n.t.bind(n,15,19)),"~blog/default/tags-async-326.json",15],"905ecccc":[()=>Promise.all([n.e(532),n.e(2939)]).then(n.bind(n,1057)),"@site/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx?truncated=true",1057],"92ade856":[()=>Promise.all([n.e(532),n.e(1772)]).then(n.bind(n,68499)),"@site/blog/2023-3/2023-08-22-DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30.mdx?truncated=true",68499],"92fef07b":[()=>n.e(300).then(n.bind(n,21474)),"@site/blog/2023-1/2023-02-14-\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0.mdx",21474],"935f2afb":[()=>n.e(53).then(n.t.bind(n,1109,19)),"~docs/default/version-current-metadata-prop-751.json",1109],"96adae60":[()=>n.e(172).then(n.t.bind(n,54217,19)),"~blog/default/page-19-21b.json",54217],"981f7647":[()=>n.e(2947).then(n.bind(n,51077)),"@site/docs/\uc124\uacc4/\ud328\ud0a4\uc9c0.mdx",51077],"9b43eac8":[()=>n.e(9286).then(n.bind(n,44284)),"@site/blog/2023-1/2023-01-02-2022\ub144 \ud68c\uace0.mdx",44284],"9b56b618":[()=>n.e(9538).then(n.t.bind(n,37e3,19)),"~blog/default/tags-awt-0e2-list.json",37e3],"9bad5ae7":[()=>n.e(2153).then(n.bind(n,2163)),"@site/blog/2023-2/2023-04-07-InnoDB \uc2a4\ud1a0\ub9ac\uc9c0 \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx?truncated=true",2163],"9bbc65ac":[()=>n.e(7210).then(n.t.bind(n,51020,19)),"~docs/default/tag-docs-tags-test-8ab.json",51020],"9ca52986":[()=>n.e(3490).then(n.t.bind(n,92016,19)),"~blog/default/tags-lock-page-2-819.json",92016],"9cfe8fd1":[()=>n.e(7725).then(n.t.bind(n,97113,19)),"~blog/default/page-18-46d.json",97113],"9d1fd2b0":[()=>n.e(7972).then(n.bind(n,15361)),"@site/blog/2023-2/2023-06-11-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca82 \ud68c\uace0.mdx",15361],"9d8ee3a8":[()=>n.e(5962).then(n.t.bind(n,71297,19)),"~blog/default/tags-oop-03c.json",71297],"9dc4119a":[()=>n.e(6490).then(n.t.bind(n,4408,19)),"~blog/default/tags-retrospective-page-10-4a6-list.json",4408],"9dec6b67":[()=>n.e(8524).then(n.t.bind(n,88221,19)),"~blog/default/tags-data-base-page-2-3a7.json",88221],"9e2e3982":[()=>n.e(7617).then(n.bind(n,57214)),"@site/blog/2023-3/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd/2023-07-27-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uae30\uc220 \uc120\ud0dd.mdx",57214],"9e4087bc":[()=>n.e(3608).then(n.bind(n,63169)),"@theme/BlogArchivePage",63169],"9e477a5e":[()=>n.e(8312).then(n.bind(n,54686)),"@site/docs/\ubb38\ud654/\ud3ec\uc2a4\ud2b8_\ubaa8\ud15c.mdx",54686],"9e4ad429":[()=>n.e(5406).then(n.t.bind(n,16060,19)),"~docs/default/tag-docs-tags-performance-339.json",16060],"9fae68e2":[()=>n.e(297).then(n.t.bind(n,77536,19)),"~blog/default/tags-kotlin-6ac-list.json",77536],a0410ab5:[()=>n.e(7843).then(n.t.bind(n,76970,19)),"~blog/default/tags-retrospective-page-7-3e2.json",76970],a1877440:[()=>n.e(7648).then(n.t.bind(n,23235,19)),"~blog/default/tags-async-326-list.json",23235],a3614f73:[()=>n.e(8474).then(n.bind(n,8797)),"@site/blog/2023-2/2023-04-01-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca81 \ud68c\uace0.mdx",8797],a3dddb77:[()=>n.e(475).then(n.t.bind(n,5479,19)),"~blog/default/tags-java-page-4-c22.json",5479],a43f2942:[()=>n.e(104).then(n.bind(n,7894)),"@site/docs/\ud14c\uc2a4\ud2b8/\uacc4\ub2e8_\ud14c\uc2a4\ud2b8.mdx",7894],a4a1e915:[()=>n.e(3671).then(n.t.bind(n,60166,19)),"~blog/default/tags-retrospective-2fb-list.json",60166],a5557bb9:[()=>n.e(5991).then(n.t.bind(n,93885,19)),"~blog/default/index.json",93885],a59d28a9:[()=>n.e(2012).then(n.bind(n,44122)),"@site/blog/2023-3/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c.mdx?truncated=true",44122],a6aa9e1f:[()=>Promise.all([n.e(532),n.e(4659),n.e(6048),n.e(3089)]).then(n.bind(n,80046)),"@theme/BlogListPage",80046],a710d533:[()=>Promise.all([n.e(532),n.e(1711)]).then(n.bind(n,95220)),"@site/blog/2023-3/2023-09-11-\ud1b0\ucea3 \uad6c\ud604 \ubbf8\uc158 \ud68c\uace0.mdx",95220],a85e626a:[()=>n.e(9092).then(n.t.bind(n,48458,19)),"~blog/default/tags-jdbc-4bd.json",48458],a896be03:[()=>n.e(2526).then(n.t.bind(n,64030,19)),"~blog/default/tags-woowahan-techcourse-page-6-429.json",64030],a8cba70f:[()=>n.e(2261).then(n.bind(n,61629)),"@site/blog/2023-2/2023-04-01-\uc6b0\uc544\ud55c\ud14c\ud06c\ucf54\uc2a4 \ub808\ubca81 \ud68c\uace0.mdx?truncated=true",61629],a9221bd5:[()=>n.e(5507).then(n.t.bind(n,40319,19)),"~blog/default/tags-inno-db-59e-list.json",40319],aacfeabc:[()=>n.e(8909).then(n.bind(n,79407)),"@site/blog/2023-2/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815.mdx?truncated=true",79407],abb0816f:[()=>n.e(4174).then(n.t.bind(n,82969,19)),"~blog/default/tags-woowahan-techcourse-page-7-5bd-list.json",82969],abc83b7f:[()=>n.e(2215).then(n.t.bind(n,8412,19)),"~blog/default/tags-retrospective-page-2-e2b.json",8412],ac23d7ee:[()=>n.e(3213).then(n.t.bind(n,43943,19)),"~blog/default/tags-woowahan-techcourse-page-3-9a8-list.json",43943],ad3b7b62:[()=>n.e(26).then(n.bind(n,55097)),"@site/blog/2023-2/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0/2023-05-12-\uc6f9 \uc7a5\ubc14\uad6c\ub2c8 \ubbf8\uc158 \ud68c\uace0.mdx?truncated=true",55097],ae1d6508:[()=>n.e(2181).then(n.t.bind(n,83486,19)),"~blog/default/tags-composite-240-list.json",83486],ae3384b2:[()=>n.e(2965).then(n.t.bind(n,15745,19)),"/home/runner/work/greeng00se.github.io/greeng00se.github.io/.docusaurus/docusaurus-plugin-content-pages/default/plugin-route-context-module-100.json",15745],af81a133:[()=>n.e(7787).then(n.t.bind(n,13800,19)),"~blog/default/tags-teco-chat-d21.json",13800],b2b675dd:[()=>n.e(533).then(n.t.bind(n,28017,19)),"~blog/default/blog-c06.json",28017],b2c8756c:[()=>n.e(1213).then(n.bind(n,31419)),"@site/blog/2023-2/2023-06-04-\uc7a5\ubc14\uad6c\ub2c8 \uc8fc\ubb38 \ubbf8\uc158 \ud68c\uace0.mdx",31419],b2ebb6fd:[()=>n.e(4091).then(n.bind(n,34604)),"@site/blog/2023-2/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815/2023-04-30-Jenkins\ub85c \ubc30\ud3ec \uc790\ub3d9\ud654 \uc124\uc815.mdx",34604],b301b20b:[()=>n.e(3637).then(n.t.bind(n,83150,19)),"~blog/default/tags-replication-56b.json",83150],b36d2d1d:[()=>n.e(1257).then(n.t.bind(n,7903,19)),"~docs/default/tag-docs-tags-latency-735.json",7903],b421ebb7:[()=>n.e(8518).then(n.bind(n,20882)),"@site/docs/\uc131\ub2a5/Throughput\uacfc Latency.mdx",20882],b474adfe:[()=>n.e(573).then(n.t.bind(n,85419,19)),"~blog/default/tags-image-page-2-cc3-list.json",85419],b5f3dcc5:[()=>n.e(7723).then(n.t.bind(n,23005,19)),"~blog/default/tags-retrospective-page-15-26b-list.json",23005],b6ffb0cb:[()=>n.e(2156).then(n.bind(n,47682)),"@site/blog/2023-1/2023-02-14-\uc790\ub3d9\ucc28 \uacbd\uc8fc \ubbf8\uc158 \ud68c\uace0.mdx?truncated=true",47682],b7d33121:[()=>n.e(7153).then(n.t.bind(n,72005,19)),"~blog/default/tags-cloudwatch-6c7.json",72005],b88cb85b:[()=>n.e(2293).then(n.bind(n,18557)),"@site/docs/\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998.mdx",18557],b9bcab37:[()=>n.e(7688).then(n.t.bind(n,43632,19)),"~blog/default/tags-grasp-418.json",43632],bace0b37:[()=>n.e(2362).then(n.bind(n,25831)),"@site/blog/2023-2/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5/2023-05-24-\uc911\ubcf5\uacfc \uc6b0\ubc1c\uc801 \uc911\ubcf5.mdx",25831],bb221eab:[()=>n.e(711).then(n.bind(n,59165)),"@site/blog/2023-2/2023-04-04-\ud14c\uc2a4\ud2b8 \ub300\uc5ed.mdx?truncated=true",59165],bbc01ba0:[()=>n.e(3009).then(n.t.bind(n,12333,19)),"~blog/default/tags-retrospective-page-10-4a6.json",12333],bbc3f62a:[()=>n.e(8894).then(n.t.bind(n,51842,19)),"~blog/default/tags-woowahan-techcourse-page-13-8ae-list.json",51842],bbceb8f1:[()=>n.e(653).then(n.t.bind(n,26529,19)),"~blog/default/tags-woowahan-techcourse-page-5-ac5-list.json",26529],bbdd7e52:[()=>n.e(3396).then(n.bind(n,31120)),"@site/blog/2023-2/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604/2023-06-01-\ud14c\ucf54\ucc57 3. \uae30\ub2a5 \uad6c\ud604.mdx",31120],bd2d06b5:[()=>n.e(9763).then(n.t.bind(n,93081,19)),"~blog/default/tags-retrospective-page-3-ee4.json",93081],bd4db8ee:[()=>n.e(6883).then(n.bind(n,93546)),"@site/blog/2023-2/2023-04-05-\ud2b8\ub79c\uc7ad\uc158\uacfc \uaca9\ub9ac\uc218\uc900.mdx",93546],be497a8d:[()=>n.e(6172).then(n.bind(n,45909)),"@site/blog/2023-2/2023-06-18-Docusaurus/2023-06-18-Docusaurus.mdx?truncated=true",45909],bf933b37:[()=>n.e(3095).then(n.t.bind(n,52954,19)),"~blog/default/tags-my-sql-46a-list.json",52954],c037d168:[()=>n.e(6587).then(n.t.bind(n,41235,19)),"~blog/default/tags-transaction-ea3-list.json",41235],c08e7a0d:[()=>n.e(7404).then(n.t.bind(n,27625,19)),"~docs/default/tag-docs-tags-throughput-8fc.json",27625],c0cb7215:[()=>n.e(7966).then(n.t.bind(n,66109,19)),"~blog/default/tags-book-page-2-bc6-list.json",66109],c189d18f:[()=>n.e(4962).then(n.t.bind(n,3470,19)),"~docs/default/tag-docs-tags-etc-c52.json",3470],c1b17b3f:[()=>n.e(8927).then(n.bind(n,29571)),"@site/blog/2023-3/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131.md",29571],c29bedb9:[()=>n.e(9242).then(n.t.bind(n,44025,19)),"~blog/default/page-35-8fd.json",44025],c3ea66fe:[()=>n.e(6698).then(n.t.bind(n,63504,19)),"~blog/default/tags-isolation-79d.json",63504],c4f5d8e4:[()=>n.e(4195).then(n.bind(n,62841)),"@site/src/pages/index.js",62841],c55d205b:[()=>n.e(3438).then(n.bind(n,83859)),"@site/docs/Nginx/\uad6c\uc870_\ubc0f_\uba85\ub839\uc5b4.mdx",83859],c573638f:[()=>n.e(964).then(n.t.bind(n,28866,19)),"~blog/default/tags-tags-c2b.json",28866],c6004f62:[()=>n.e(5892).then(n.t.bind(n,37567,19)),"~blog/default/tags-mock-330-list.json",37567],c60995f6:[()=>n.e(6199).then(n.t.bind(n,62474,19)),"~docs/default/tag-docs-tags-nginx-3b7.json",62474],c60ea0ff:[()=>n.e(3085).then(n.t.bind(n,14072,19)),"~blog/default/tags-teco-chat-page-2-d4f.json",14072],c6d04683:[()=>n.e(5436).then(n.bind(n,48905)),"@site/blog/2023-2/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30.mdx",48905],c7015929:[()=>n.e(4185).then(n.t.bind(n,910,19)),"~blog/default/tags-python-687.json",910],c92f81ac:[()=>n.e(4393).then(n.t.bind(n,10767,19)),"~blog/default/tags-replication-56b-list.json",10767],caf1b628:[()=>n.e(7230).then(n.bind(n,16711)),"@site/docs/\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1.mdx",16711],cb6229c3:[()=>n.e(7204).then(n.bind(n,26930)),"@site/blog/2023-2/2023-04-06-MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx",26930],cc519f63:[()=>n.e(5323).then(n.bind(n,7771)),"@site/blog/2023-3/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604/2023-08-02-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \uad6c\ud604.mdx",7771],ccc49370:[()=>Promise.all([n.e(532),n.e(4659),n.e(6048),n.e(6103)]).then(n.bind(n,65203)),"@theme/BlogPostPage",65203],cef46b76:[()=>n.e(8644).then(n.bind(n,32390)),"@site/blog/2023-3/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c/2023-07-31-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ud30c\uc774\uc36c.mdx",32390],cf8e491a:[()=>n.e(5870).then(n.t.bind(n,19799,19)),"~blog/default/tags-awt-0e2.json",19799],d0277431:[()=>n.e(846).then(n.t.bind(n,4838,19)),"~blog/default/tags-dto-cb6-list.json",4838],d0840b01:[()=>n.e(8037).then(n.t.bind(n,20317,19)),"~blog/default/tags-transaction-ea3.json",20317],d09f7e4b:[()=>n.e(3098).then(n.t.bind(n,84057,19)),"~blog/default/tags-teco-chat-page-3-007-list.json",84057],d0e4cdf1:[()=>n.e(5465).then(n.t.bind(n,64020,19)),"~blog/default/page-7-3c3.json",64020],d126aabd:[()=>n.e(1675).then(n.t.bind(n,7220,19)),"~blog/default/tags-retrospective-page-4-3a3-list.json",7220],d1cef389:[()=>n.e(9310).then(n.t.bind(n,40836,19)),"~blog/default/page-17-62c.json",40836],d202e2c5:[()=>n.e(7175).then(n.t.bind(n,3395,19)),"~blog/default/tags-oop-03c-list.json",3395],d2611248:[()=>n.e(8561).then(n.t.bind(n,81667,19)),"~blog/default/page-43-b0a.json",81667],d2770bf7:[()=>n.e(843).then(n.t.bind(n,41156,19)),"~blog/default/tags-woowahan-techcourse-page-11-6c9.json",41156],d28e30d7:[()=>Promise.all([n.e(532),n.e(6671)]).then(n.bind(n,27464)),"@site/blog/2023-1/2023-01-16-Kotlin\uc5d0\uc11c null\uc744 \ub2e4\ub8e8\ub294 \ubc29\ubc95.mdx",27464],d2935d14:[()=>n.e(3259).then(n.t.bind(n,92158,19)),"~blog/default/tags-isolation-79d-list.json",92158],d361ad2d:[()=>n.e(2247).then(n.bind(n,40237)),"@site/docs/\uc131\ub2a5/Throughput \ubaa9\ud46f\uac12.mdx",40237],d368e73e:[()=>n.e(7954).then(n.t.bind(n,71965,19)),"~blog/default/tags-image-97d-list.json",71965],d40f51e1:[()=>n.e(9633).then(n.t.bind(n,9415,19)),"~blog/default/tags-jdbc-4bd-list.json",9415],d50fd269:[()=>n.e(100).then(n.t.bind(n,38132,19)),"~blog/default/page-31-308.json",38132],d5bb232a:[()=>n.e(3651).then(n.bind(n,14345)),"@site/blog/2023-2/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30/2023-05-26-\ucef4\ud3ec\uc9c0\ud2b8 \ud328\ud134\uc73c\ub85c \uc694\uae08 \uc815\ucc45 \ucd94\uc0c1\ud654\ud558\uae30.mdx?truncated=true",14345],d5dfecc2:[()=>n.e(1677).then(n.t.bind(n,83335,19)),"~blog/default/tags-teco-chat-page-2-d4f-list.json",83335],d60e2b0c:[()=>n.e(8174).then(n.t.bind(n,36927,19)),"~blog/default/tags-retrospective-page-17-636-list.json",36927],d65e25b7:[()=>Promise.all([n.e(532),n.e(7776)]).then(n.bind(n,8e4)),"@site/blog/2023-3/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac/2023-08-13-\uacbd\ub85c \uc774\ubbf8\uc9c0 \uc0dd\uc131\ud558\uae30 - \ube44\ub3d9\uae30 \ucc98\ub9ac.mdx?truncated=true",8e4],d6a3d698:[()=>n.e(2890).then(n.t.bind(n,39477,19)),"~blog/default/tags-image-page-3-942.json",39477],d7955594:[()=>Promise.all([n.e(532),n.e(588)]).then(n.bind(n,89788)),"@site/blog/2023-2/2023-04-02-\ucee4\uc2a4\ud140 JdbcTemplate \ub9cc\ub4e4\uae30.mdx",89788],d86f7a37:[()=>n.e(3392).then(n.bind(n,10823)),"@site/docs/Nginx/\uc815\uc801_\ucee8\ud150\uce20_\uc81c\uacf5.mdx",10823],d8775059:[()=>n.e(6387).then(n.bind(n,98809)),"@site/blog/2023-2/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30.mdx",98809],d88bdb28:[()=>n.e(9788).then(n.t.bind(n,29417,19)),"~blog/default/tags-retrospective-page-13-49c.json",29417],d8cdf5ef:[()=>n.e(5919).then(n.bind(n,11311)),"@site/docs/\uae30\ud0c0/\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8.mdx",11311],dab4c683:[()=>n.e(6058).then(n.t.bind(n,67315,19)),"~blog/default/tags-woowahan-techcourse-page-3-9a8.json",67315],daff1d93:[()=>n.e(1659).then(n.bind(n,26016)),"@site/blog/2023-2/2023-04-08-\uc0c1\uc790 \ubc16\uc5d0 \uc788\ub294 \uc0ac\ub78c.mdx?truncated=true",26016],db7928b3:[()=>n.e(5046).then(n.t.bind(n,11478,19)),"~blog/default/tags-intelli-j-2bf-list.json",11478],db86613e:[()=>n.e(9458).then(n.bind(n,58967)),"@site/blog/2023-2/2023-04-06-MySQL \uc5d4\uc9c4\uc758 \uc7a0\uae08.mdx?truncated=true",58967],dca6a1e3:[()=>n.e(3239).then(n.bind(n,52589)),"@site/blog/2023-2/2023-06-08-\ub808\ubca8 2 - \ub808\ubca8 \uc778\ud130\ubdf0 \ud68c\uace0.mdx?truncated=true",52589],dcf70953:[()=>n.e(1761).then(n.t.bind(n,83769,19)),"/home/runner/work/greeng00se.github.io/greeng00se.github.io/.docusaurus/docusaurus-plugin-content-docs/default/plugin-route-context-module-100.json",83769],ddf9e0bd:[()=>n.e(2542).then(n.t.bind(n,84039,19)),"~blog/default/tags-woowahan-techcourse-page-8-93a-list.json",84039],df147deb:[()=>n.e(5103).then(n.bind(n,78134)),"@site/blog/2023-1/2023-02-26-\uc0ac\ub2e4\ub9ac \ud0c0\uae30 \ubbf8\uc158 \ud68c\uace0.mdx?truncated=true",78134],df203c0f:[()=>n.e(9924).then(n.bind(n,40491)),"@theme/DocTagDocListPage",40491],df862072:[()=>n.e(7474).then(n.t.bind(n,24827,19)),"~blog/default/tags-book-page-3-a93.json",24827],dfa84138:[()=>n.e(1434).then(n.t.bind(n,22483,19)),"~blog/default/tags-data-base-page-3-9db-list.json",22483],dfc7013c:[()=>n.e(5857).then(n.bind(n,57515)),"@site/blog/2023-2/2023-04-03-\uc790\ubc14 \ud074\ub798\uc2a4\ud30c\uc77c \uad6c\uc870.mdx?truncated=true",57515],e073eb07:[()=>n.e(5819).then(n.t.bind(n,57743,19)),"~blog/default/tags-retrospective-page-11-e3c.json",57743],e0d68441:[()=>n.e(628).then(n.t.bind(n,75301,19)),"~blog/default/tags-retrospective-page-12-8cf-list.json",75301],e0e4666e:[()=>n.e(4665).then(n.t.bind(n,16482,19)),"~blog/default/tags-my-sql-46a.json",16482],e1735da7:[()=>n.e(1611).then(n.bind(n,9753)),"@site/blog/2023-2/2023-05-01-\ud14c\ucf54\ucc57 2. \ubc30\ud3ec.mdx?truncated=true",9753],e1a06456:[()=>n.e(9910).then(n.bind(n,34117)),"@site/blog/2023-2/2023-06-26-WebSocket.mdx",34117],e21c8cc4:[()=>n.e(6049).then(n.t.bind(n,48765,19)),"~blog/default/tags-retrospective-page-8-5ab.json",48765],e2de2dbb:[()=>n.e(6710).then(n.t.bind(n,47023,19)),"~blog/default/tags-java-page-5-b71.json",47023],e4ebfe18:[()=>n.e(9940).then(n.t.bind(n,57954,19)),"~blog/default/page-3-02e.json",57954],e6a6ed43:[()=>n.e(5300).then(n.bind(n,19463)),"@site/blog/2023-1/2023-03-30-GRASP.mdx?truncated=true",19463],e7d2a655:[()=>n.e(8652).then(n.t.bind(n,71501,19)),"~blog/default/tags-woowahan-techcourse-page-2-567-list.json",71501],e8d6e7ce:[()=>n.e(3912).then(n.t.bind(n,65245,19)),"~blog/default/tags-retrospective-page-6-594.json",65245],e9624b4f:[()=>n.e(4564).then(n.t.bind(n,11780,19)),"~blog/default/tags-retrospective-page-13-49c-list.json",11780],e9eabc5d:[()=>n.e(1112).then(n.bind(n,778)),"@site/blog/2023-1/2023-03-30-GRASP.mdx",778],e9ff60ad:[()=>n.e(2530).then(n.t.bind(n,10242,19)),"~blog/default/tags-pattern-b4e-list.json",10242],ee1dd2ad:[()=>n.e(5435).then(n.t.bind(n,48834,19)),"~blog/default/tags-woowahan-techcourse-page-13-8ae.json",48834],ee92877e:[()=>n.e(8716).then(n.t.bind(n,41106,19)),"~blog/default/tags-retrospective-page-5-22d.json",41106],eec33099:[()=>n.e(4953).then(n.t.bind(n,80133,19)),"~blog/default/page-40-397.json",80133],ef5b2427:[()=>n.e(9606).then(n.t.bind(n,50195,19)),"~blog/default/page-22-f33.json",50195],eff1d58f:[()=>n.e(4137).then(n.bind(n,93097)),"@site/blog/2023-2/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30/2023-04-22-\ud14c\ucf54\ucc57 1. \ud504\ub85c\ud1a0\ud0c0\uc785 \ub9cc\ub4e4\uae30.mdx?truncated=true",93097],f042b56c:[()=>n.e(8919).then(n.t.bind(n,27490,19)),"~blog/default/tags-teco-chat-d21-list.json",27490],f06cb3e2:[()=>Promise.all([n.e(532),n.e(2620)]).then(n.bind(n,7729)),"@site/blog/2023-3/2023-08-22-DB \ubcf5\uc81c, @Transactional\uc5d0 \ub530\ub77c \uc694\uccad \ubd84\ub9ac\ud574\ubcf4\uae30.mdx",7729],f078e301:[()=>n.e(1926).then(n.t.bind(n,18385,19)),"~blog/default/tags-woowahan-techcourse-page-6-429-list.json",18385],f0978ee1:[()=>n.e(7740).then(n.t.bind(n,69366,19)),"~blog/default/tags-awt-page-2-eb4.json",69366],f156dfb9:[()=>n.e(5602).then(n.t.bind(n,83311,19)),"~blog/default/tags-time-471.json",83311],f25de701:[()=>n.e(2245).then(n.bind(n,87875)),"@site/blog/2023-3/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131/2023-08-17-CloudWatch\ub97c \uc774\uc6a9\ud55c \ub85c\uae45, \uba54\ud2b8\ub9ad \ubaa8\ub2c8\ud130\ub9c1 \ud658\uacbd \uad6c\uc131.md?truncated=true",87875],f332d221:[()=>n.e(2717).then(n.t.bind(n,99371,19)),"~blog/default/page-10-857.json",99371],f3e308ad:[()=>n.e(6123).then(n.t.bind(n,16240,19)),"~blog/default/page-33-758.json",16240],f4c6e7e6:[()=>n.e(31).then(n.t.bind(n,77922,19)),"~docs/default/tag-docs-tags-book-8e4.json",77922],f4f49e13:[()=>n.e(6887).then(n.t.bind(n,26329,19)),"~blog/default/page-12-b6a.json",26329],f580a9d0:[()=>n.e(9887).then(n.t.bind(n,78989,19)),"~blog/default/tags-python-687-list.json",78989],f63a747b:[()=>n.e(5131).then(n.t.bind(n,81723,19)),"~blog/default/tags-woowahan-techcourse-page-7-5bd.json",81723],f75a8651:[()=>n.e(8882).then(n.t.bind(n,44633,19)),"~blog/default/page-8-8c2.json",44633],f7b9d2f4:[()=>n.e(2958).then(n.t.bind(n,53122,19)),"~blog/default/tags-woowahan-techcourse-page-12-5ba.json",53122],f8409a7e:[()=>n.e(3206).then(n.bind(n,69568)),"@site/docs/intro.mdx",69568],f87bdf62:[()=>n.e(6750).then(n.bind(n,19426)),"@site/blog/2023-2/2023-06-26-WebSocket.mdx?truncated=true",19426],f90d0c52:[()=>n.e(5294).then(n.bind(n,85749)),"@site/blog/2023-1/2023-01-07-\uac1d\uccb4\uc9c0\ud5a5\uc758 \uc0ac\uc2e4\uacfc \uc624\ud574.mdx",85749],fa3d3942:[()=>n.e(916).then(n.bind(n,53036)),"@site/blog/2023-1/2023-01-01-\uae00, \uc6b0\ub9ac\ub3c4 \uc798 \uc4f8 \uc218 \uc788\uc2b5\ub2c8\ub2e4.mdx",53036],fbd57548:[()=>n.e(6837).then(n.t.bind(n,30990,19)),"~blog/default/page-11-f65.json",30990],fcb446a5:[()=>n.e(3440).then(n.bind(n,84425)),"@site/docs/\ub9ac\ub205\uc2a4/\ud130\ubbf8\ub110_\uc258_\ud504\ub86c\ud504\ud2b8_\uc124\uc815.md",84425],fd5d2408:[()=>n.e(3614).then(n.t.bind(n,64631,19)),"~blog/default/tags-time-471-list.json",64631],fe273484:[()=>n.e(8355).then(n.t.bind(n,53034,19)),"~blog/default/tags-java-a6e-list.json",53034],fe8cce0a:[()=>n.e(955).then(n.t.bind(n,78535,19)),"~blog/default/tags-intelli-j-2bf.json",78535],fed8bc04:[()=>n.e(8110).then(n.t.bind(n,96375,19)),"~blog/default/tags-woowahan-techcourse-page-5-ac5.json",96375],ff4c6c5e:[()=>n.e(820).then(n.bind(n,23856)),"@site/blog/2023-3/2023-07-24-\uc790\ubc14 17, \uc2a4\ud504\ub9c1 6.0, \uc2a4\ud504\ub9c1 \ubd80\ud2b8 3.1.mdx",23856],ffb0fa11:[()=>n.e(7400).then(n.t.bind(n,58214,19)),"~blog/default/tags-book-page-3-a93-list.json",58214]};function c(e){let{error:t,retry:n,pastDelay:r}=e;return t?a.createElement("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"}},a.createElement("p",null,String(t)),a.createElement("div",null,a.createElement("button",{type:"button",onClick:n},"Retry"))):r?a.createElement("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"}},a.createElement("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb"},a.createElement("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2"},a.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},a.createElement("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),a.createElement("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),a.createElement("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),a.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},a.createElement("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),a.createElement("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),a.createElement("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),a.createElement("circle",{cx:"22",cy:"22",r:"8"},a.createElement("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"}))))):null}var u=n(99670),d=n(30226);function f(e,t){if("*"===e)return i()({loading:c,loader:()=>n.e(4972).then(n.bind(n,4972)),modules:["@theme/NotFound"],webpack:()=>[4972],render(e,t){const n=e.default;return a.createElement(d.z,{value:{plugin:{name:"native",id:"default"}}},a.createElement(n,t))}});const o=s[`${e}-${t}`],f={},p=[],g=[],m=(0,u.Z)(o);return Object.entries(m).forEach((e=>{let[t,n]=e;const a=l[n];a&&(f[t]=a[0],p.push(a[1]),g.push(a[2]))})),i().Map({loading:c,loader:f,modules:p,webpack:()=>g,render(t,n){const i=JSON.parse(JSON.stringify(o));Object.entries(t).forEach((t=>{let[n,a]=t;const r=a.default;if(!r)throw new Error(`The page component at ${e} doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.`);"object"!=typeof r&&"function"!=typeof r||Object.keys(a).filter((e=>"default"!==e)).forEach((e=>{r[e]=a[e]}));let o=i;const s=n.split(".");s.slice(0,-1).forEach((e=>{o=o[e]})),o[s[s.length-1]]=r}));const s=i.__comp;delete i.__comp;const l=i.__context;return delete i.__context,a.createElement(d.z,{value:l},a.createElement(s,(0,r.Z)({},i,n)))}})}const p=[{path:"/2022-retrospective",component:f("/2022-retrospective","624"),exact:!0},{path:"/accidental-duplication",component:f("/accidental-duplication","d17"),exact:!0},{path:"/blackjack-retrospective",component:f("/blackjack-retrospective","595"),exact:!0},{path:"/blog",component:f("/blog","3d8"),exact:!0},{path:"/book-leadership-and-self-deception",component:f("/book-leadership-and-self-deception","ddd"),exact:!0},{path:"/book-writer",component:f("/book-writer","3c9"),exact:!0},{path:"/chess-retrospective",component:f("/chess-retrospective","168"),exact:!0},{path:"/cloudwatch",component:f("/cloudwatch","cb1"),exact:!0},{path:"/composite",component:f("/composite","302"),exact:!0},{path:"/custom-jdbc-template",component:f("/custom-jdbc-template","cfa"),exact:!0},{path:"/db-replication",component:f("/db-replication","6f2"),exact:!0},{path:"/docs/tags",component:f("/docs/tags","820"),exact:!0},{path:"/docs/tags/book",component:f("/docs/tags/book","c20"),exact:!0},{path:"/docs/tags/etc",component:f("/docs/tags/etc","338"),exact:!0},{path:"/docs/tags/jpa",component:f("/docs/tags/jpa","f95"),exact:!0},{path:"/docs/tags/latency",component:f("/docs/tags/latency","e82"),exact:!0},{path:"/docs/tags/load-balancing",component:f("/docs/tags/load-balancing","0be"),exact:!0},{path:"/docs/tags/monitoring",component:f("/docs/tags/monitoring","50f"),exact:!0},{path:"/docs/tags/network",component:f("/docs/tags/network","322"),exact:!0},{path:"/docs/tags/nginx",component:f("/docs/tags/nginx","ecc"),exact:!0},{path:"/docs/tags/package",component:f("/docs/tags/package","593"),exact:!0},{path:"/docs/tags/performance",component:f("/docs/tags/performance","a3d"),exact:!0},{path:"/docs/tags/postmortem",component:f("/docs/tags/postmortem","4b0"),exact:!0},{path:"/docs/tags/test",component:f("/docs/tags/test","b58"),exact:!0},{path:"/docs/tags/throughput",component:f("/docs/tags/throughput","206"),exact:!0},{path:"/docusaurus",component:f("/docusaurus","926"),exact:!0},{path:"/grasp",component:f("/grasp","f9b"),exact:!0},{path:"/innodb-lock",component:f("/innodb-lock","d18"),exact:!0},{path:"/intellij-settings",component:f("/intellij-settings","80e"),exact:!0},{path:"/java-class-file",component:f("/java-class-file","50e"),exact:!0},{path:"/java-spring-springboot",component:f("/java-spring-springboot","bed"),exact:!0},{path:"/jenkins",component:f("/jenkins","48c"),exact:!0},{path:"/jsr-310",component:f("/jsr-310","7f9"),exact:!0},{path:"/kotlin-null",component:f("/kotlin-null","8d3"),exact:!0},{path:"/ladder-retrospective",component:f("/ladder-retrospective","7ea"),exact:!0},{path:"/level2-interview-retrospective",component:f("/level2-interview-retrospective","c75"),exact:!0},{path:"/mock-static-method",component:f("/mock-static-method","fd3"),exact:!0},{path:"/mysql-lock",component:f("/mysql-lock","3cb"),exact:!0},{path:"/order-retrospective",component:f("/order-retrospective","a11"),exact:!0},{path:"/page/10",component:f("/page/10","83e"),exact:!0},{path:"/page/11",component:f("/page/11","dfb"),exact:!0},{path:"/page/12",component:f("/page/12","ad3"),exact:!0},{path:"/page/13",component:f("/page/13","f02"),exact:!0},{path:"/page/14",component:f("/page/14","ff7"),exact:!0},{path:"/page/15",component:f("/page/15","52c"),exact:!0},{path:"/page/16",component:f("/page/16","a4c"),exact:!0},{path:"/page/17",component:f("/page/17","8ab"),exact:!0},{path:"/page/18",component:f("/page/18","190"),exact:!0},{path:"/page/19",component:f("/page/19","bb8"),exact:!0},{path:"/page/2",component:f("/page/2","c18"),exact:!0},{path:"/page/20",component:f("/page/20","86f"),exact:!0},{path:"/page/21",component:f("/page/21","fc2"),exact:!0},{path:"/page/22",component:f("/page/22","420"),exact:!0},{path:"/page/23",component:f("/page/23","f91"),exact:!0},{path:"/page/24",component:f("/page/24","ed4"),exact:!0},{path:"/page/25",component:f("/page/25","5c6"),exact:!0},{path:"/page/26",component:f("/page/26","80c"),exact:!0},{path:"/page/27",component:f("/page/27","798"),exact:!0},{path:"/page/28",component:f("/page/28","529"),exact:!0},{path:"/page/29",component:f("/page/29","517"),exact:!0},{path:"/page/3",component:f("/page/3","a8a"),exact:!0},{path:"/page/30",component:f("/page/30","f27"),exact:!0},{path:"/page/31",component:f("/page/31","5fb"),exact:!0},{path:"/page/32",component:f("/page/32","8d7"),exact:!0},{path:"/page/33",component:f("/page/33","463"),exact:!0},{path:"/page/34",component:f("/page/34","825"),exact:!0},{path:"/page/35",component:f("/page/35","014"),exact:!0},{path:"/page/36",component:f("/page/36","55d"),exact:!0},{path:"/page/37",component:f("/page/37","45d"),exact:!0},{path:"/page/38",component:f("/page/38","e77"),exact:!0},{path:"/page/39",component:f("/page/39","17f"),exact:!0},{path:"/page/4",component:f("/page/4","639"),exact:!0},{path:"/page/40",component:f("/page/40","f67"),exact:!0},{path:"/page/41",component:f("/page/41","911"),exact:!0},{path:"/page/42",component:f("/page/42","547"),exact:!0},{path:"/page/43",component:f("/page/43","303"),exact:!0},{path:"/page/44",component:f("/page/44","a78"),exact:!0},{path:"/page/45",component:f("/page/45","70c"),exact:!0},{path:"/page/5",component:f("/page/5","47b"),exact:!0},{path:"/page/6",component:f("/page/6","fbc"),exact:!0},{path:"/page/7",component:f("/page/7","a17"),exact:!0},{path:"/page/8",component:f("/page/8","274"),exact:!0},{path:"/page/9",component:f("/page/9","f4d"),exact:!0},{path:"/parameterized-tests",component:f("/parameterized-tests","1fb"),exact:!0},{path:"/performance-test-type",component:f("/performance-test-type","df3"),exact:!0},{path:"/racing-car-retrospective",component:f("/racing-car-retrospective","ebc"),exact:!0},{path:"/route-image-async-with-event",component:f("/route-image-async-with-event","832"),exact:!0},{path:"/route-image-implementation",component:f("/route-image-implementation","b0f"),exact:!0},{path:"/route-image-intro",component:f("/route-image-intro","7e7"),exact:!0},{path:"/route-image-python",component:f("/route-image-python","dfd"),exact:!0},{path:"/search",component:f("/search","c1e"),exact:!0},{path:"/shopping-cart-retrospective",component:f("/shopping-cart-retrospective","28b"),exact:!0},{path:"/subway-retrospective",component:f("/subway-retrospective","7db"),exact:!0},{path:"/tags",component:f("/tags","4bf"),exact:!0},{path:"/tags/async",component:f("/tags/async","a06"),exact:!0},{path:"/tags/awt",component:f("/tags/awt","9e3"),exact:!0},{path:"/tags/awt/page/2",component:f("/tags/awt/page/2","b68"),exact:!0},{path:"/tags/book",component:f("/tags/book","4df"),exact:!0},{path:"/tags/book/page/2",component:f("/tags/book/page/2","88a"),exact:!0},{path:"/tags/book/page/3",component:f("/tags/book/page/3","128"),exact:!0},{path:"/tags/class",component:f("/tags/class","d3d"),exact:!0},{path:"/tags/cloudwatch",component:f("/tags/cloudwatch","264"),exact:!0},{path:"/tags/composite",component:f("/tags/composite","931"),exact:!0},{path:"/tags/data-base",component:f("/tags/data-base","2fe"),exact:!0},{path:"/tags/data-base/page/2",component:f("/tags/data-base/page/2","049"),exact:!0},{path:"/tags/data-base/page/3",component:f("/tags/data-base/page/3","9ac"),exact:!0},{path:"/tags/documentation",component:f("/tags/documentation","86d"),exact:!0},{path:"/tags/dto",component:f("/tags/dto","bef"),exact:!0},{path:"/tags/elastic-beanstalk",component:f("/tags/elastic-beanstalk","164"),exact:!0},{path:"/tags/event",component:f("/tags/event","56a"),exact:!0},{path:"/tags/grasp",component:f("/tags/grasp","130"),exact:!0},{path:"/tags/image",component:f("/tags/image","067"),exact:!0},{path:"/tags/image/page/2",component:f("/tags/image/page/2","88a"),exact:!0},{path:"/tags/image/page/3",component:f("/tags/image/page/3","acc"),exact:!0},{path:"/tags/inno-db",component:f("/tags/inno-db","2aa"),exact:!0},{path:"/tags/intelli-j",component:f("/tags/intelli-j","aaa"),exact:!0},{path:"/tags/isolation",component:f("/tags/isolation","bab"),exact:!0},{path:"/tags/java",component:f("/tags/java","60d"),exact:!0},{path:"/tags/java/page/2",component:f("/tags/java/page/2","d9f"),exact:!0},{path:"/tags/java/page/3",component:f("/tags/java/page/3","d2c"),exact:!0},{path:"/tags/java/page/4",component:f("/tags/java/page/4","c97"),exact:!0},{path:"/tags/java/page/5",component:f("/tags/java/page/5","6c7"),exact:!0},{path:"/tags/jdbc",component:f("/tags/jdbc","667"),exact:!0},{path:"/tags/jenkins",component:f("/tags/jenkins","590"),exact:!0},{path:"/tags/kotlin",component:f("/tags/kotlin","b84"),exact:!0},{path:"/tags/lock",component:f("/tags/lock","059"),exact:!0},{path:"/tags/lock/page/2",component:f("/tags/lock/page/2","2e3"),exact:!0},{path:"/tags/log",component:f("/tags/log","0ae"),exact:!0},{path:"/tags/mock",component:f("/tags/mock","9dd"),exact:!0},{path:"/tags/mockito",component:f("/tags/mockito","3de"),exact:!0},{path:"/tags/monitoring",component:f("/tags/monitoring","775"),exact:!0},{path:"/tags/my-sql",component:f("/tags/my-sql","214"),exact:!0},{path:"/tags/mysql",component:f("/tags/mysql","79d"),exact:!0},{path:"/tags/oop",component:f("/tags/oop","613"),exact:!0},{path:"/tags/pattern",component:f("/tags/pattern","5e4"),exact:!0},{path:"/tags/performance-test",component:f("/tags/performance-test","d3e"),exact:!0},{path:"/tags/python",component:f("/tags/python","7fe"),exact:!0},{path:"/tags/replication",component:f("/tags/replication","77b"),exact:!0},{path:"/tags/retrospective",component:f("/tags/retrospective","69e"),exact:!0},{path:"/tags/retrospective/page/10",component:f("/tags/retrospective/page/10","927"),exact:!0},{path:"/tags/retrospective/page/11",component:f("/tags/retrospective/page/11","792"),exact:!0},{path:"/tags/retrospective/page/12",component:f("/tags/retrospective/page/12","3a2"),exact:!0},{path:"/tags/retrospective/page/13",component:f("/tags/retrospective/page/13","21d"),exact:!0},{path:"/tags/retrospective/page/14",component:f("/tags/retrospective/page/14","178"),exact:!0},{path:"/tags/retrospective/page/15",component:f("/tags/retrospective/page/15","9bb"),exact:!0},{path:"/tags/retrospective/page/16",component:f("/tags/retrospective/page/16","0ea"),exact:!0},{path:"/tags/retrospective/page/17",component:f("/tags/retrospective/page/17","624"),exact:!0},{path:"/tags/retrospective/page/2",component:f("/tags/retrospective/page/2","599"),exact:!0},{path:"/tags/retrospective/page/3",component:f("/tags/retrospective/page/3","b13"),exact:!0},{path:"/tags/retrospective/page/4",component:f("/tags/retrospective/page/4","825"),exact:!0},{path:"/tags/retrospective/page/5",component:f("/tags/retrospective/page/5","f9f"),exact:!0},{path:"/tags/retrospective/page/6",component:f("/tags/retrospective/page/6","5ea"),exact:!0},{path:"/tags/retrospective/page/7",component:f("/tags/retrospective/page/7","21e"),exact:!0},{path:"/tags/retrospective/page/8",component:f("/tags/retrospective/page/8","520"),exact:!0},{path:"/tags/retrospective/page/9",component:f("/tags/retrospective/page/9","24d"),exact:!0},{path:"/tags/spring",component:f("/tags/spring","ab2"),exact:!0},{path:"/tags/spring-boot",component:f("/tags/spring-boot","eb7"),exact:!0},{path:"/tags/static",component:f("/tags/static","dd5"),exact:!0},{path:"/tags/teco-chat",component:f("/tags/teco-chat","7a9"),exact:!0},{path:"/tags/teco-chat/page/2",component:f("/tags/teco-chat/page/2","fbf"),exact:!0},{path:"/tags/teco-chat/page/3",component:f("/tags/teco-chat/page/3","c3d"),exact:!0},{path:"/tags/test",component:f("/tags/test","902"),exact:!0},{path:"/tags/time",component:f("/tags/time","f3d"),exact:!0},{path:"/tags/transaction",component:f("/tags/transaction","622"),exact:!0},{path:"/tags/web-socket",component:f("/tags/web-socket","e59"),exact:!0},{path:"/tags/woowahan-techcourse",component:f("/tags/woowahan-techcourse","03d"),exact:!0},{path:"/tags/woowahan-techcourse/page/10",component:f("/tags/woowahan-techcourse/page/10","294"),exact:!0},{path:"/tags/woowahan-techcourse/page/11",component:f("/tags/woowahan-techcourse/page/11","3b7"),exact:!0},{path:"/tags/woowahan-techcourse/page/12",component:f("/tags/woowahan-techcourse/page/12","490"),exact:!0},{path:"/tags/woowahan-techcourse/page/13",component:f("/tags/woowahan-techcourse/page/13","827"),exact:!0},{path:"/tags/woowahan-techcourse/page/2",component:f("/tags/woowahan-techcourse/page/2","901"),exact:!0},{path:"/tags/woowahan-techcourse/page/3",component:f("/tags/woowahan-techcourse/page/3","3cd"),exact:!0},{path:"/tags/woowahan-techcourse/page/4",component:f("/tags/woowahan-techcourse/page/4","8f1"),exact:!0},{path:"/tags/woowahan-techcourse/page/5",component:f("/tags/woowahan-techcourse/page/5","295"),exact:!0},{path:"/tags/woowahan-techcourse/page/6",component:f("/tags/woowahan-techcourse/page/6","89a"),exact:!0},{path:"/tags/woowahan-techcourse/page/7",component:f("/tags/woowahan-techcourse/page/7","fa8"),exact:!0},{path:"/tags/woowahan-techcourse/page/8",component:f("/tags/woowahan-techcourse/page/8","1fb"),exact:!0},{path:"/tags/woowahan-techcourse/page/9",component:f("/tags/woowahan-techcourse/page/9","2ae"),exact:!0},{path:"/tecochat-retrospective-1",component:f("/tecochat-retrospective-1","ed9"),exact:!0},{path:"/tecochat-retrospective-2",component:f("/tecochat-retrospective-2","fc4"),exact:!0},{path:"/tecochat-retrospective-3",component:f("/tecochat-retrospective-3","6f4"),exact:!0},{path:"/test-double",component:f("/test-double","8ea"),exact:!0},{path:"/the-essence-of-object-orientation",component:f("/the-essence-of-object-orientation","9a2"),exact:!0},{path:"/tomcat-retrospective",component:f("/tomcat-retrospective","5a3"),exact:!0},{path:"/transaction-and-isolation",component:f("/transaction-and-isolation","d60"),exact:!0},{path:"/web-racing-car-retrospective",component:f("/web-racing-car-retrospective","ccb"),exact:!0},{path:"/websocket",component:f("/websocket","5c1"),exact:!0},{path:"/woowacourse-level1-retrospective",component:f("/woowacourse-level1-retrospective","a6a"),exact:!0},{path:"/woowacourse-level2-retrospective",component:f("/woowacourse-level2-retrospective","758"),exact:!0},{path:"/woowacourse-level3-retrospective",component:f("/woowacourse-level3-retrospective","4e5"),exact:!0},{path:"/docs",component:f("/docs","5fb"),routes:[{path:"/docs",component:f("/docs","818"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/book/getting-out-of-the-box",component:f("/docs/book/getting-out-of-the-box","e0f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/culture/postmortem",component:f("/docs/culture/postmortem","b2f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/design/package",component:f("/docs/design/package","274"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/etc/communication",component:f("/docs/etc/communication","9c1"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/etc/develop-with-spring",component:f("/docs/etc/develop-with-spring","9ab"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/etc/experience-and-self-question",component:f("/docs/etc/experience-and-self-question","d25"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/etc/healthful-growth",component:f("/docs/etc/healthful-growth","b7b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/jpa/key",component:f("/docs/jpa/key","fed"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/linux/shell",component:f("/docs/linux/shell","a10"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/linux/swap",component:f("/docs/linux/swap","8f5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/monitoring/intro",component:f("/docs/monitoring/intro","66d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/network/load-balancing",component:f("/docs/network/load-balancing","caa"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/network/load-balancing-algorithm",component:f("/docs/network/load-balancing-algorithm","a4d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/nginx/command",component:f("/docs/nginx/command","0e3"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/nginx/static-file",component:f("/docs/nginx/static-file","629"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/performance/throughput",component:f("/docs/performance/throughput","56a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/performance/throughput-latency",component:f("/docs/performance/throughput-latency","12a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/performance/types",component:f("/docs/performance/types","c08"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/test/benefit",component:f("/docs/test/benefit","163"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/test/first",component:f("/docs/test/first","566"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/test/heuristics",component:f("/docs/test/heuristics","a1d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/test/stairstep",component:f("/docs/test/stairstep","364"),exact:!0,sidebar:"tutorialSidebar"}]},{path:"/",component:f("/","3ce"),exact:!0},{path:"/",component:f("/","f16"),exact:!0},{path:"*",component:f("*")}]},98934:(e,t,n)=>{"use strict";n.d(t,{_:()=>r,t:()=>o});var a=n(67294);const r=a.createContext(!1);function o(e){let{children:t}=e;const[n,o]=(0,a.useState)(!1);return(0,a.useEffect)((()=>{o(!0)}),[]),a.createElement(r.Provider,{value:n},t)}},49383:(e,t,n)=>{"use strict";var a=n(67294),r=n(73935),o=n(73727),i=n(70405),s=n(10412);const l=[n(56657),n(32497),n(3310),n(18320),n(52295)];var c=n(723),u=n(16550),d=n(18790);function f(e){let{children:t}=e;return a.createElement(a.Fragment,null,t)}var p=n(87462),g=n(35742),m=n(52263),h=n(44996),b=n(86668),v=n(10833),y=n(94711),w=n(19727),_=n(43320),k=n(90197);function x(){const{i18n:{defaultLocale:e,localeConfigs:t}}=(0,m.Z)(),n=(0,y.l)();return a.createElement(g.Z,null,Object.entries(t).map((e=>{let[t,{htmlLang:r}]=e;return a.createElement("link",{key:t,rel:"alternate",href:n.createUrl({locale:t,fullyQualified:!0}),hrefLang:r})})),a.createElement("link",{rel:"alternate",href:n.createUrl({locale:e,fullyQualified:!0}),hrefLang:"x-default"}))}function E(e){let{permalink:t}=e;const{siteConfig:{url:n}}=(0,m.Z)(),r=function(){const{siteConfig:{url:e}}=(0,m.Z)(),{pathname:t}=(0,u.TH)();return e+(0,h.Z)(t)}(),o=t?`${n}${t}`:r;return a.createElement(g.Z,null,a.createElement("meta",{property:"og:url",content:o}),a.createElement("link",{rel:"canonical",href:o}))}function S(){const{i18n:{currentLocale:e}}=(0,m.Z)(),{metadata:t,image:n}=(0,b.L)();return a.createElement(a.Fragment,null,a.createElement(g.Z,null,a.createElement("meta",{name:"twitter:card",content:"summary_large_image"}),a.createElement("body",{className:w.h})),n&&a.createElement(v.d,{image:n}),a.createElement(E,null),a.createElement(x,null),a.createElement(k.Z,{tag:_.HX,locale:e}),a.createElement(g.Z,null,t.map(((e,t)=>a.createElement("meta",(0,p.Z)({key:t},e))))))}const T=new Map;function C(e){if(T.has(e.pathname))return{...e,pathname:T.get(e.pathname)};if((0,d.f)(c.Z,e.pathname).some((e=>{let{route:t}=e;return!0===t.exact})))return T.set(e.pathname,e.pathname),e;const t=e.pathname.trim().replace(/(?:\/index)?\.html$/,"")||"/";return T.set(e.pathname,t),{...e,pathname:t}}var A=n(98934),L=n(58940);function P(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),a=1;a(t.default?.[e]??t[e])?.(...n)));return()=>r.forEach((e=>e?.()))}const N=function(e){let{children:t,location:n,previousLocation:r}=e;return(0,a.useLayoutEffect)((()=>{r!==n&&(!function(e){let{location:t,previousLocation:n}=e;if(!n)return;const a=t.pathname===n.pathname,r=t.hash===n.hash,o=t.search===n.search;if(a&&r&&!o)return;const{hash:i}=t;if(i){const e=decodeURIComponent(i.substring(1));document.getElementById(e)?.scrollIntoView()}else window.scrollTo(0,0)}({location:n,previousLocation:r}),P("onRouteDidUpdate",{previousLocation:r,location:n}))}),[r,n]),t};function O(e){const t=Array.from(new Set([e,decodeURI(e)])).map((e=>(0,d.f)(c.Z,e))).flat();return Promise.all(t.map((e=>e.route.component.preload?.())))}class R extends a.Component{constructor(e){super(e),this.previousLocation=void 0,this.routeUpdateCleanupCb=void 0,this.previousLocation=null,this.routeUpdateCleanupCb=s.Z.canUseDOM?P("onRouteUpdate",{previousLocation:null,location:this.props.location}):()=>{},this.state={nextRouteHasLoaded:!0}}shouldComponentUpdate(e,t){if(e.location===this.props.location)return t.nextRouteHasLoaded;const n=e.location;return this.previousLocation=this.props.location,this.setState({nextRouteHasLoaded:!1}),this.routeUpdateCleanupCb=P("onRouteUpdate",{previousLocation:this.previousLocation,location:n}),O(n.pathname).then((()=>{this.routeUpdateCleanupCb(),this.setState({nextRouteHasLoaded:!0})})).catch((e=>{console.warn(e),window.location.reload()})),!1}render(){const{children:e,location:t}=this.props;return a.createElement(N,{previousLocation:this.previousLocation,location:t},a.createElement(u.AW,{location:t,render:()=>e}))}}const M=R,I="docusaurus-base-url-issue-banner-container",j="docusaurus-base-url-issue-banner-suggestion-container",D="__DOCUSAURUS_INSERT_BASEURL_BANNER";function B(e){return`\nwindow['${D}'] = true;\n\ndocument.addEventListener('DOMContentLoaded', maybeInsertBanner);\n\nfunction maybeInsertBanner() {\n var shouldInsert = window['${D}'];\n shouldInsert && insertBanner();\n}\n\nfunction insertBanner() {\n var bannerContainer = document.getElementById('${I}');\n if (!bannerContainer) {\n return;\n }\n var bannerHtml = ${JSON.stringify(function(e){return`\n
\n

Your Docusaurus site did not load properly.

\n

A very common reason is a wrong site baseUrl configuration.

\n

Current configured baseUrl = ${e} ${"/"===e?" (default value)":""}

\n

We suggest trying baseUrl =

\n
\n`}(e)).replace(/{window[D]=!1}),[]),a.createElement(a.Fragment,null,!s.Z.canUseDOM&&a.createElement(g.Z,null,a.createElement("script",null,B(e))),a.createElement("div",{id:I}))}function $(){const{siteConfig:{baseUrl:e,baseUrlIssueBanner:t}}=(0,m.Z)(),{pathname:n}=(0,u.TH)();return t&&n===e?a.createElement(F,null):null}function U(){const{siteConfig:{favicon:e,title:t,noIndex:n},i18n:{currentLocale:r,localeConfigs:o}}=(0,m.Z)(),i=(0,h.Z)(e),{htmlLang:s,direction:l}=o[r];return a.createElement(g.Z,null,a.createElement("html",{lang:s,dir:l}),a.createElement("title",null,t),a.createElement("meta",{property:"og:title",content:t}),a.createElement("meta",{name:"viewport",content:"width=device-width, initial-scale=1.0"}),n&&a.createElement("meta",{name:"robots",content:"noindex, nofollow"}),e&&a.createElement("link",{rel:"icon",href:i}))}var z=n(44763);function Z(){const e=(0,d.H)(c.Z),t=(0,u.TH)();return a.createElement(z.Z,null,a.createElement(L.M,null,a.createElement(A.t,null,a.createElement(f,null,a.createElement(U,null),a.createElement(S,null),a.createElement($,null),a.createElement(M,{location:C(t)},e)))))}var H=n(16887);const V=function(e){try{return document.createElement("link").relList.supports(e)}catch{return!1}}("prefetch")?function(e){return new Promise(((t,n)=>{if("undefined"==typeof document)return void n();const a=document.createElement("link");a.setAttribute("rel","prefetch"),a.setAttribute("href",e),a.onload=()=>t(),a.onerror=()=>n();(document.getElementsByTagName("head")[0]??document.getElementsByName("script")[0]?.parentNode)?.appendChild(a)}))}:function(e){return new Promise(((t,n)=>{const a=new XMLHttpRequest;a.open("GET",e,!0),a.withCredentials=!0,a.onload=()=>{200===a.status?t():n()},a.send(null)}))};var W=n(99670);const G=new Set,q=new Set,K=()=>navigator.connection?.effectiveType.includes("2g")||navigator.connection?.saveData,Y={prefetch(e){if(!(e=>!K()&&!q.has(e)&&!G.has(e))(e))return!1;G.add(e);const t=(0,d.f)(c.Z,e).flatMap((e=>{return t=e.route.path,Object.entries(H).filter((e=>{let[n]=e;return n.replace(/-[^-]+$/,"")===t})).flatMap((e=>{let[,t]=e;return Object.values((0,W.Z)(t))}));var t}));return Promise.all(t.map((e=>{const t=n.gca(e);return t&&!t.includes("undefined")?V(t).catch((()=>{})):Promise.resolve()})))},preload:e=>!!(e=>!K()&&!q.has(e))(e)&&(q.add(e),O(e))},Q=Object.freeze(Y);if(s.Z.canUseDOM){window.docusaurus=Q;const e=r.hydrate;O(window.location.pathname).then((()=>{e(a.createElement(i.B6,null,a.createElement(o.VK,null,a.createElement(Z,null))),document.getElementById("__docusaurus"))}))}},58940:(e,t,n)=>{"use strict";n.d(t,{_:()=>u,M:()=>d});var a=n(67294),r=n(36809);const o=JSON.parse('{"docusaurus-plugin-google-gtag":{"default":{"trackingID":"G-17TREGCW4H","anonymizeIP":true,"id":"default"}},"docusaurus-plugin-content-docs":{"default":{"path":"/docs","versions":[{"name":"current","label":"Next","isLast":true,"path":"/docs","mainDocId":"intro","docs":[{"id":"intro","path":"/docs/","sidebar":"tutorialSidebar"},{"id":"JPA/\uae30\ubcf8_\ud0a4_\ub9e4\ud551","path":"/docs/jpa/key","sidebar":"tutorialSidebar"},{"id":"Nginx/\uad6c\uc870_\ubc0f_\uba85\ub839\uc5b4","path":"/docs/nginx/command","sidebar":"tutorialSidebar"},{"id":"Nginx/\uc815\uc801_\ucee8\ud150\uce20_\uc81c\uacf5","path":"/docs/nginx/static-file","sidebar":"tutorialSidebar"},{"id":"\uae30\ud0c0/\uac74\uac15\ud558\uac8c_\ub098\uc544\uc9c0\uae30","path":"/docs/etc/healthful-growth","sidebar":"tutorialSidebar"},{"id":"\uae30\ud0c0/\uc2a4\ud504\ub9c1\uacfc \ud568\uaed8 \ub354 \ub098\uc740 \uac1c\ubc1c\uc790 \ub418\uae30","path":"/docs/etc/develop-with-spring","sidebar":"tutorialSidebar"},{"id":"\uae30\ud0c0/\uc790\uae30 \uc8fc\ub3c4\uc801\uc73c\ub85c \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uae30 \uc704\ud55c \ub3c4\uc804 \uacbd\ud5d8","path":"/docs/etc/experience-and-self-question","sidebar":"tutorialSidebar"},{"id":"\uae30\ud0c0/\ucee4\ubba4\ub2c8\ucf00\uc774\uc158 \uc798\ud558\ub294 \uac1c\ubc1c\uc790\uc758 4\uac00\uc9c0 \uc2b5\uad00","path":"/docs/etc/communication","sidebar":"tutorialSidebar"},{"id":"\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1","path":"/docs/network/load-balancing","sidebar":"tutorialSidebar"},{"id":"\ub124\ud2b8\uc6cc\ud06c/\ub85c\ub4dc \ubc38\ub7f0\uc2f1 \uc54c\uace0\ub9ac\uc998","path":"/docs/network/load-balancing-algorithm","sidebar":"tutorialSidebar"},{"id":"\ub3c4\uc11c/\uc0c1\uc790_\ubc16\uc73c\ub85c_\ud0c8\ucd9c\ud558\uae30","path":"/docs/book/getting-out-of-the-box","sidebar":"tutorialSidebar"},{"id":"\ub9ac\ub205\uc2a4/Swap_\uba54\ubaa8\ub9ac_\uc124\uc815","path":"/docs/linux/swap","sidebar":"tutorialSidebar"},{"id":"\ub9ac\ub205\uc2a4/\ud130\ubbf8\ub110_\uc258_\ud504\ub86c\ud504\ud2b8_\uc124\uc815","path":"/docs/linux/shell","sidebar":"tutorialSidebar"},{"id":"\ubaa8\ub2c8\ud130\ub9c1/\ubaa8\ub2c8\ud130\ub9c1_\ud658\uacbd_\uad6c\uc131","path":"/docs/monitoring/intro","sidebar":"tutorialSidebar"},{"id":"\ubb38\ud654/\ud3ec\uc2a4\ud2b8_\ubaa8\ud15c","path":"/docs/culture/postmortem","sidebar":"tutorialSidebar"},{"id":"\uc124\uacc4/\ud328\ud0a4\uc9c0","path":"/docs/design/package","sidebar":"tutorialSidebar"},{"id":"\uc131\ub2a5/Throughput \ubaa9\ud46f\uac12","path":"/docs/performance/throughput","sidebar":"tutorialSidebar"},{"id":"\uc131\ub2a5/Throughput\uacfc Latency","path":"/docs/performance/throughput-latency","sidebar":"tutorialSidebar"},{"id":"\uc131\ub2a5/\uc131\ub2a5 \ud14c\uc2a4\ud2b8","path":"/docs/performance/types","sidebar":"tutorialSidebar"},{"id":"\ud14c\uc2a4\ud2b8/FIRST","path":"/docs/test/first","sidebar":"tutorialSidebar"},{"id":"\ud14c\uc2a4\ud2b8/\uacc4\ub2e8_\ud14c\uc2a4\ud2b8","path":"/docs/test/stairstep","sidebar":"tutorialSidebar"},{"id":"\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\uc8fc\ub3c4_\uac1c\ubc1c_\uaddc\uce59","path":"/docs/test/heuristics","sidebar":"tutorialSidebar"},{"id":"\ud14c\uc2a4\ud2b8/\ud14c\uc2a4\ud2b8_\ucf54\ub4dc\uac00_\uc8fc\ub294_\ud61c\ud0dd","path":"/docs/test/benefit","sidebar":"tutorialSidebar"}],"draftIds":[],"sidebars":{"tutorialSidebar":{"link":{"path":"/docs/jpa/key","label":"JPA/\uae30\ubcf8_\ud0a4_\ub9e4\ud551"}}}}],"breadcrumbs":true}}}'),i=JSON.parse('{"defaultLocale":"ko","locales":["ko"],"path":"i18n","currentLocale":"ko","localeConfigs":{"ko":{"label":"\ud55c\uad6d\uc5b4","direction":"ltr","htmlLang":"ko","calendar":"gregory","path":"ko"}}}');var s=n(57529);const l=JSON.parse('{"docusaurusVersion":"2.3.0","siteVersion":"0.0.0","pluginVersions":{"docusaurus-plugin-content-docs":{"type":"package","name":"@docusaurus/plugin-content-docs","version":"2.3.0"},"docusaurus-plugin-content-blog":{"type":"package","name":"@docusaurus/plugin-content-blog","version":"2.3.0"},"docusaurus-plugin-content-pages":{"type":"package","name":"@docusaurus/plugin-content-pages","version":"2.3.0"},"docusaurus-plugin-google-gtag":{"type":"package","name":"@docusaurus/plugin-google-gtag","version":"2.3.0"},"docusaurus-plugin-sitemap":{"type":"package","name":"@docusaurus/plugin-sitemap","version":"2.3.0"},"docusaurus-theme-classic":{"type":"package","name":"@docusaurus/theme-classic","version":"2.3.0"},"docusaurus-theme-search-algolia":{"type":"package","name":"@docusaurus/theme-search-algolia","version":"2.3.0"},"docusaurus-theme-mermaid":{"type":"package","name":"@docusaurus/theme-mermaid","version":"2.3.0"}}}'),c={siteConfig:r.default,siteMetadata:l,globalData:o,i18n:i,codeTranslations:s},u=a.createContext(c);function d(e){let{children:t}=e;return a.createElement(u.Provider,{value:c},t)}},44763:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var a=n(67294),r=n(10412),o=n(35742),i=n(54774);function s(e){let{error:t,tryAgain:n}=e;return a.createElement("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center",height:"50vh",width:"100%",fontSize:"20px"}},a.createElement("h1",null,"This page crashed."),a.createElement("p",null,t.message),a.createElement("button",{type:"button",onClick:n},"Try again"))}function l(e){let{error:t,tryAgain:n}=e;return a.createElement(u,{fallback:()=>a.createElement(s,{error:t,tryAgain:n})},a.createElement(o.Z,null,a.createElement("title",null,"Page Error")),a.createElement(i.Z,null,a.createElement(s,{error:t,tryAgain:n})))}const c=e=>a.createElement(l,e);class u extends a.Component{constructor(e){super(e),this.state={error:null}}componentDidCatch(e){r.Z.canUseDOM&&this.setState({error:e})}render(){const{children:e}=this.props,{error:t}=this.state;if(t){const e={error:t,tryAgain:()=>this.setState({error:null})};return(this.props.fallback??c)(e)}return e??null}}},10412:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});const a="undefined"!=typeof window&&"document"in window&&"createElement"in window.document,r={canUseDOM:a,canUseEventListeners:a&&("addEventListener"in window||"attachEvent"in window),canUseIntersectionObserver:a&&"IntersectionObserver"in window,canUseViewport:a&&"screen"in window}},35742:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var a=n(67294),r=n(70405);function o(e){return a.createElement(r.ql,e)}},39960:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var a=n(87462),r=n(67294),o=n(73727),i=n(18780),s=n(52263),l=n(13919),c=n(10412);const u=r.createContext({collectLink:()=>{}});var d=n(44996);function f(e,t){let{isNavLink:n,to:f,href:p,activeClassName:g,isActive:m,"data-noBrokenLinkCheck":h,autoAddBaseUrl:b=!0,...v}=e;const{siteConfig:{trailingSlash:y,baseUrl:w}}=(0,s.Z)(),{withBaseUrl:_}=(0,d.C)(),k=(0,r.useContext)(u),x=(0,r.useRef)(null);(0,r.useImperativeHandle)(t,(()=>x.current));const E=f||p;const S=(0,l.Z)(E),T=E?.replace("pathname://","");let C=void 0!==T?(A=T,b&&(e=>e.startsWith("/"))(A)?_(A):A):void 0;var A;C&&S&&(C=(0,i.applyTrailingSlash)(C,{trailingSlash:y,baseUrl:w}));const L=(0,r.useRef)(!1),P=n?o.OL:o.rU,N=c.Z.canUseIntersectionObserver,O=(0,r.useRef)(),R=()=>{L.current||null==C||(window.docusaurus.preload(C),L.current=!0)};(0,r.useEffect)((()=>(!N&&S&&null!=C&&window.docusaurus.prefetch(C),()=>{N&&O.current&&O.current.disconnect()})),[O,C,N,S]);const M=C?.startsWith("#")??!1,I=!C||!S||M;return I||h||k.collectLink(C),I?r.createElement("a",(0,a.Z)({ref:x,href:C},E&&!S&&{target:"_blank",rel:"noopener noreferrer"},v)):r.createElement(P,(0,a.Z)({},v,{onMouseEnter:R,onTouchStart:R,innerRef:e=>{x.current=e,N&&e&&S&&(O.current=new window.IntersectionObserver((t=>{t.forEach((t=>{e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(O.current.unobserve(e),O.current.disconnect(),null!=C&&window.docusaurus.prefetch(C))}))})),O.current.observe(e))},to:C},n&&{isActive:m,activeClassName:g}))}const p=r.forwardRef(f)},95999:(e,t,n)=>{"use strict";n.d(t,{Z:()=>l,I:()=>s});var a=n(67294);function r(e,t){const n=e.split(/(\{\w+\})/).map(((e,n)=>{if(n%2==1){const n=t?.[e.slice(1,-1)];if(void 0!==n)return n}return e}));return n.some((e=>(0,a.isValidElement)(e)))?n.map(((e,t)=>(0,a.isValidElement)(e)?a.cloneElement(e,{key:t}):e)).filter((e=>""!==e)):n.join("")}var o=n(57529);function i(e){let{id:t,message:n}=e;if(void 0===t&&void 0===n)throw new Error("Docusaurus translation declarations must have at least a translation id or a default translation message");return o[t??n]??n??t}function s(e,t){let{message:n,id:a}=e;return r(i({message:n,id:a}),t)}function l(e){let{children:t,id:n,values:o}=e;if(t&&"string"!=typeof t)throw console.warn("Illegal children",t),new Error("The Docusaurus component only accept simple string values");const s=i({message:t,id:n});return a.createElement(a.Fragment,null,r(s,o))}},29935:(e,t,n)=>{"use strict";n.d(t,{m:()=>a});const a="default"},13919:(e,t,n)=>{"use strict";function a(e){return/^(?:\w*:|\/\/)/.test(e)}function r(e){return void 0!==e&&!a(e)}n.d(t,{Z:()=>r,b:()=>a})},44996:(e,t,n)=>{"use strict";n.d(t,{C:()=>i,Z:()=>s});var a=n(67294),r=n(52263),o=n(13919);function i(){const{siteConfig:{baseUrl:e,url:t}}=(0,r.Z)(),n=(0,a.useCallback)(((n,a)=>function(e,t,n,a){let{forcePrependBaseUrl:r=!1,absolute:i=!1}=void 0===a?{}:a;if(!n||n.startsWith("#")||(0,o.b)(n))return n;if(r)return t+n.replace(/^\//,"");if(n===t.replace(/\/$/,""))return t;const s=n.startsWith(t)?n:t+n.replace(/^\//,"");return i?e+s:s}(t,e,n,a)),[t,e]);return{withBaseUrl:n}}function s(e,t){void 0===t&&(t={});const{withBaseUrl:n}=i();return n(e,t)}},52263:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var a=n(67294),r=n(58940);function o(){return(0,a.useContext)(r._)}},72389:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var a=n(67294),r=n(98934);function o(){return(0,a.useContext)(r._)}},99670:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});function a(e){const t={};return function e(n,a){Object.entries(n).forEach((n=>{let[r,o]=n;const i=a?`${a}.${r}`:r;var s;"object"==typeof(s=o)&&s&&Object.keys(s).length>0?e(o,i):t[i]=o}))}(e),t}},30226:(e,t,n)=>{"use strict";n.d(t,{_:()=>r,z:()=>o});var a=n(67294);const r=a.createContext(null);function o(e){let{children:t,value:n}=e;const o=a.useContext(r),i=(0,a.useMemo)((()=>function(e){let{parent:t,value:n}=e;if(!t){if(!n)throw new Error("Unexpected: no Docusaurus route context found");if(!("plugin"in n))throw new Error("Unexpected: Docusaurus topmost route context has no `plugin` attribute");return n}const a={...t.data,...n?.data};return{plugin:t.plugin,data:a}}({parent:o,value:n})),[o,n]);return a.createElement(r.Provider,{value:i},t)}},80143:(e,t,n)=>{"use strict";n.d(t,{Iw:()=>b,gA:()=>p,WS:()=>g,_r:()=>d,Jo:()=>v,zh:()=>f,yW:()=>h,gB:()=>m});var a=n(16550),r=n(52263),o=n(29935);function i(e,t){void 0===t&&(t={});const n=function(){const{globalData:e}=(0,r.Z)();return e}()[e];if(!n&&t.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin.`);return n}const s=e=>e.versions.find((e=>e.isLast));function l(e,t){const n=s(e);return[...e.versions.filter((e=>e!==n)),n].find((e=>!!(0,a.LX)(t,{path:e.path,exact:!1,strict:!1})))}function c(e,t){const n=l(e,t),r=n?.docs.find((e=>!!(0,a.LX)(t,{path:e.path,exact:!0,strict:!1})));return{activeVersion:n,activeDoc:r,alternateDocVersions:r?function(t){const n={};return e.versions.forEach((e=>{e.docs.forEach((a=>{a.id===t&&(n[e.name]=a)}))})),n}(r.id):{}}}const u={},d=()=>i("docusaurus-plugin-content-docs")??u,f=e=>function(e,t,n){void 0===t&&(t=o.m),void 0===n&&(n={});const a=i(e)?.[t];if(!a&&n.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin with id "${t}".`);return a}("docusaurus-plugin-content-docs",e,{failfast:!0});function p(e){void 0===e&&(e={});const t=d(),{pathname:n}=(0,a.TH)();return function(e,t,n){void 0===n&&(n={});const r=Object.entries(e).sort(((e,t)=>t[1].path.localeCompare(e[1].path))).find((e=>{let[,n]=e;return!!(0,a.LX)(t,{path:n.path,exact:!1,strict:!1})})),o=r?{pluginId:r[0],pluginData:r[1]}:void 0;if(!o&&n.failfast)throw new Error(`Can't find active docs plugin for "${t}" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: ${Object.values(e).map((e=>e.path)).join(", ")}`);return o}(t,n,e)}function g(e){void 0===e&&(e={});const t=p(e),{pathname:n}=(0,a.TH)();if(!t)return;return{activePlugin:t,activeVersion:l(t.pluginData,n)}}function m(e){return f(e).versions}function h(e){const t=f(e);return s(t)}function b(e){const t=f(e),{pathname:n}=(0,a.TH)();return c(t,n)}function v(e){const t=f(e),{pathname:n}=(0,a.TH)();return function(e,t){const n=s(e);return{latestDocSuggestion:c(e,t).alternateDocVersions[n.name],latestVersionSuggestion:n}}(t,n)}},56657:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});const a={onRouteDidUpdate(e){let{location:t,previousLocation:n}=e;!n||t.pathname===n.pathname&&t.search===n.search&&t.hash===n.hash||setTimeout((()=>{window.gtag("event","page_view",{page_title:document.title,page_location:window.location.href,page_path:t.pathname+t.search+t.hash})}))}}},18320:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var a=n(74865),r=n.n(a);r().configure({showSpinner:!1});const o={onRouteUpdate(e){let{location:t,previousLocation:n}=e;if(n&&t.pathname!==n.pathname){const e=window.setTimeout((()=>{r().start()}),200);return()=>window.clearTimeout(e)}},onRouteDidUpdate(){r().done()}}},3310:(e,t,n)=>{"use strict";n.r(t);var a=n(87410),r=n(36809);!function(e){const{themeConfig:{prism:t}}=r.default,{additionalLanguages:a}=t;globalThis.Prism=e,a.forEach((e=>{n(52811)(`./prism-${e}`)})),delete globalThis.Prism}(a.Z)},39471:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var a=n(67294);const r="iconExternalLink_nPIU";function o(e){let{width:t=13.5,height:n=13.5}=e;return a.createElement("svg",{width:t,height:n,"aria-hidden":"true",viewBox:"0 0 24 24",className:r},a.createElement("path",{fill:"currentColor",d:"M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"}))}},54774:(e,t,n)=>{"use strict";n.d(t,{Z:()=>Ct});var a=n(67294),r=n(86010),o=n(44763),i=n(10833),s=n(87462),l=n(16550),c=n(95999),u=n(85936);const d="docusaurus_skipToContent_fallback";function f(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}function p(){const e=(0,a.useRef)(null),{action:t}=(0,l.k6)(),n=(0,a.useCallback)((e=>{e.preventDefault();const t=document.querySelector("main:first-of-type")??document.getElementById(d);t&&f(t)}),[]);return(0,u.S)((n=>{let{location:a}=n;e.current&&!a.hash&&"PUSH"===t&&f(e.current)})),{containerRef:e,onClick:n}}const g=(0,c.I)({id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation",message:"Skip to main content"});function m(e){const t=e.children??g,{containerRef:n,onClick:r}=p();return a.createElement("div",{ref:n,role:"region","aria-label":g},a.createElement("a",(0,s.Z)({},e,{href:`#${d}`,onClick:r}),t))}var h=n(35281),b=n(19727);const v="skipToContent_fXgn";function y(){return a.createElement(m,{className:v})}var w=n(86668),_=n(59689);function k(e){let{width:t=21,height:n=21,color:r="currentColor",strokeWidth:o=1.2,className:i,...l}=e;return a.createElement("svg",(0,s.Z)({viewBox:"0 0 15 15",width:t,height:n},l),a.createElement("g",{stroke:r,strokeWidth:o},a.createElement("path",{d:"M.75.75l13.5 13.5M14.25.75L.75 14.25"})))}const x="closeButton_CVFx";function E(e){return a.createElement("button",(0,s.Z)({type:"button","aria-label":(0,c.I)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"})},e,{className:(0,r.Z)("clean-btn close",x,e.className)}),a.createElement(k,{width:14,height:14,strokeWidth:3.1}))}const S="content_knG7";function T(e){const{announcementBar:t}=(0,w.L)(),{content:n}=t;return a.createElement("div",(0,s.Z)({},e,{className:(0,r.Z)(S,e.className),dangerouslySetInnerHTML:{__html:n}}))}const C="announcementBar_mb4j",A="announcementBarPlaceholder_vyr4",L="announcementBarClose_gvF7",P="announcementBarContent_xLdY";function N(){const{announcementBar:e}=(0,w.L)(),{isActive:t,close:n}=(0,_.nT)();if(!t)return null;const{backgroundColor:r,textColor:o,isCloseable:i}=e;return a.createElement("div",{className:C,style:{backgroundColor:r,color:o},role:"banner"},i&&a.createElement("div",{className:A}),a.createElement(T,{className:P}),i&&a.createElement(E,{onClick:n,className:L}))}var O=n(93163),R=n(12466);var M=n(902),I=n(13102);const j=a.createContext(null);function D(e){let{children:t}=e;const n=function(){const e=(0,O.e)(),t=(0,I.HY)(),[n,r]=(0,a.useState)(!1),o=null!==t.component,i=(0,M.D9)(o);return(0,a.useEffect)((()=>{o&&!i&&r(!0)}),[o,i]),(0,a.useEffect)((()=>{o?e.shown||r(!0):r(!1)}),[e.shown,o]),(0,a.useMemo)((()=>[n,r]),[n])}();return a.createElement(j.Provider,{value:n},t)}function B(e){if(e.component){const t=e.component;return a.createElement(t,e.props)}}function F(){const e=(0,a.useContext)(j);if(!e)throw new M.i6("NavbarSecondaryMenuDisplayProvider");const[t,n]=e,r=(0,a.useCallback)((()=>n(!1)),[n]),o=(0,I.HY)();return(0,a.useMemo)((()=>({shown:t,hide:r,content:B(o)})),[r,o,t])}function $(e){let{header:t,primaryMenu:n,secondaryMenu:o}=e;const{shown:i}=F();return a.createElement("div",{className:"navbar-sidebar"},t,a.createElement("div",{className:(0,r.Z)("navbar-sidebar__items",{"navbar-sidebar__items--show-secondary":i})},a.createElement("div",{className:"navbar-sidebar__item menu"},n),a.createElement("div",{className:"navbar-sidebar__item menu"},o)))}var U=n(92949),z=n(72389);function Z(e){return a.createElement("svg",(0,s.Z)({viewBox:"0 0 24 24",width:24,height:24},e),a.createElement("path",{fill:"currentColor",d:"M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"}))}function H(e){return a.createElement("svg",(0,s.Z)({viewBox:"0 0 24 24",width:24,height:24},e),a.createElement("path",{fill:"currentColor",d:"M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"}))}const V={toggle:"toggle_vylO",toggleButton:"toggleButton_gllP",darkToggleIcon:"darkToggleIcon_wfgR",lightToggleIcon:"lightToggleIcon_pyhR",toggleButtonDisabled:"toggleButtonDisabled_aARS"};function W(e){let{className:t,value:n,onChange:o}=e;const i=(0,z.Z)(),s=(0,c.I)({message:"Switch between dark and light mode (currently {mode})",id:"theme.colorToggle.ariaLabel",description:"The ARIA label for the navbar color mode toggle"},{mode:"dark"===n?(0,c.I)({message:"dark mode",id:"theme.colorToggle.ariaLabel.mode.dark",description:"The name for the dark color mode"}):(0,c.I)({message:"light mode",id:"theme.colorToggle.ariaLabel.mode.light",description:"The name for the light color mode"})});return a.createElement("div",{className:(0,r.Z)(V.toggle,t)},a.createElement("button",{className:(0,r.Z)("clean-btn",V.toggleButton,!i&&V.toggleButtonDisabled),type:"button",onClick:()=>o("dark"===n?"light":"dark"),disabled:!i,title:s,"aria-label":s,"aria-live":"polite"},a.createElement(Z,{className:(0,r.Z)(V.toggleIcon,V.lightToggleIcon)}),a.createElement(H,{className:(0,r.Z)(V.toggleIcon,V.darkToggleIcon)})))}const G=a.memo(W);function q(e){let{className:t}=e;const n=(0,w.L)().colorMode.disableSwitch,{colorMode:r,setColorMode:o}=(0,U.I)();return n?null:a.createElement(G,{className:t,value:r,onChange:o})}var K=n(21327);function Y(){return a.createElement(K.Z,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title text--truncate"})}function Q(){const e=(0,O.e)();return a.createElement("button",{type:"button","aria-label":(0,c.I)({id:"theme.docs.sidebar.closeSidebarButtonAriaLabel",message:"Close navigation bar",description:"The ARIA label for close button of mobile sidebar"}),className:"clean-btn navbar-sidebar__close",onClick:()=>e.toggle()},a.createElement(k,{color:"var(--ifm-color-emphasis-600)"}))}function X(){return a.createElement("div",{className:"navbar-sidebar__brand"},a.createElement(Y,null),a.createElement(q,{className:"margin-right--md"}),a.createElement(Q,null))}var J=n(39960),ee=n(44996),te=n(13919),ne=n(98022),ae=n(39471);function re(e){let{activeBasePath:t,activeBaseRegex:n,to:r,href:o,label:i,html:l,isDropdownLink:c,prependBaseUrlToHref:u,...d}=e;const f=(0,ee.Z)(r),p=(0,ee.Z)(t),g=(0,ee.Z)(o,{forcePrependBaseUrl:!0}),m=i&&o&&!(0,te.Z)(o),h=l?{dangerouslySetInnerHTML:{__html:l}}:{children:a.createElement(a.Fragment,null,i,m&&a.createElement(ae.Z,c&&{width:12,height:12}))};return o?a.createElement(J.Z,(0,s.Z)({href:u?g:o},d,h)):a.createElement(J.Z,(0,s.Z)({to:f,isNavLink:!0},(t||n)&&{isActive:(e,t)=>n?(0,ne.F)(n,t.pathname):t.pathname.startsWith(p)},d,h))}function oe(e){let{className:t,isDropdownItem:n=!1,...o}=e;const i=a.createElement(re,(0,s.Z)({className:(0,r.Z)(n?"dropdown__link":"navbar__item navbar__link",t),isDropdownLink:n},o));return n?a.createElement("li",null,i):i}function ie(e){let{className:t,isDropdownItem:n,...o}=e;return a.createElement("li",{className:"menu__list-item"},a.createElement(re,(0,s.Z)({className:(0,r.Z)("menu__link",t)},o)))}function se(e){let{mobile:t=!1,position:n,...r}=e;const o=t?ie:oe;return a.createElement(o,(0,s.Z)({},r,{activeClassName:r.activeClassName??(t?"menu__link--active":"navbar__link--active")}))}var le=n(86043),ce=n(48596),ue=n(52263);function de(e,t){return e.some((e=>function(e,t){return!!(0,ce.Mg)(e.to,t)||!!(0,ne.F)(e.activeBaseRegex,t)||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t)))}function fe(e){let{items:t,position:n,className:o,onClick:i,...l}=e;const c=(0,a.useRef)(null),[u,d]=(0,a.useState)(!1);return(0,a.useEffect)((()=>{const e=e=>{c.current&&!c.current.contains(e.target)&&d(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e)}}),[c]),a.createElement("div",{ref:c,className:(0,r.Z)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===n,"dropdown--show":u})},a.createElement(re,(0,s.Z)({"aria-haspopup":"true","aria-expanded":u,role:"button",href:l.to?void 0:"#",className:(0,r.Z)("navbar__link",o)},l,{onClick:l.to?void 0:e=>e.preventDefault(),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),d(!u))}}),l.children??l.label),a.createElement("ul",{className:"dropdown__menu"},t.map(((e,n)=>a.createElement(Ve,(0,s.Z)({isDropdownItem:!0,onKeyDown:e=>{if(n===t.length-1&&"Tab"===e.key){e.preventDefault(),d(!1);const t=c.current.nextElementSibling;if(t){(t instanceof HTMLAnchorElement?t:t.querySelector("a")).focus()}}},activeClassName:"dropdown__link--active"},e,{key:n}))))))}function pe(e){let{items:t,className:n,position:o,onClick:i,...c}=e;const u=function(){const{siteConfig:{baseUrl:e}}=(0,ue.Z)(),{pathname:t}=(0,l.TH)();return t.replace(e,"/")}(),d=de(t,u),{collapsed:f,toggleCollapsed:p,setCollapsed:g}=(0,le.u)({initialState:()=>!d});return(0,a.useEffect)((()=>{d&&g(!d)}),[u,d,g]),a.createElement("li",{className:(0,r.Z)("menu__list-item",{"menu__list-item--collapsed":f})},a.createElement(re,(0,s.Z)({role:"button",className:(0,r.Z)("menu__link menu__link--sublist menu__link--sublist-caret",n)},c,{onClick:e=>{e.preventDefault(),p()}}),c.children??c.label),a.createElement(le.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:f},t.map(((e,t)=>a.createElement(Ve,(0,s.Z)({mobile:!0,isDropdownItem:!0,onClick:i,activeClassName:"menu__link--active"},e,{key:t}))))))}function ge(e){let{mobile:t=!1,...n}=e;const r=t?pe:fe;return a.createElement(r,n)}var me=n(94711);function he(e){let{width:t=20,height:n=20,...r}=e;return a.createElement("svg",(0,s.Z)({viewBox:"0 0 24 24",width:t,height:n,"aria-hidden":!0},r),a.createElement("path",{fill:"currentColor",d:"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"}))}const be="iconLanguage_nlXk";function ve(){return a.createElement("svg",{width:"15",height:"15",className:"DocSearch-Control-Key-Icon"},a.createElement("path",{d:"M4.505 4.496h2M5.505 5.496v5M8.216 4.496l.055 5.993M10 7.5c.333.333.5.667.5 1v2M12.326 4.5v5.996M8.384 4.496c1.674 0 2.116 0 2.116 1.5s-.442 1.5-2.116 1.5M3.205 9.303c-.09.448-.277 1.21-1.241 1.203C1 10.5.5 9.513.5 8V7c0-1.57.5-2.5 1.464-2.494.964.006 1.134.598 1.24 1.342M12.553 10.5h1.953",strokeWidth:"1.2",stroke:"currentColor",fill:"none",strokeLinecap:"square"}))}var ye=n(20830),we=["translations"];function _e(){return _e=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,a=new Array(t);n=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var Se="Ctrl";var Te=a.forwardRef((function(e,t){var n=e.translations,r=void 0===n?{}:n,o=Ee(e,we),i=r.buttonText,s=void 0===i?"Search":i,l=r.buttonAriaLabel,c=void 0===l?"Search":l,u=ke((0,a.useState)(null),2),d=u[0],f=u[1];return(0,a.useEffect)((function(){"undefined"!=typeof navigator&&(/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)?f("\u2318"):f(Se))}),[]),a.createElement("button",_e({type:"button",className:"DocSearch DocSearch-Button","aria-label":c},o,{ref:t}),a.createElement("span",{className:"DocSearch-Button-Container"},a.createElement(ye.W,null),a.createElement("span",{className:"DocSearch-Button-Placeholder"},s)),a.createElement("span",{className:"DocSearch-Button-Keys"},null!==d&&a.createElement(a.Fragment,null,a.createElement("kbd",{className:"DocSearch-Button-Key"},d===Se?a.createElement(ve,null):d),a.createElement("kbd",{className:"DocSearch-Button-Key"},"K"))))})),Ce=n(35742),Ae=n(66177),Le=n(239),Pe=n(43320);var Ne=n(73935);const Oe={button:{buttonText:(0,c.I)({id:"theme.SearchBar.label",message:"Search",description:"The ARIA label and placeholder for search button"}),buttonAriaLabel:(0,c.I)({id:"theme.SearchBar.label",message:"Search",description:"The ARIA label and placeholder for search button"})},modal:{searchBox:{resetButtonTitle:(0,c.I)({id:"theme.SearchModal.searchBox.resetButtonTitle",message:"Clear the query",description:"The label and ARIA label for search box reset button"}),resetButtonAriaLabel:(0,c.I)({id:"theme.SearchModal.searchBox.resetButtonTitle",message:"Clear the query",description:"The label and ARIA label for search box reset button"}),cancelButtonText:(0,c.I)({id:"theme.SearchModal.searchBox.cancelButtonText",message:"Cancel",description:"The label and ARIA label for search box cancel button"}),cancelButtonAriaLabel:(0,c.I)({id:"theme.SearchModal.searchBox.cancelButtonText",message:"Cancel",description:"The label and ARIA label for search box cancel button"})},startScreen:{recentSearchesTitle:(0,c.I)({id:"theme.SearchModal.startScreen.recentSearchesTitle",message:"Recent",description:"The title for recent searches"}),noRecentSearchesText:(0,c.I)({id:"theme.SearchModal.startScreen.noRecentSearchesText",message:"No recent searches",description:"The text when no recent searches"}),saveRecentSearchButtonTitle:(0,c.I)({id:"theme.SearchModal.startScreen.saveRecentSearchButtonTitle",message:"Save this search",description:"The label for save recent search button"}),removeRecentSearchButtonTitle:(0,c.I)({id:"theme.SearchModal.startScreen.removeRecentSearchButtonTitle",message:"Remove this search from history",description:"The label for remove recent search button"}),favoriteSearchesTitle:(0,c.I)({id:"theme.SearchModal.startScreen.favoriteSearchesTitle",message:"Favorite",description:"The title for favorite searches"}),removeFavoriteSearchButtonTitle:(0,c.I)({id:"theme.SearchModal.startScreen.removeFavoriteSearchButtonTitle",message:"Remove this search from favorites",description:"The label for remove favorite search button"})},errorScreen:{titleText:(0,c.I)({id:"theme.SearchModal.errorScreen.titleText",message:"Unable to fetch results",description:"The title for error screen of search modal"}),helpText:(0,c.I)({id:"theme.SearchModal.errorScreen.helpText",message:"You might want to check your network connection.",description:"The help text for error screen of search modal"})},footer:{selectText:(0,c.I)({id:"theme.SearchModal.footer.selectText",message:"to select",description:"The explanatory text of the action for the enter key"}),selectKeyAriaLabel:(0,c.I)({id:"theme.SearchModal.footer.selectKeyAriaLabel",message:"Enter key",description:"The ARIA label for the Enter key button that makes the selection"}),navigateText:(0,c.I)({id:"theme.SearchModal.footer.navigateText",message:"to navigate",description:"The explanatory text of the action for the Arrow up and Arrow down key"}),navigateUpKeyAriaLabel:(0,c.I)({id:"theme.SearchModal.footer.navigateUpKeyAriaLabel",message:"Arrow up",description:"The ARIA label for the Arrow up key button that makes the navigation"}),navigateDownKeyAriaLabel:(0,c.I)({id:"theme.SearchModal.footer.navigateDownKeyAriaLabel",message:"Arrow down",description:"The ARIA label for the Arrow down key button that makes the navigation"}),closeText:(0,c.I)({id:"theme.SearchModal.footer.closeText",message:"to close",description:"The explanatory text of the action for Escape key"}),closeKeyAriaLabel:(0,c.I)({id:"theme.SearchModal.footer.closeKeyAriaLabel",message:"Escape key",description:"The ARIA label for the Escape key button that close the modal"}),searchByText:(0,c.I)({id:"theme.SearchModal.footer.searchByText",message:"Search by",description:"The text explain that the search is making by Algolia"})},noResultsScreen:{noResultsText:(0,c.I)({id:"theme.SearchModal.noResultsScreen.noResultsText",message:"No results for",description:"The text explains that there are no results for the following search"}),suggestedQueryText:(0,c.I)({id:"theme.SearchModal.noResultsScreen.suggestedQueryText",message:"Try searching for",description:"The text for the suggested query when no results are found for the following search"}),reportMissingResultsText:(0,c.I)({id:"theme.SearchModal.noResultsScreen.reportMissingResultsText",message:"Believe this query should return results?",description:"The text for the question where the user thinks there are missing results"}),reportMissingResultsLinkText:(0,c.I)({id:"theme.SearchModal.noResultsScreen.reportMissingResultsLinkText",message:"Let us know.",description:"The text for the link to report missing results"})}},placeholder:(0,c.I)({id:"theme.SearchModal.placeholder",message:"Search docs",description:"The placeholder of the input of the DocSearch pop-up modal"})};let Re=null;function Me(e){let{hit:t,children:n}=e;return a.createElement(J.Z,{to:t.url},n)}function Ie(e){let{state:t,onClose:n}=e;const{generateSearchPageLink:r}=(0,Ae.O)();return a.createElement(J.Z,{to:r(t.query),onClick:n},a.createElement(c.Z,{id:"theme.SearchBar.seeAll",values:{count:t.context.nbHits}},"See all {count} results"))}function je(e){let{contextualSearch:t,externalUrlRegex:r,...o}=e;const{siteMetadata:i}=(0,ue.Z)(),c=(0,Le.l)(),u=function(){const{locale:e,tags:t}=(0,Pe._q)();return[`language:${e}`,t.map((e=>`docusaurus_tag:${e}`))]}(),d=o.searchParameters?.facetFilters??[],f=t?function(e,t){const n=e=>"string"==typeof e?[e]:e;return[...n(e),...n(t)]}(u,d):d,p={...o.searchParameters,facetFilters:f},g=(0,l.k6)(),m=(0,a.useRef)(null),h=(0,a.useRef)(null),[b,v]=(0,a.useState)(!1),[y,w]=(0,a.useState)(void 0),_=(0,a.useCallback)((()=>Re?Promise.resolve():Promise.all([n.e(6780).then(n.bind(n,76780)),Promise.all([n.e(532),n.e(6945)]).then(n.bind(n,46945)),Promise.all([n.e(532),n.e(2090)]).then(n.bind(n,18894))]).then((e=>{let[{DocSearchModal:t}]=e;Re=t}))),[]),k=(0,a.useCallback)((()=>{_().then((()=>{m.current=document.createElement("div"),document.body.insertBefore(m.current,document.body.firstChild),v(!0)}))}),[_,v]),x=(0,a.useCallback)((()=>{v(!1),m.current?.remove()}),[v]),E=(0,a.useCallback)((e=>{_().then((()=>{v(!0),w(e.key)}))}),[_,v,w]),S=(0,a.useRef)({navigate(e){let{itemUrl:t}=e;(0,ne.F)(r,t)?window.location.href=t:g.push(t)}}).current,T=(0,a.useRef)((e=>o.transformItems?o.transformItems(e):e.map((e=>({...e,url:c(e.url)}))))).current,C=(0,a.useMemo)((()=>e=>a.createElement(Ie,(0,s.Z)({},e,{onClose:x}))),[x]),A=(0,a.useCallback)((e=>(e.addAlgoliaAgent("docusaurus",i.docusaurusVersion),e)),[i.docusaurusVersion]);return function(e){var t=e.isOpen,n=e.onOpen,r=e.onClose,o=e.onInput,i=e.searchButtonRef;a.useEffect((function(){function e(e){(27===e.keyCode&&t||"k"===e.key.toLowerCase()&&(e.metaKey||e.ctrlKey)||!function(e){var t=e.target,n=t.tagName;return t.isContentEditable||"INPUT"===n||"SELECT"===n||"TEXTAREA"===n}(e)&&"/"===e.key&&!t)&&(e.preventDefault(),t?r():document.body.classList.contains("DocSearch--active")||document.body.classList.contains("DocSearch--active")||n()),i&&i.current===document.activeElement&&o&&/[a-zA-Z0-9]/.test(String.fromCharCode(e.keyCode))&&o(e)}return window.addEventListener("keydown",e),function(){window.removeEventListener("keydown",e)}}),[t,n,r,o,i])}({isOpen:b,onOpen:k,onClose:x,onInput:E,searchButtonRef:h}),a.createElement(a.Fragment,null,a.createElement(Ce.Z,null,a.createElement("link",{rel:"preconnect",href:`https://${o.appId}-dsn.algolia.net`,crossOrigin:"anonymous"})),a.createElement(Te,{onTouchStart:_,onFocus:_,onMouseOver:_,onClick:k,ref:h,translations:Oe.button}),b&&Re&&m.current&&(0,Ne.createPortal)(a.createElement(Re,(0,s.Z)({onClose:x,initialScrollY:window.scrollY,initialQuery:y,navigator:S,transformItems:T,hitComponent:Me,transformSearchClient:A},o.searchPagePath&&{resultsFooterComponent:C},o,{searchParameters:p,placeholder:Oe.placeholder,translations:Oe.modal})),m.current))}function De(){const{siteConfig:e}=(0,ue.Z)();return a.createElement(je,e.themeConfig.algolia)}const Be="searchBox_ZlJk";function Fe(e){let{children:t,className:n}=e;return a.createElement("div",{className:(0,r.Z)(n,Be)},t)}var $e=n(80143),Ue=n(53438);var ze=n(60373);const Ze=e=>e.docs.find((t=>t.id===e.mainDocId));const He={default:se,localeDropdown:function(e){let{mobile:t,dropdownItemsBefore:n,dropdownItemsAfter:r,...o}=e;const{i18n:{currentLocale:i,locales:u,localeConfigs:d}}=(0,ue.Z)(),f=(0,me.l)(),{search:p,hash:g}=(0,l.TH)(),m=[...n,...u.map((e=>{const n=`${`pathname://${f.createUrl({locale:e,fullyQualified:!1})}`}${p}${g}`;return{label:d[e].label,lang:d[e].htmlLang,to:n,target:"_self",autoAddBaseUrl:!1,className:e===i?t?"menu__link--active":"dropdown__link--active":""}})),...r],h=t?(0,c.I)({message:"Languages",id:"theme.navbar.mobileLanguageDropdown.label",description:"The label for the mobile language switcher dropdown"}):d[i].label;return a.createElement(ge,(0,s.Z)({},o,{mobile:t,label:a.createElement(a.Fragment,null,a.createElement(he,{className:be}),h),items:m}))},search:function(e){let{mobile:t,className:n}=e;return t?null:a.createElement(Fe,{className:n},a.createElement(De,null))},dropdown:ge,html:function(e){let{value:t,className:n,mobile:o=!1,isDropdownItem:i=!1}=e;const s=i?"li":"div";return a.createElement(s,{className:(0,r.Z)({navbar__item:!o&&!i,"menu__list-item":o},n),dangerouslySetInnerHTML:{__html:t}})},doc:function(e){let{docId:t,label:n,docsPluginId:r,...o}=e;const{activeDoc:i}=(0,$e.Iw)(r),l=(0,Ue.vY)(t,r);return null===l?null:a.createElement(se,(0,s.Z)({exact:!0},o,{isActive:()=>i?.path===l.path||!!i?.sidebar&&i.sidebar===l.sidebar,label:n??l.id,to:l.path}))},docSidebar:function(e){let{sidebarId:t,label:n,docsPluginId:r,...o}=e;const{activeDoc:i}=(0,$e.Iw)(r),l=(0,Ue.oz)(t,r).link;if(!l)throw new Error(`DocSidebarNavbarItem: Sidebar with ID "${t}" doesn't have anything to be linked to.`);return a.createElement(se,(0,s.Z)({exact:!0},o,{isActive:()=>i?.sidebar===t,label:n??l.label,to:l.path}))},docsVersion:function(e){let{label:t,to:n,docsPluginId:r,...o}=e;const i=(0,Ue.lO)(r)[0],l=t??i.label,c=n??(e=>e.docs.find((t=>t.id===e.mainDocId)))(i).path;return a.createElement(se,(0,s.Z)({},o,{label:l,to:c}))},docsVersionDropdown:function(e){let{mobile:t,docsPluginId:n,dropdownActiveClassDisabled:r,dropdownItemsBefore:o,dropdownItemsAfter:i,...u}=e;const{search:d,hash:f}=(0,l.TH)(),p=(0,$e.Iw)(n),g=(0,$e.gB)(n),{savePreferredVersionName:m}=(0,ze.J)(n),h=[...o,...g.map((e=>{const t=p.alternateDocVersions[e.name]??Ze(e);return{label:e.label,to:`${t.path}${d}${f}`,isActive:()=>e===p.activeVersion,onClick:()=>m(e.name)}})),...i],b=(0,Ue.lO)(n)[0],v=t&&h.length>1?(0,c.I)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):b.label,y=t&&h.length>1?void 0:Ze(b).path;return h.length<=1?a.createElement(se,(0,s.Z)({},u,{mobile:t,label:v,to:y,isActive:r?()=>!1:void 0})):a.createElement(ge,(0,s.Z)({},u,{mobile:t,label:v,to:y,items:h,isActive:r?()=>!1:void 0}))}};function Ve(e){let{type:t,...n}=e;const r=function(e,t){return e&&"default"!==e?e:"items"in t?"dropdown":"default"}(t,n),o=He[r];if(!o)throw new Error(`No NavbarItem component found for type "${t}".`);return a.createElement(o,n)}function We(){const e=(0,O.e)(),t=(0,w.L)().navbar.items;return a.createElement("ul",{className:"menu__list"},t.map(((t,n)=>a.createElement(Ve,(0,s.Z)({mobile:!0},t,{onClick:()=>e.toggle(),key:n})))))}function Ge(e){return a.createElement("button",(0,s.Z)({},e,{type:"button",className:"clean-btn navbar-sidebar__back"}),a.createElement(c.Z,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"},"\u2190 Back to main menu"))}function qe(){const e=0===(0,w.L)().navbar.items.length,t=F();return a.createElement(a.Fragment,null,!e&&a.createElement(Ge,{onClick:()=>t.hide()}),t.content)}function Ke(){const e=(0,O.e)();var t;return void 0===(t=e.shown)&&(t=!0),(0,a.useEffect)((()=>(document.body.style.overflow=t?"hidden":"visible",()=>{document.body.style.overflow="visible"})),[t]),e.shouldRender?a.createElement($,{header:a.createElement(X,null),primaryMenu:a.createElement(We,null),secondaryMenu:a.createElement(qe,null)}):null}const Ye="navbarHideable_m1mJ",Qe="navbarHidden_jGov";function Xe(e){return a.createElement("div",(0,s.Z)({role:"presentation"},e,{className:(0,r.Z)("navbar-sidebar__backdrop",e.className)}))}function Je(e){let{children:t}=e;const{navbar:{hideOnScroll:n,style:o}}=(0,w.L)(),i=(0,O.e)(),{navbarRef:s,isNavbarVisible:l}=function(e){const[t,n]=(0,a.useState)(e),r=(0,a.useRef)(!1),o=(0,a.useRef)(0),i=(0,a.useCallback)((e=>{null!==e&&(o.current=e.getBoundingClientRect().height)}),[]);return(0,R.RF)(((t,a)=>{let{scrollY:i}=t;if(!e)return;if(i=s?n(!1):i+c{if(!e)return;const a=t.location.hash;if(a?document.getElementById(a.substring(1)):void 0)return r.current=!0,void n(!1);n(!0)})),{navbarRef:i,isNavbarVisible:t}}(n);return a.createElement("nav",{ref:s,"aria-label":(0,c.I)({id:"theme.NavBar.navAriaLabel",message:"Main",description:"The ARIA label for the main navigation"}),className:(0,r.Z)("navbar","navbar--fixed-top",n&&[Ye,!l&&Qe],{"navbar--dark":"dark"===o,"navbar--primary":"primary"===o,"navbar-sidebar--show":i.shown})},t,a.createElement(Xe,{onClick:i.toggle}),a.createElement(Ke,null))}function et(e){let{width:t=30,height:n=30,className:r,...o}=e;return a.createElement("svg",(0,s.Z)({className:r,width:t,height:n,viewBox:"0 0 30 30","aria-hidden":"true"},o),a.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeMiterlimit:"10",strokeWidth:"2",d:"M4 7h22M4 15h22M4 23h22"}))}function tt(){const{toggle:e,shown:t}=(0,O.e)();return a.createElement("button",{onClick:e,"aria-label":(0,c.I)({id:"theme.docs.sidebar.toggleSidebarButtonAriaLabel",message:"Toggle navigation bar",description:"The ARIA label for hamburger menu button of mobile navigation"}),"aria-expanded":t,className:"navbar__toggle clean-btn",type:"button"},a.createElement(et,null))}const nt="colorModeToggle_DEke";function at(e){let{items:t}=e;return a.createElement(a.Fragment,null,t.map(((e,t)=>a.createElement(Ve,(0,s.Z)({},e,{key:t})))))}function rt(e){let{left:t,right:n}=e;return a.createElement("div",{className:"navbar__inner"},a.createElement("div",{className:"navbar__items"},t),a.createElement("div",{className:"navbar__items navbar__items--right"},n))}function ot(){const e=(0,O.e)(),t=(0,w.L)().navbar.items,[n,r]=function(e){function t(e){return"left"===(e.position??"right")}return[e.filter(t),e.filter((e=>!t(e)))]}(t),o=t.find((e=>"search"===e.type));return a.createElement(rt,{left:a.createElement(a.Fragment,null,!e.disabled&&a.createElement(tt,null),a.createElement(Y,null),a.createElement(at,{items:n})),right:a.createElement(a.Fragment,null,a.createElement(at,{items:r}),a.createElement(q,{className:nt}),!o&&a.createElement(Fe,null,a.createElement(De,null)))})}function it(){return a.createElement(Je,null,a.createElement(ot,null))}function st(e){let{item:t}=e;const{to:n,href:r,label:o,prependBaseUrlToHref:i,...l}=t,c=(0,ee.Z)(n),u=(0,ee.Z)(r,{forcePrependBaseUrl:!0});return a.createElement(J.Z,(0,s.Z)({className:"footer__link-item"},r?{href:i?u:r}:{to:c},l),o,r&&!(0,te.Z)(r)&&a.createElement(ae.Z,null))}function lt(e){let{item:t}=e;return t.html?a.createElement("li",{className:"footer__item",dangerouslySetInnerHTML:{__html:t.html}}):a.createElement("li",{key:t.href??t.to,className:"footer__item"},a.createElement(st,{item:t}))}function ct(e){let{column:t}=e;return a.createElement("div",{className:"col footer__col"},a.createElement("div",{className:"footer__title"},t.title),a.createElement("ul",{className:"footer__items clean-list"},t.items.map(((e,t)=>a.createElement(lt,{key:t,item:e})))))}function ut(e){let{columns:t}=e;return a.createElement("div",{className:"row footer__links"},t.map(((e,t)=>a.createElement(ct,{key:t,column:e}))))}function dt(){return a.createElement("span",{className:"footer__link-separator"},"\xb7")}function ft(e){let{item:t}=e;return t.html?a.createElement("span",{className:"footer__link-item",dangerouslySetInnerHTML:{__html:t.html}}):a.createElement(st,{item:t})}function pt(e){let{links:t}=e;return a.createElement("div",{className:"footer__links text--center"},a.createElement("div",{className:"footer__links"},t.map(((e,n)=>a.createElement(a.Fragment,{key:n},a.createElement(ft,{item:e}),t.length!==n+1&&a.createElement(dt,null))))))}function gt(e){let{links:t}=e;return function(e){return"title"in e[0]}(t)?a.createElement(ut,{columns:t}):a.createElement(pt,{links:t})}var mt=n(50941);const ht="footerLogoLink_BH7S";function bt(e){let{logo:t}=e;const{withBaseUrl:n}=(0,ee.C)(),o={light:n(t.src),dark:n(t.srcDark??t.src)};return a.createElement(mt.Z,{className:(0,r.Z)("footer__logo",t.className),alt:t.alt,sources:o,width:t.width,height:t.height,style:t.style})}function vt(e){let{logo:t}=e;return t.href?a.createElement(J.Z,{href:t.href,className:ht,target:t.target},a.createElement(bt,{logo:t})):a.createElement(bt,{logo:t})}function yt(e){let{copyright:t}=e;return a.createElement("div",{className:"footer__copyright",dangerouslySetInnerHTML:{__html:t}})}function wt(e){let{style:t,links:n,logo:o,copyright:i}=e;return a.createElement("footer",{className:(0,r.Z)("footer",{"footer--dark":"dark"===t})},a.createElement("div",{className:"container container-fluid"},n,(o||i)&&a.createElement("div",{className:"footer__bottom text--center"},o&&a.createElement("div",{className:"margin-bottom--sm"},o),i)))}function _t(){const{footer:e}=(0,w.L)();if(!e)return null;const{copyright:t,links:n,logo:r,style:o}=e;return a.createElement(wt,{style:o,links:n&&n.length>0&&a.createElement(gt,{links:n}),logo:r&&a.createElement(vt,{logo:r}),copyright:t&&a.createElement(yt,{copyright:t})})}const kt=a.memo(_t),xt=(0,M.Qc)([U.S,_.pl,R.OC,ze.L5,i.VC,function(e){let{children:t}=e;return a.createElement(I.n2,null,a.createElement(O.M,null,a.createElement(D,null,t)))}]);function Et(e){let{children:t}=e;return a.createElement(xt,null,t)}function St(e){let{error:t,tryAgain:n}=e;return a.createElement("main",{className:"container margin-vert--xl"},a.createElement("div",{className:"row"},a.createElement("div",{className:"col col--6 col--offset-3"},a.createElement("h1",{className:"hero__title"},a.createElement(c.Z,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed"},"This page crashed.")),a.createElement("p",null,t.message),a.createElement("div",null,a.createElement("button",{type:"button",onClick:n},a.createElement(c.Z,{id:"theme.ErrorPageContent.tryAgain",description:"The label of the button to try again when the page crashed"},"Try again"))))))}const Tt="mainWrapper_z2l0";function Ct(e){const{children:t,noFooter:n,wrapperClassName:s,title:l,description:c}=e;return(0,b.t)(),a.createElement(Et,null,a.createElement(i.d,{title:l,description:c}),a.createElement(y,null),a.createElement(N,null),a.createElement(it,null),a.createElement("div",{id:d,className:(0,r.Z)(h.k.wrapper.main,Tt,s)},a.createElement(o.Z,{fallback:e=>a.createElement(St,e)},t)),!n&&a.createElement(kt,null))}},21327:(e,t,n)=>{"use strict";n.d(t,{Z:()=>d});var a=n(87462),r=n(67294),o=n(39960),i=n(44996),s=n(52263),l=n(86668),c=n(50941);function u(e){let{logo:t,alt:n,imageClassName:a}=e;const o={light:(0,i.Z)(t.src),dark:(0,i.Z)(t.srcDark||t.src)},s=r.createElement(c.Z,{className:t.className,sources:o,height:t.height,width:t.width,alt:n,style:t.style});return a?r.createElement("div",{className:a},s):s}function d(e){const{siteConfig:{title:t}}=(0,s.Z)(),{navbar:{title:n,logo:c}}=(0,l.L)(),{imageClassName:d,titleClassName:f,...p}=e,g=(0,i.Z)(c?.href||"/"),m=n?"":t,h=c?.alt??m;return r.createElement(o.Z,(0,a.Z)({to:g},p,c?.target&&{target:c.target}),c&&r.createElement(u,{logo:c,alt:h,imageClassName:d}),null!=n&&r.createElement("b",{className:f},n))}},90197:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var a=n(67294),r=n(35742);function o(e){let{locale:t,version:n,tag:o}=e;const i=t;return a.createElement(r.Z,null,t&&a.createElement("meta",{name:"docusaurus_locale",content:t}),n&&a.createElement("meta",{name:"docusaurus_version",content:n}),o&&a.createElement("meta",{name:"docusaurus_tag",content:o}),i&&a.createElement("meta",{name:"docsearch:language",content:i}),n&&a.createElement("meta",{name:"docsearch:version",content:n}),o&&a.createElement("meta",{name:"docsearch:docusaurus_tag",content:o}))}},50941:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});var a=n(87462),r=n(67294),o=n(86010),i=n(72389),s=n(92949);const l={themedImage:"themedImage_ToTc","themedImage--light":"themedImage--light_HNdA","themedImage--dark":"themedImage--dark_i4oU"};function c(e){const t=(0,i.Z)(),{colorMode:n}=(0,s.I)(),{sources:c,className:u,alt:d,...f}=e,p=t?"dark"===n?["dark"]:["light"]:["light","dark"];return r.createElement(r.Fragment,null,p.map((e=>r.createElement("img",(0,a.Z)({key:e,src:c[e],alt:d,className:(0,o.Z)(l.themedImage,l[`themedImage--${e}`],u)},f)))))}},86043:(e,t,n)=>{"use strict";n.d(t,{u:()=>i,z:()=>g});var a=n(87462),r=n(67294),o=n(10412);function i(e){let{initialState:t}=e;const[n,a]=(0,r.useState)(t??!1),o=(0,r.useCallback)((()=>{a((e=>!e))}),[]);return{collapsed:n,setCollapsed:a,toggleCollapsed:o}}const s={display:"none",overflow:"hidden",height:"0px"},l={display:"block",overflow:"visible",height:"auto"};function c(e,t){const n=t?s:l;e.style.display=n.display,e.style.overflow=n.overflow,e.style.height=n.height}function u(e){let{collapsibleRef:t,collapsed:n,animation:a}=e;const o=(0,r.useRef)(!1);(0,r.useEffect)((()=>{const e=t.current;function r(){const t=e.scrollHeight,n=a?.duration??function(e){const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}(t);return{transition:`height ${n}ms ${a?.easing??"ease-in-out"}`,height:`${t}px`}}function i(){const t=r();e.style.transition=t.transition,e.style.height=t.height}if(!o.current)return c(e,n),void(o.current=!0);return e.style.willChange="height",function(){const t=requestAnimationFrame((()=>{n?(i(),requestAnimationFrame((()=>{e.style.height=s.height,e.style.overflow=s.overflow}))):(e.style.display="block",requestAnimationFrame((()=>{i()})))}));return()=>cancelAnimationFrame(t)}()}),[t,n,a])}function d(e){if(!o.Z.canUseDOM)return e?s:l}function f(e){let{as:t="div",collapsed:n,children:a,animation:o,onCollapseTransitionEnd:i,className:s,disableSSRStyle:l}=e;const f=(0,r.useRef)(null);return u({collapsibleRef:f,collapsed:n,animation:o}),r.createElement(t,{ref:f,style:l?void 0:d(n),onTransitionEnd:e=>{"height"===e.propertyName&&(c(f.current,n),i?.(n))},className:s},a)}function p(e){let{collapsed:t,...n}=e;const[o,i]=(0,r.useState)(!t),[s,l]=(0,r.useState)(t);return(0,r.useLayoutEffect)((()=>{t||i(!0)}),[t]),(0,r.useLayoutEffect)((()=>{o&&l(t)}),[o,t]),o?r.createElement(f,(0,a.Z)({},n,{collapsed:s})):null}function g(e){let{lazy:t,...n}=e;const a=t?p:f;return r.createElement(a,n)}},59689:(e,t,n)=>{"use strict";n.d(t,{nT:()=>g,pl:()=>p});var a=n(67294),r=n(72389),o=n(50012),i=n(902),s=n(86668);const l=(0,o.WA)("docusaurus.announcement.dismiss"),c=(0,o.WA)("docusaurus.announcement.id"),u=()=>"true"===l.get(),d=e=>l.set(String(e)),f=a.createContext(null);function p(e){let{children:t}=e;const n=function(){const{announcementBar:e}=(0,s.L)(),t=(0,r.Z)(),[n,o]=(0,a.useState)((()=>!!t&&u()));(0,a.useEffect)((()=>{o(u())}),[]);const i=(0,a.useCallback)((()=>{d(!0),o(!0)}),[]);return(0,a.useEffect)((()=>{if(!e)return;const{id:t}=e;let n=c.get();"annoucement-bar"===n&&(n="announcement-bar");const a=t!==n;c.set(t),a&&d(!1),!a&&u()||o(!1)}),[e]),(0,a.useMemo)((()=>({isActive:!!e&&!n,close:i})),[e,n,i])}();return a.createElement(f.Provider,{value:n},t)}function g(){const e=(0,a.useContext)(f);if(!e)throw new i.i6("AnnouncementBarProvider");return e}},92949:(e,t,n)=>{"use strict";n.d(t,{I:()=>h,S:()=>m});var a=n(67294),r=n(10412),o=n(902),i=n(50012),s=n(86668);const l=a.createContext(void 0),c="theme",u=(0,i.WA)(c),d="light",f="dark",p=e=>e===f?f:d;function g(){const{colorMode:{defaultMode:e,disableSwitch:t,respectPrefersColorScheme:n}}=(0,s.L)(),[o,i]=(0,a.useState)((e=>r.Z.canUseDOM?p(document.documentElement.getAttribute("data-theme")):p(e))(e));(0,a.useEffect)((()=>{t&&u.del()}),[t]);const l=(0,a.useCallback)((function(t,a){void 0===a&&(a={});const{persist:r=!0}=a;t?(i(t),r&&(e=>{u.set(p(e))})(t)):(i(n?window.matchMedia("(prefers-color-scheme: dark)").matches?f:d:e),u.del())}),[n,e]);(0,a.useEffect)((()=>{document.documentElement.setAttribute("data-theme",p(o))}),[o]),(0,a.useEffect)((()=>{if(t)return;const e=e=>{if(e.key!==c)return;const t=u.get();null!==t&&l(p(t))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)}),[t,l]);const g=(0,a.useRef)(!1);return(0,a.useEffect)((()=>{if(t&&!n)return;const e=window.matchMedia("(prefers-color-scheme: dark)"),a=()=>{window.matchMedia("print").matches||g.current?g.current=window.matchMedia("print").matches:l(null)};return e.addListener(a),()=>e.removeListener(a)}),[l,t,n]),(0,a.useMemo)((()=>({colorMode:o,setColorMode:l,get isDarkTheme(){return o===f},setLightTheme(){l(d)},setDarkTheme(){l(f)}})),[o,l])}function m(e){let{children:t}=e;const n=g();return a.createElement(l.Provider,{value:n},t)}function h(){const e=(0,a.useContext)(l);if(null==e)throw new o.i6("ColorModeProvider","Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.");return e}},60373:(e,t,n)=>{"use strict";n.d(t,{J:()=>y,L5:()=>b,Oh:()=>w});var a=n(67294),r=n(80143),o=n(29935),i=n(86668),s=n(53438),l=n(902),c=n(50012);const u=e=>`docs-preferred-version-${e}`,d=(e,t,n)=>{(0,c.WA)(u(e),{persistence:t}).set(n)},f=(e,t)=>(0,c.WA)(u(e),{persistence:t}).get(),p=(e,t)=>{(0,c.WA)(u(e),{persistence:t}).del()};const g=a.createContext(null);function m(){const e=(0,r._r)(),t=(0,i.L)().docs.versionPersistence,n=(0,a.useMemo)((()=>Object.keys(e)),[e]),[o,s]=(0,a.useState)((()=>(e=>Object.fromEntries(e.map((e=>[e,{preferredVersionName:null}]))))(n)));(0,a.useEffect)((()=>{s(function(e){let{pluginIds:t,versionPersistence:n,allDocsData:a}=e;function r(e){const t=f(e,n);return a[e].versions.some((e=>e.name===t))?{preferredVersionName:t}:(p(e,n),{preferredVersionName:null})}return Object.fromEntries(t.map((e=>[e,r(e)])))}({allDocsData:e,versionPersistence:t,pluginIds:n}))}),[e,t,n]);return[o,(0,a.useMemo)((()=>({savePreferredVersion:function(e,n){d(e,t,n),s((t=>({...t,[e]:{preferredVersionName:n}})))}})),[t])]}function h(e){let{children:t}=e;const n=m();return a.createElement(g.Provider,{value:n},t)}function b(e){let{children:t}=e;return s.cE?a.createElement(h,null,t):a.createElement(a.Fragment,null,t)}function v(){const e=(0,a.useContext)(g);if(!e)throw new l.i6("DocsPreferredVersionContextProvider");return e}function y(e){void 0===e&&(e=o.m);const t=(0,r.zh)(e),[n,i]=v(),{preferredVersionName:s}=n[e];return{preferredVersion:t.versions.find((e=>e.name===s))??null,savePreferredVersionName:(0,a.useCallback)((t=>{i.savePreferredVersion(e,t)}),[i,e])}}function w(){const e=(0,r._r)(),[t]=v();function n(n){const a=e[n],{preferredVersionName:r}=t[n];return a.versions.find((e=>e.name===r))??null}const a=Object.keys(e);return Object.fromEntries(a.map((e=>[e,n(e)])))}},1116:(e,t,n)=>{"use strict";n.d(t,{V:()=>l,b:()=>s});var a=n(67294),r=n(902);const o=Symbol("EmptyContext"),i=a.createContext(o);function s(e){let{children:t,name:n,items:r}=e;const o=(0,a.useMemo)((()=>n&&r?{name:n,items:r}:null),[n,r]);return a.createElement(i.Provider,{value:o},t)}function l(){const e=(0,a.useContext)(i);if(e===o)throw new r.i6("DocsSidebarProvider");return e}},93163:(e,t,n)=>{"use strict";n.d(t,{M:()=>d,e:()=>f});var a=n(67294),r=n(13102),o=n(87524),i=n(91980),s=n(86668),l=n(902);const c=a.createContext(void 0);function u(){const e=function(){const e=(0,r.HY)(),{items:t}=(0,s.L)().navbar;return 0===t.length&&!e.component}(),t=(0,o.i)(),n=!e&&"mobile"===t,[l,c]=(0,a.useState)(!1);(0,i.Rb)((()=>{if(l)return c(!1),!1}));const u=(0,a.useCallback)((()=>{c((e=>!e))}),[]);return(0,a.useEffect)((()=>{"desktop"===t&&c(!1)}),[t]),(0,a.useMemo)((()=>({disabled:e,shouldRender:n,toggle:u,shown:l})),[e,n,u,l])}function d(e){let{children:t}=e;const n=u();return a.createElement(c.Provider,{value:n},t)}function f(){const e=a.useContext(c);if(void 0===e)throw new l.i6("NavbarMobileSidebarProvider");return e}},13102:(e,t,n)=>{"use strict";n.d(t,{HY:()=>s,Zo:()=>l,n2:()=>i});var a=n(67294),r=n(902);const o=a.createContext(null);function i(e){let{children:t}=e;const n=(0,a.useState)({component:null,props:null});return a.createElement(o.Provider,{value:n},t)}function s(){const e=(0,a.useContext)(o);if(!e)throw new r.i6("NavbarSecondaryMenuContentProvider");return e[0]}function l(e){let{component:t,props:n}=e;const i=(0,a.useContext)(o);if(!i)throw new r.i6("NavbarSecondaryMenuContentProvider");const[,s]=i,l=(0,r.Ql)(n);return(0,a.useEffect)((()=>{s({component:t,props:l})}),[s,t,l]),(0,a.useEffect)((()=>()=>s({component:null,props:null})),[s]),null}},19727:(e,t,n)=>{"use strict";n.d(t,{h:()=>r,t:()=>o});var a=n(67294);const r="navigation-with-keyboard";function o(){(0,a.useEffect)((()=>{function e(e){"keydown"===e.type&&"Tab"===e.key&&document.body.classList.add(r),"mousedown"===e.type&&document.body.classList.remove(r)}return document.addEventListener("keydown",e),document.addEventListener("mousedown",e),()=>{document.body.classList.remove(r),document.removeEventListener("keydown",e),document.removeEventListener("mousedown",e)}}),[])}},66177:(e,t,n)=>{"use strict";n.d(t,{O:()=>s});var a=n(67294),r=n(16550),o=n(52263);const i="q";function s(){const e=(0,r.k6)(),{siteConfig:{baseUrl:t,themeConfig:n}}=(0,o.Z)(),{algolia:{searchPagePath:s}}=n,[l,c]=(0,a.useState)("");(0,a.useEffect)((()=>{const e=new URLSearchParams(window.location.search).get(i)??"";c(e)}),[]);return{searchQuery:l,setSearchQuery:(0,a.useCallback)((t=>{const n=new URLSearchParams(window.location.search);t?n.set(i,t):n.delete(i),e.replace({search:n.toString()}),c(t)}),[e]),generateSearchPageLink:(0,a.useCallback)((e=>`${t}${s}?q=${encodeURIComponent(e)}`),[t,s])}}},87524:(e,t,n)=>{"use strict";n.d(t,{i:()=>c});var a=n(67294),r=n(10412);const o="desktop",i="mobile",s="ssr";function l(){return r.Z.canUseDOM?window.innerWidth>996?o:i:s}function c(){const[e,t]=(0,a.useState)((()=>l()));return(0,a.useEffect)((()=>{function e(){t(l())}return window.addEventListener("resize",e),()=>{window.removeEventListener("resize",e),clearTimeout(undefined)}}),[]),e}},35281:(e,t,n)=>{"use strict";n.d(t,{k:()=>a});const a={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",admonitionType:e=>`theme-admonition-${e}`},layout:{},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:e=>`theme-doc-sidebar-item-category-level-${e}`,docSidebarItemLinkLevel:e=>`theme-doc-sidebar-item-link-level-${e}`},blog:{}}},53438:(e,t,n)=>{"use strict";n.d(t,{Wl:()=>f,_F:()=>g,cE:()=>d,hI:()=>w,lO:()=>b,oz:()=>v,s1:()=>h,vY:()=>y});var a=n(67294),r=n(16550),o=n(18790),i=n(80143),s=n(60373),l=n(1116),c=n(67392),u=n(48596);const d=!!i._r;function f(e){if(e.href)return e.href;for(const t of e.items){if("link"===t.type)return t.href;if("category"===t.type){const e=f(t);if(e)return e}}}const p=(e,t)=>void 0!==e&&(0,u.Mg)(e,t);function g(e,t){return"link"===e.type?p(e.href,t):"category"===e.type&&(p(e.href,t)||((e,t)=>e.some((e=>g(e,t))))(e.items,t))}function m(e){let{sidebarItems:t,pathname:n,onlyCategories:a=!1}=e;const r=[];return function e(t){for(const o of t)if("category"===o.type&&((0,u.Mg)(o.href,n)||e(o.items))||"link"===o.type&&(0,u.Mg)(o.href,n)){return a&&"category"!==o.type||r.unshift(o),!0}return!1}(t),r}function h(){const e=(0,l.V)(),{pathname:t}=(0,r.TH)(),n=(0,i.gA)()?.pluginData.breadcrumbs;return!1!==n&&e?m({sidebarItems:e.items,pathname:t}):null}function b(e){const{activeVersion:t}=(0,i.Iw)(e),{preferredVersion:n}=(0,s.J)(e),r=(0,i.yW)(e);return(0,a.useMemo)((()=>(0,c.j)([t,n,r].filter(Boolean))),[t,n,r])}function v(e,t){const n=b(t);return(0,a.useMemo)((()=>{const t=n.flatMap((e=>e.sidebars?Object.entries(e.sidebars):[])),a=t.find((t=>t[0]===e));if(!a)throw new Error(`Can't find any sidebar with id "${e}" in version${n.length>1?"s":""} ${n.map((e=>e.name)).join(", ")}".\n Available sidebar ids are:\n - ${Object.keys(t).join("\n- ")}`);return a[1]}),[e,n])}function y(e,t){const n=b(t);return(0,a.useMemo)((()=>{const t=n.flatMap((e=>e.docs)),a=t.find((t=>t.id===e));if(!a){if(n.flatMap((e=>e.draftIds)).includes(e))return null;throw new Error(`DocNavbarItem: couldn't find any doc with id "${e}" in version${n.length>1?"s":""} ${n.map((e=>e.name)).join(", ")}".\nAvailable doc ids are:\n- ${(0,c.j)(t.map((e=>e.id))).join("\n- ")}`)}return a}),[e,n])}function w(e){let{route:t,versionMetadata:n}=e;const a=(0,r.TH)(),i=t.routes,s=i.find((e=>(0,r.LX)(a.pathname,e)));if(!s)return null;const l=s.sidebar,c=l?n.docsSidebars[l]:void 0;return{docElement:(0,o.H)(i),sidebarName:l,sidebarItems:c}}},82128:(e,t,n)=>{"use strict";n.d(t,{p:()=>r});var a=n(52263);function r(e){const{siteConfig:t}=(0,a.Z)(),{title:n,titleDelimiter:r}=t;return e?.trim().length?`${e.trim()} ${r} ${n}`:n}},91980:(e,t,n)=>{"use strict";n.d(t,{Rb:()=>s,_X:()=>l});var a=n(67294),r=n(16550),o=n(61688),i=n(902);function s(e){!function(e){const t=(0,r.k6)(),n=(0,i.zX)(e);(0,a.useEffect)((()=>t.block(((e,t)=>n(e,t)))),[t,n])}(((t,n)=>{if("POP"===n)return e(t,n)}))}function l(e){return function(e){const t=(0,r.k6)();return(0,o.useSyncExternalStore)(t.listen,(()=>e(t)))}((t=>null===e?null:new URLSearchParams(t.location.search).get(e)))}},67392:(e,t,n)=>{"use strict";function a(e,t){return void 0===t&&(t=(e,t)=>e===t),e.filter(((n,a)=>e.findIndex((e=>t(e,n)))!==a))}function r(e){return Array.from(new Set(e))}n.d(t,{j:()=>r,l:()=>a})},10833:(e,t,n)=>{"use strict";n.d(t,{FG:()=>f,d:()=>u,VC:()=>p});var a=n(67294),r=n(86010),o=n(35742),i=n(30226);function s(){const e=a.useContext(i._);if(!e)throw new Error("Unexpected: no Docusaurus route context found");return e}var l=n(44996),c=n(82128);function u(e){let{title:t,description:n,keywords:r,image:i,children:s}=e;const u=(0,c.p)(t),{withBaseUrl:d}=(0,l.C)(),f=i?d(i,{absolute:!0}):void 0;return a.createElement(o.Z,null,t&&a.createElement("title",null,u),t&&a.createElement("meta",{property:"og:title",content:u}),n&&a.createElement("meta",{name:"description",content:n}),n&&a.createElement("meta",{property:"og:description",content:n}),r&&a.createElement("meta",{name:"keywords",content:Array.isArray(r)?r.join(","):r}),f&&a.createElement("meta",{property:"og:image",content:f}),f&&a.createElement("meta",{name:"twitter:image",content:f}),s)}const d=a.createContext(void 0);function f(e){let{className:t,children:n}=e;const i=a.useContext(d),s=(0,r.Z)(i,t);return a.createElement(d.Provider,{value:s},a.createElement(o.Z,null,a.createElement("html",{className:s})),n)}function p(e){let{children:t}=e;const n=s(),o=`plugin-${n.plugin.name.replace(/docusaurus-(?:plugin|theme)-(?:content-)?/gi,"")}`;const i=`plugin-id-${n.plugin.id}`;return a.createElement(f,{className:(0,r.Z)(o,i)},t)}},902:(e,t,n)=>{"use strict";n.d(t,{D9:()=>i,Qc:()=>c,Ql:()=>l,i6:()=>s,zX:()=>o});var a=n(67294);const r=n(10412).Z.canUseDOM?a.useLayoutEffect:a.useEffect;function o(e){const t=(0,a.useRef)(e);return r((()=>{t.current=e}),[e]),(0,a.useCallback)((function(){return t.current(...arguments)}),[])}function i(e){const t=(0,a.useRef)();return r((()=>{t.current=e})),t.current}class s extends Error{constructor(e,t){super(),this.name="ReactContextError",this.message=`Hook ${this.stack?.split("\n")[1]?.match(/at (?:\w+\.)?(?\w+)/)?.groups.name??""} is called outside the <${e}>. ${t??""}`}}function l(e){const t=Object.entries(e);return t.sort(((e,t)=>e[0].localeCompare(t[0]))),(0,a.useMemo)((()=>e),t.flat())}function c(e){return t=>{let{children:n}=t;return a.createElement(a.Fragment,null,e.reduceRight(((e,t)=>a.createElement(t,null,e)),n))}}},98022:(e,t,n)=>{"use strict";function a(e,t){return void 0!==e&&void 0!==t&&new RegExp(e,"gi").test(t)}n.d(t,{F:()=>a})},48596:(e,t,n)=>{"use strict";n.d(t,{Mg:()=>i,Ns:()=>s});var a=n(67294),r=n(723),o=n(52263);function i(e,t){const n=e=>(!e||e.endsWith("/")?e:`${e}/`)?.toLowerCase();return n(e)===n(t)}function s(){const{baseUrl:e}=(0,o.Z)().siteConfig;return(0,a.useMemo)((()=>function(e){let{baseUrl:t,routes:n}=e;function a(e){return e.path===t&&!0===e.exact}function r(e){return e.path===t&&!e.exact}return function e(t){if(0===t.length)return;return t.find(a)||e(t.filter(r).flatMap((e=>e.routes??[])))}(n)}({routes:r.Z,baseUrl:e})),[e])}},12466:(e,t,n)=>{"use strict";n.d(t,{Ct:()=>p,OC:()=>l,RF:()=>d,o5:()=>f});var a=n(67294),r=n(10412),o=n(72389),i=n(902);const s=a.createContext(void 0);function l(e){let{children:t}=e;const n=function(){const e=(0,a.useRef)(!0);return(0,a.useMemo)((()=>({scrollEventsEnabledRef:e,enableScrollEvents:()=>{e.current=!0},disableScrollEvents:()=>{e.current=!1}})),[])}();return a.createElement(s.Provider,{value:n},t)}function c(){const e=(0,a.useContext)(s);if(null==e)throw new i.i6("ScrollControllerProvider");return e}const u=()=>r.Z.canUseDOM?{scrollX:window.pageXOffset,scrollY:window.pageYOffset}:null;function d(e,t){void 0===t&&(t=[]);const{scrollEventsEnabledRef:n}=c(),r=(0,a.useRef)(u()),o=(0,i.zX)(e);(0,a.useEffect)((()=>{const e=()=>{if(!n.current)return;const e=u();o(e,r.current),r.current=e},t={passive:!0};return e(),window.addEventListener("scroll",e,t),()=>window.removeEventListener("scroll",e,t)}),[o,n,...t])}function f(){const e=c(),t=function(){const e=(0,a.useRef)({elem:null,top:0}),t=(0,a.useCallback)((t=>{e.current={elem:t,top:t.getBoundingClientRect().top}}),[]),n=(0,a.useCallback)((()=>{const{current:{elem:t,top:n}}=e;if(!t)return{restored:!1};const a=t.getBoundingClientRect().top-n;return a&&window.scrollBy({left:0,top:a}),e.current={elem:null,top:0},{restored:0!==a}}),[]);return(0,a.useMemo)((()=>({save:t,restore:n})),[n,t])}(),n=(0,a.useRef)(void 0),r=(0,a.useCallback)((a=>{t.save(a),e.disableScrollEvents(),n.current=()=>{const{restored:a}=t.restore();if(n.current=void 0,a){const t=()=>{e.enableScrollEvents(),window.removeEventListener("scroll",t)};window.addEventListener("scroll",t)}else e.enableScrollEvents()}}),[e,t]);return(0,a.useLayoutEffect)((()=>{n.current?.()})),{blockElementScrollPositionUntilNextRender:r}}function p(){const e=(0,a.useRef)(null),t=(0,o.Z)()&&"smooth"===getComputedStyle(document.documentElement).scrollBehavior;return{startScroll:n=>{e.current=t?function(e){return window.scrollTo({top:e,behavior:"smooth"}),()=>{}}(n):function(e){let t=null;const n=document.documentElement.scrollTop>e;return function a(){const r=document.documentElement.scrollTop;(n&&r>e||!n&&rt&&cancelAnimationFrame(t)}(n)},cancelScroll:()=>e.current?.()}}},43320:(e,t,n)=>{"use strict";n.d(t,{HX:()=>i,_q:()=>l,os:()=>s});var a=n(80143),r=n(52263),o=n(60373);const i="default";function s(e,t){return`docs-${e}-${t}`}function l(){const{i18n:e}=(0,r.Z)(),t=(0,a._r)(),n=(0,a.WS)(),l=(0,o.Oh)();const c=[i,...Object.keys(t).map((function(e){const a=n?.activePlugin.pluginId===e?n.activeVersion:void 0,r=l[e],o=t[e].versions.find((e=>e.isLast));return s(e,(a??r??o).name)}))];return{locale:e.currentLocale,tags:c}}},50012:(e,t,n)=>{"use strict";n.d(t,{Nk:()=>d,WA:()=>u});var a=n(67294),r=n(61688);const o="localStorage";function i(e){let{key:t,oldValue:n,newValue:a,storage:r}=e;const o=document.createEvent("StorageEvent");o.initStorageEvent("storage",!1,!1,t,n,a,window.location.href,r),window.dispatchEvent(o)}function s(e){if(void 0===e&&(e=o),"undefined"==typeof window)throw new Error("Browser storage is not available on Node.js/Docusaurus SSR process.");if("none"===e)return null;try{return window[e]}catch(n){return t=n,l||(console.warn("Docusaurus browser storage is not available.\nPossible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.",t),l=!0),null}var t}let l=!1;const c={get:()=>null,set:()=>{},del:()=>{},listen:()=>()=>{}};function u(e,t){if("undefined"==typeof window)return function(e){function t(){throw new Error(`Illegal storage API usage for storage key "${e}".\nDocusaurus storage APIs are not supposed to be called on the server-rendering process.\nPlease only call storage APIs in effects and event handlers.`)}return{get:t,set:t,del:t,listen:t}}(e);const n=s(t?.persistence);return null===n?c:{get:()=>{try{return n.getItem(e)}catch(t){return console.error(`Docusaurus storage error, can't get key=${e}`,t),null}},set:t=>{try{const a=n.getItem(e);n.setItem(e,t),i({key:e,oldValue:a,newValue:t,storage:n})}catch(a){console.error(`Docusaurus storage error, can't set ${e}=${t}`,a)}},del:()=>{try{const t=n.getItem(e);n.removeItem(e),i({key:e,oldValue:t,newValue:null,storage:n})}catch(t){console.error(`Docusaurus storage error, can't delete key=${e}`,t)}},listen:t=>{try{const a=a=>{a.storageArea===n&&a.key===e&&t(a)};return window.addEventListener("storage",a),()=>window.removeEventListener("storage",a)}catch(a){return console.error(`Docusaurus storage error, can't listen for changes of key=${e}`,a),()=>{}}}}}function d(e,t){const n=(0,a.useRef)((()=>null===e?c:u(e,t))).current(),o=(0,a.useCallback)((e=>"undefined"==typeof window?()=>{}:n.listen(e)),[n]);return[(0,r.useSyncExternalStore)(o,(()=>"undefined"==typeof window?null:n.get()),(()=>null)),n]}},94711:(e,t,n)=>{"use strict";n.d(t,{l:()=>o});var a=n(52263),r=n(16550);function o(){const{siteConfig:{baseUrl:e,url:t},i18n:{defaultLocale:n,currentLocale:o}}=(0,a.Z)(),{pathname:i}=(0,r.TH)(),s=o===n?e:e.replace(`/${o}/`,"/"),l=i.replace(e,"");return{createUrl:function(e){let{locale:a,fullyQualified:r}=e;return`${r?t:""}${function(e){return e===n?`${s}`:`${s}${e}/`}(a)}${l}`}}}},85936:(e,t,n)=>{"use strict";n.d(t,{S:()=>i});var a=n(67294),r=n(16550),o=n(902);function i(e){const t=(0,r.TH)(),n=(0,o.D9)(t),i=(0,o.zX)(e);(0,a.useEffect)((()=>{n&&t!==n&&i({location:t,previousLocation:n})}),[i,t,n])}},86668:(e,t,n)=>{"use strict";n.d(t,{L:()=>r});var a=n(52263);function r(){return(0,a.Z)().siteConfig.themeConfig}},6278:(e,t,n)=>{"use strict";n.d(t,{L:()=>r});var a=n(52263);function r(){const{siteConfig:{themeConfig:e}}=(0,a.Z)();return e}},239:(e,t,n)=>{"use strict";n.d(t,{l:()=>s});var a=n(67294),r=n(98022),o=n(44996),i=n(6278);function s(){const{withBaseUrl:e}=(0,o.C)(),{algolia:{externalUrlRegex:t,replaceSearchResultPathname:n}}=(0,i.L)();return(0,a.useCallback)((a=>{const o=new URL(a);if((0,r.F)(t,o.href))return a;const i=`${o.pathname+o.hash}`;return e(function(e,t){return t?e.replaceAll(new RegExp(t.from,"g"),t.to):e}(i,n))}),[e,t,n])}},8802:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){const{trailingSlash:n,baseUrl:a}=t;if(e.startsWith("#"))return e;if(void 0===n)return e;const[r]=e.split(/[#?]/),o="/"===r||r===a?r:(i=r,n?function(e){return e.endsWith("/")?e:`${e}/`}(i):function(e){return e.endsWith("/")?e.slice(0,-1):e}(i));var i;return e.replace(r,o)}},18780:function(e,t,n){"use strict";var a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.applyTrailingSlash=t.blogPostContainerID=void 0,t.blogPostContainerID="post-content";var r=n(8802);Object.defineProperty(t,"applyTrailingSlash",{enumerable:!0,get:function(){return a(r).default}})},86010:(e,t,n)=>{"use strict";function a(e){var t,n,r="";if("string"==typeof e||"number"==typeof e)r+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;tr});const r=function(){for(var e,t,n=0,r="";n{"use strict";n.d(t,{lX:()=>x,q_:()=>L,ob:()=>m,PP:()=>N,Ep:()=>g,Hp:()=>h});var a=n(87462);function r(e){return"/"===e.charAt(0)}function o(e,t){for(var n=t,a=n+1,r=e.length;a=0;f--){var p=i[f];"."===p?o(i,f):".."===p?(o(i,f),d++):d&&(o(i,f),d--)}if(!c)for(;d--;d)i.unshift("..");!c||""===i[0]||i[0]&&r(i[0])||i.unshift("");var g=i.join("/");return n&&"/"!==g.substr(-1)&&(g+="/"),g};function s(e){return e.valueOf?e.valueOf():Object.prototype.valueOf.call(e)}const l=function e(t,n){if(t===n)return!0;if(null==t||null==n)return!1;if(Array.isArray(t))return Array.isArray(n)&&t.length===n.length&&t.every((function(t,a){return e(t,n[a])}));if("object"==typeof t||"object"==typeof n){var a=s(t),r=s(n);return a!==t||r!==n?e(a,r):Object.keys(Object.assign({},t,n)).every((function(a){return e(t[a],n[a])}))}return!1};var c=n(38776);function u(e){return"/"===e.charAt(0)?e:"/"+e}function d(e){return"/"===e.charAt(0)?e.substr(1):e}function f(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function p(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function g(e){var t=e.pathname,n=e.search,a=e.hash,r=t||"/";return n&&"?"!==n&&(r+="?"===n.charAt(0)?n:"?"+n),a&&"#"!==a&&(r+="#"===a.charAt(0)?a:"#"+a),r}function m(e,t,n,r){var o;"string"==typeof e?(o=function(e){var t=e||"/",n="",a="",r=t.indexOf("#");-1!==r&&(a=t.substr(r),t=t.substr(0,r));var o=t.indexOf("?");return-1!==o&&(n=t.substr(o),t=t.substr(0,o)),{pathname:t,search:"?"===n?"":n,hash:"#"===a?"":a}}(e),o.state=t):(void 0===(o=(0,a.Z)({},e)).pathname&&(o.pathname=""),o.search?"?"!==o.search.charAt(0)&&(o.search="?"+o.search):o.search="",o.hash?"#"!==o.hash.charAt(0)&&(o.hash="#"+o.hash):o.hash="",void 0!==t&&void 0===o.state&&(o.state=t));try{o.pathname=decodeURI(o.pathname)}catch(s){throw s instanceof URIError?new URIError('Pathname "'+o.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):s}return n&&(o.key=n),r?o.pathname?"/"!==o.pathname.charAt(0)&&(o.pathname=i(o.pathname,r.pathname)):o.pathname=r.pathname:o.pathname||(o.pathname="/"),o}function h(e,t){return e.pathname===t.pathname&&e.search===t.search&&e.hash===t.hash&&e.key===t.key&&l(e.state,t.state)}function b(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,a,r){if(null!=e){var o="function"==typeof e?e(t,n):e;"string"==typeof o?"function"==typeof a?a(o,r):r(!0):r(!1!==o)}else r(!0)},appendListener:function(e){var n=!0;function a(){n&&e.apply(void 0,arguments)}return t.push(a),function(){n=!1,t=t.filter((function(e){return e!==a}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),a=0;at?n.splice(t,n.length-t,r):n.push(r),d({action:a,location:r,index:t,entries:n})}}))},replace:function(e,t){var a="REPLACE",r=m(e,t,f(),w.location);u.confirmTransitionTo(r,a,n,(function(e){e&&(w.entries[w.index]=r,d({action:a,location:r}))}))},go:y,goBack:function(){y(-1)},goForward:function(){y(1)},canGo:function(e){var t=w.index+e;return t>=0&&t{"use strict";var a=n(59864),r={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},s={};function l(e){return a.isMemo(e)?i:s[e.$$typeof]||r}s[a.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},s[a.Memo]=i;var c=Object.defineProperty,u=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,f=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,g=Object.prototype;e.exports=function e(t,n,a){if("string"!=typeof n){if(g){var r=p(n);r&&r!==g&&e(t,r,a)}var i=u(n);d&&(i=i.concat(d(n)));for(var s=l(t),m=l(n),h=0;h{"use strict";e.exports=function(e,t,n,a,r,o,i,s){if(!e){var l;if(void 0===t)l=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,a,r,o,i,s],u=0;(l=new Error(t.replace(/%s/g,(function(){return c[u++]})))).name="Invariant Violation"}throw l.framesToPop=1,l}}},5826:e=>{e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},32497:(e,t,n)=>{"use strict";n.r(t)},52295:(e,t,n)=>{"use strict";n.r(t)},74865:function(e,t,n){var a,r;a=function(){var e,t,n={version:"0.2.0"},a=n.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
'};function r(e,t,n){return en?n:e}function o(e){return 100*(-1+e)}function i(e,t,n){var r;return(r="translate3d"===a.positionUsing?{transform:"translate3d("+o(e)+"%,0,0)"}:"translate"===a.positionUsing?{transform:"translate("+o(e)+"%,0)"}:{"margin-left":o(e)+"%"}).transition="all "+t+"ms "+n,r}n.configure=function(e){var t,n;for(t in e)void 0!==(n=e[t])&&e.hasOwnProperty(t)&&(a[t]=n);return this},n.status=null,n.set=function(e){var t=n.isStarted();e=r(e,a.minimum,1),n.status=1===e?null:e;var o=n.render(!t),c=o.querySelector(a.barSelector),u=a.speed,d=a.easing;return o.offsetWidth,s((function(t){""===a.positionUsing&&(a.positionUsing=n.getPositioningCSS()),l(c,i(e,u,d)),1===e?(l(o,{transition:"none",opacity:1}),o.offsetWidth,setTimeout((function(){l(o,{transition:"all "+u+"ms linear",opacity:0}),setTimeout((function(){n.remove(),t()}),u)}),u)):setTimeout(t,u)})),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var e=function(){setTimeout((function(){n.status&&(n.trickle(),e())}),a.trickleSpeed)};return a.trickle&&e(),this},n.done=function(e){return e||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(e){var t=n.status;return t?("number"!=typeof e&&(e=(1-t)*r(Math.random()*t,.1,.95)),t=r(t+e,0,.994),n.set(t)):n.start()},n.trickle=function(){return n.inc(Math.random()*a.trickleRate)},e=0,t=0,n.promise=function(a){return a&&"resolved"!==a.state()?(0===t&&n.start(),e++,t++,a.always((function(){0==--t?(e=0,n.done()):n.set((e-t)/e)})),this):this},n.render=function(e){if(n.isRendered())return document.getElementById("nprogress");u(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=a.template;var r,i=t.querySelector(a.barSelector),s=e?"-100":o(n.status||0),c=document.querySelector(a.parent);return l(i,{transition:"all 0 linear",transform:"translate3d("+s+"%,0,0)"}),a.showSpinner||(r=t.querySelector(a.spinnerSelector))&&p(r),c!=document.body&&u(c,"nprogress-custom-parent"),c.appendChild(t),t},n.remove=function(){d(document.documentElement,"nprogress-busy"),d(document.querySelector(a.parent),"nprogress-custom-parent");var e=document.getElementById("nprogress");e&&p(e)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var e=document.body.style,t="WebkitTransform"in e?"Webkit":"MozTransform"in e?"Moz":"msTransform"in e?"ms":"OTransform"in e?"O":"";return t+"Perspective"in e?"translate3d":t+"Transform"in e?"translate":"margin"};var s=function(){var e=[];function t(){var n=e.shift();n&&n(t)}return function(n){e.push(n),1==e.length&&t()}}(),l=function(){var e=["Webkit","O","Moz","ms"],t={};function n(e){return e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(function(e,t){return t.toUpperCase()}))}function a(t){var n=document.body.style;if(t in n)return t;for(var a,r=e.length,o=t.charAt(0).toUpperCase()+t.slice(1);r--;)if((a=e[r]+o)in n)return a;return t}function r(e){return e=n(e),t[e]||(t[e]=a(e))}function o(e,t,n){t=r(t),e.style[t]=n}return function(e,t){var n,a,r=arguments;if(2==r.length)for(n in t)void 0!==(a=t[n])&&t.hasOwnProperty(n)&&o(e,n,a);else o(e,r[1],r[2])}}();function c(e,t){return("string"==typeof e?e:f(e)).indexOf(" "+t+" ")>=0}function u(e,t){var n=f(e),a=n+t;c(n,t)||(e.className=a.substring(1))}function d(e,t){var n,a=f(e);c(e,t)&&(n=a.replace(" "+t+" "," "),e.className=n.substring(1,n.length-1))}function f(e){return(" "+(e.className||"")+" ").replace(/\s+/gi," ")}function p(e){e&&e.parentNode&&e.parentNode.removeChild(e)}return n},void 0===(r="function"==typeof a?a.call(t,n,t,e):a)||(e.exports=r)},27418:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;function r(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var a={};return"abcdefghijklmnopqrst".split("").forEach((function(e){a[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},a)).join("")}catch(r){return!1}}()?Object.assign:function(e,o){for(var i,s,l=r(e),c=1;c{"use strict";n.d(t,{Z:()=>o});var a=function(){var e=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,n={},a={util:{encode:function e(t){return t instanceof r?new r(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=d.reach);x+=k.value.length,k=k.next){var E=k.value;if(t.length>e.length)return;if(!(E instanceof r)){var S,T=1;if(v){if(!(S=o(_,x,e,b))||S.index>=e.length)break;var C=S.index,A=S.index+S[0].length,L=x;for(L+=k.value.length;C>=L;)L+=(k=k.next).value.length;if(x=L-=k.value.length,k.value instanceof r)continue;for(var P=k;P!==t.tail&&(Ld.reach&&(d.reach=M);var I=k.prev;if(O&&(I=l(t,I,O),x+=O.length),c(t,I,T),k=l(t,I,new r(f,h?a.tokenize(N,h):N,y,N)),R&&l(t,k,R),T>1){var j={cause:f+","+g,reach:M};i(e,t,n,k.prev,x,j),d&&j.reach>d.reach&&(d.reach=j.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function l(e,t,n){var a=t.next,r={value:n,prev:t,next:a};return t.next=r,a.prev=r,e.length++,r}function c(e,t,n){for(var a=t.next,r=0;r"+o.content+""},a}(),r=a;a.default=a,r.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},r.languages.markup.tag.inside["attr-value"].inside.entity=r.languages.markup.entity,r.languages.markup.doctype.inside["internal-subset"].inside=r.languages.markup,r.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(r.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:r.languages[t]},n.cdata=/^$/i;var a={"included-cdata":{pattern://i,inside:n}};a["language-"+t]={pattern:/[\s\S]+/,inside:r.languages[t]};var o={};o[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:a},r.languages.insertBefore("markup","cdata",o)}}),Object.defineProperty(r.languages.markup.tag,"addAttribute",{value:function(e,t){r.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:r.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),r.languages.html=r.languages.markup,r.languages.mathml=r.languages.markup,r.languages.svg=r.languages.markup,r.languages.xml=r.languages.extend("markup",{}),r.languages.ssml=r.languages.xml,r.languages.atom=r.languages.xml,r.languages.rss=r.languages.xml,function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",n={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},a={bash:n,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:a},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:n}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:a},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:a.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:a.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},n.inside=e.languages.bash;for(var r=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=a.variable[1].inside,i=0;i]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},r.languages.c=r.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),r.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),r.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},r.languages.c.string],char:r.languages.c.char,comment:r.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:r.languages.c}}}}),r.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete r.languages.c.boolean,function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace(//g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace(//g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/(?:\s*:\s*)?|:\s*/.source.replace(//g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(r),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(r),function(e){var t,n=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css.selector={pattern:e.languages.css.selector.pattern,lookbehind:!0,inside:t={"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/,"pseudo-class":/:[-\w]+/,class:/\.[-\w]+/,id:/#[-\w]+/,attribute:{pattern:RegExp("\\[(?:[^[\\]\"']|"+n.source+")*\\]"),greedy:!0,inside:{punctuation:/^\[|\]$/,"case-sensitivity":{pattern:/(\s)[si]$/i,lookbehind:!0,alias:"keyword"},namespace:{pattern:/^(\s*)(?:(?!\s)[-*\w\xA0-\uFFFF])*\|(?!=)/,lookbehind:!0,inside:{punctuation:/\|$/}},"attr-name":{pattern:/^(\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+/,lookbehind:!0},"attr-value":[n,{pattern:/(=\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+(?=\s*$)/,lookbehind:!0}],operator:/[|~*^$]?=/}},"n-th":[{pattern:/(\(\s*)[+-]?\d*[\dn](?:\s*[+-]\s*\d+)?(?=\s*\))/,lookbehind:!0,inside:{number:/[\dn]+/,operator:/[+-]/}},{pattern:/(\(\s*)(?:even|odd)(?=\s*\))/i,lookbehind:!0}],combinator:/>|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}});var a={pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0},r={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0};e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:a,number:r,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:a,number:r})}(r),r.languages.javascript=r.languages.extend("clike",{"class-name":[r.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),r.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,r.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:r.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:r.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:r.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:r.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:r.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),r.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:r.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),r.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),r.languages.markup&&(r.languages.markup.tag.addInlined("script","javascript"),r.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),r.languages.js=r.languages.javascript,function(e){var t=/#(?!\{).+/,n={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:n}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:n}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:e.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:n}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"],e.languages.coffee=e.languages.coffeescript}(r),function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,a="(?:"+n.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+n.source+")?)",r=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*/.source.replace(//g,(function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source})),o=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function i(e,t){t=(t||"").replace(/m/g,"")+"m";var n=/([:\-,[{]\s*(?:\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<>/g,(function(){return a})).replace(/<>/g,(function(){return e}));return RegExp(n,t)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<>/g,(function(){return a}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\s*:\s)/.source.replace(/<>/g,(function(){return a})).replace(/<>/g,(function(){return"(?:"+r+"|"+o+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:i(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:i(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:i(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:i(o),lookbehind:!0,greedy:!0},number:{pattern:i(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:n,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(r),function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function n(e){return e=e.replace(//g,(function(){return t})),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+e+")")}var a=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,r=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,(function(){return a})),o=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+r+o+"(?:"+r+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+r+o+")(?:"+r+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(a),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+r+")"+o+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+r+"$"),inside:{"table-header":{pattern:RegExp(a),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n(/\b__(?:(?!_)|_(?:(?!_))+_)+__\b|\*\*(?:(?!\*)|\*(?:(?!\*))+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n(/\b_(?:(?!_)|__(?:(?!_))+__)+_\b|\*(?:(?!\*)|\*\*(?:(?!\*))+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n(/(~~?)(?:(?!~))+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:n(/!?\[(?:(?!\]))+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\]))+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(t){["url","bold","italic","strike","code-snippet"].forEach((function(n){t!==n&&(e.languages.markdown[t].inside.content.inside[n]=e.languages.markdown[n])}))})),e.hooks.add("after-tokenize",(function(e){"markdown"!==e.language&&"md"!==e.language||function e(t){if(t&&"string"!=typeof t)for(var n=0,a=t.length;n",quot:'"'},l=String.fromCodePoint||String.fromCharCode;e.languages.md=e.languages.markdown}(r),r.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:r.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},r.hooks.add("after-tokenize",(function(e){if("graphql"===e.language)for(var t=e.tokens.filter((function(e){return"string"!=typeof e&&"comment"!==e.type&&"scalar"!==e.type})),n=0;n0)){var s=f(/^\{$/,/^\}$/);if(-1===s)continue;for(var l=n;l=0&&p(c,"variable-input")}}}}function u(e){return t[n+e]}function d(e,t){t=t||0;for(var n=0;n?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/},function(e){var t=e.languages.javascript["template-string"],n=t.pattern.source,a=t.inside.interpolation,r=a.inside["interpolation-punctuation"],o=a.pattern.source;function i(t,a){if(e.languages[t])return{pattern:RegExp("((?:"+a+")\\s*)"+n),lookbehind:!0,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},"embedded-code":{pattern:/[\s\S]+/,alias:t}}}}function s(e,t){return"___"+t.toUpperCase()+"_"+e+"___"}function l(t,n,a){var r={code:t,grammar:n,language:a};return e.hooks.run("before-tokenize",r),r.tokens=e.tokenize(r.code,r.grammar),e.hooks.run("after-tokenize",r),r.tokens}function c(t){var n={};n["interpolation-punctuation"]=r;var o=e.tokenize(t,n);if(3===o.length){var i=[1,1];i.push.apply(i,l(o[1],e.languages.javascript,"javascript")),o.splice.apply(o,i)}return new e.Token("interpolation",o,a.alias,t)}function u(t,n,a){var r=e.tokenize(t,{interpolation:{pattern:RegExp(o),lookbehind:!0}}),i=0,u={},d=l(r.map((function(e){if("string"==typeof e)return e;for(var n,r=e.content;-1!==t.indexOf(n=s(i++,a)););return u[n]=r,n})).join(""),n,a),f=Object.keys(u);return i=0,function e(t){for(var n=0;n=f.length)return;var a=t[n];if("string"==typeof a||"string"==typeof a.content){var r=f[i],o="string"==typeof a?a:a.content,s=o.indexOf(r);if(-1!==s){++i;var l=o.substring(0,s),d=c(u[r]),p=o.substring(s+r.length),g=[];if(l&&g.push(l),g.push(d),p){var m=[p];e(m),g.push.apply(g,m)}"string"==typeof a?(t.splice.apply(t,[n,1].concat(g)),n+=g.length-1):a.content=g}}else{var h=a.content;Array.isArray(h)?e(h):e([h])}}}(d),new e.Token(a,d,"language-"+a,t)}e.languages.javascript["template-string"]=[i("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),i("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),i("svg",/\bsvg/.source),i("markdown",/\b(?:markdown|md)/.source),i("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),i("sql",/\bsql/.source),t].filter(Boolean);var d={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function f(e){return"string"==typeof e?e:Array.isArray(e)?e.map(f).join(""):f(e.content)}e.hooks.add("after-tokenize",(function(t){t.language in d&&function t(n){for(var a=0,r=n.length;a]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var t=e.languages.extend("typescript",{});delete t["class-name"],e.languages.typescript["class-name"].inside=t,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t}}}}),e.languages.ts=e.languages.typescript}(r),function(e){function t(e,t){return RegExp(e.replace(//g,(function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source})),t)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|(?:Weak)?(?:Map|Set)|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:(?:\s*,\s*(?:\*\s*as\s+|\{[^{}]*\}))?|\*\s*as\s+|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|finally|for|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|(?:local|session)Storage|location|navigator|performance|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var n=["function","function-variable","method","method-variable","property-access"],a=0;a*\.{3}(?:[^{}]|)*\})/.source;function o(e,t){return e=e.replace(//g,(function(){return n})).replace(//g,(function(){return a})).replace(//g,(function(){return r})),RegExp(e,t)}r=o(r).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=o(/<\/?(?:[\w.:-]+(?:+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|))?|))**\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:o(//.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:o(/=/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx}}},e.languages.jsx.tag);var i=function(e){return e?"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(i).join(""):""},s=function(t){for(var n=[],a=0;a0&&n[n.length-1].tagName===i(r.content[0].content[1])&&n.pop():"/>"===r.content[r.content.length-1].content||n.push({tagName:i(r.content[0].content[1]),openedBraces:0}):n.length>0&&"punctuation"===r.type&&"{"===r.content?n[n.length-1].openedBraces++:n.length>0&&n[n.length-1].openedBraces>0&&"punctuation"===r.type&&"}"===r.content?n[n.length-1].openedBraces--:o=!0),(o||"string"==typeof r)&&n.length>0&&0===n[n.length-1].openedBraces){var l=i(r);a0&&("string"==typeof t[a-1]||"plain-text"===t[a-1].type)&&(l=i(t[a-1])+l,t.splice(a-1,1),a--),t[a]=new e.Token("plain-text",l,null,l)}r.content&&"string"!=typeof r.content&&s(r.content)}};e.hooks.add("after-tokenize",(function(e){"jsx"!==e.language&&"tsx"!==e.language||s(e.tokens)}))}(r),function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var t={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(t).forEach((function(n){var a=t[n],r=[];/^\w+$/.test(n)||r.push(/\w+/.exec(n)[0]),"diff"===n&&r.push("bold"),e.languages.diff[n]={pattern:RegExp("^(?:["+a+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:r,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(n)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:t})}(r),r.languages.git={comment:/^#.*/m,deleted:/^[-\u2013].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m},r.languages.go=r.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),r.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete r.languages.go["class-name"],function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(n,a,r,o){if(n.language===a){var i=n.tokenStack=[];n.code=n.code.replace(r,(function(e){if("function"==typeof o&&!o(e))return e;for(var r,s=i.length;-1!==n.code.indexOf(r=t(a,s));)++s;return i[s]=e,r})),n.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(n,a){if(n.language===a&&n.tokenStack){n.grammar=e.languages[a];var r=0,o=Object.keys(n.tokenStack);!function i(s){for(var l=0;l=o.length);l++){var c=s[l];if("string"==typeof c||c.content&&"string"==typeof c.content){var u=o[r],d=n.tokenStack[u],f="string"==typeof c?c:c.content,p=t(a,u),g=f.indexOf(p);if(g>-1){++r;var m=f.substring(0,g),h=new e.Token(a,e.tokenize(d,n.grammar),"language-"+a,d),b=f.substring(g+p.length),v=[];m&&v.push.apply(v,i([m])),v.push(h),b&&v.push.apply(v,i([b])),"string"==typeof c?s.splice.apply(s,[l,1].concat(v)):c.content=v}}else c.content&&i(c.content)}return s}(n.tokens)}}}})}(r),function(e){e.languages.handlebars={comment:/\{\{![\s\S]*?\}\}/,delimiter:{pattern:/^\{\{\{?|\}\}\}?$/,alias:"punctuation"},string:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee][+-]?\d+)?/,boolean:/\b(?:false|true)\b/,block:{pattern:/^(\s*(?:~\s*)?)[#\/]\S+?(?=\s*(?:~\s*)?$|\s)/,lookbehind:!0,alias:"keyword"},brackets:{pattern:/\[[^\]]+\]/,inside:{punctuation:/\[|\]/,variable:/[\s\S]+/}},punctuation:/[!"#%&':()*+,.\/;<=>@\[\\\]^`{|}~]/,variable:/[^!"#%&'()*+,\/;<=>@\[\\\]^`{|}~\s]+/},e.hooks.add("before-tokenize",(function(t){e.languages["markup-templating"].buildPlaceholders(t,"handlebars",/\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\}/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"handlebars")})),e.languages.hbs=e.languages.handlebars}(r),r.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},r.languages.webmanifest=r.languages.json,r.languages.less=r.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-](?:\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};@\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/,operator:/[+\-*\/]/}),r.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/,lookbehind:!0,alias:"function"}}),r.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"builtin-target":{pattern:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,alias:"builtin"},target:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,alias:"symbol",inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,function:{pattern:/(\()(?:abspath|addsuffix|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:list|s)?)(?=[ \t])/,lookbehind:!0},operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/},r.languages.objectivec=r.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete r.languages.objectivec["class-name"],r.languages.objc=r.languages.objectivec,r.languages.ocaml={comment:{pattern:/\(\*[\s\S]*?\*\)/,greedy:!0},char:{pattern:/'(?:[^\\\r\n']|\\(?:.|[ox]?[0-9a-f]{1,3}))'/i,greedy:!0},string:[{pattern:/"(?:\\(?:[\s\S]|\r\n)|[^\\\r\n"])*"/,greedy:!0},{pattern:/\{([a-z_]*)\|[\s\S]*?\|\1\}/,greedy:!0}],number:[/\b(?:0b[01][01_]*|0o[0-7][0-7_]*)\b/i,/\b0x[a-f0-9][a-f0-9_]*(?:\.[a-f0-9_]*)?(?:p[+-]?\d[\d_]*)?(?!\w)/i,/\b\d[\d_]*(?:\.[\d_]*)?(?:e[+-]?\d[\d_]*)?(?!\w)/i],directive:{pattern:/\B#\w+/,alias:"property"},label:{pattern:/\B~\w+/,alias:"property"},"type-variable":{pattern:/\B'\w+/,alias:"function"},variant:{pattern:/`\w+/,alias:"symbol"},keyword:/\b(?:as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|match|method|module|mutable|new|nonrec|object|of|open|private|rec|sig|struct|then|to|try|type|val|value|virtual|when|where|while|with)\b/,boolean:/\b(?:false|true)\b/,"operator-like-punctuation":{pattern:/\[[<>|]|[>|]\]|\{<|>\}/,alias:"punctuation"},operator:/\.[.~]|:[=>]|[=<>@^|&+\-*\/$%!?~][!$%&*+\-.\/:<=>?@^|~]*|\b(?:and|asr|land|lor|lsl|lsr|lxor|mod|or)\b/,punctuation:/;;|::|[(){}\[\].,:;#]|\b_\b/},r.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},r.languages.python["string-interpolation"].inside.interpolation.inside.rest=r.languages.python,r.languages.py=r.languages.python,r.languages.reason=r.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/}),r.languages.insertBefore("reason","class-name",{char:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,greedy:!0},constructor:/\b[A-Z]\w*\b(?!\s*\.)/,label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete r.languages.reason.function,function(e){e.languages.sass=e.languages.extend("css",{comment:{pattern:/^([ \t]*)\/[\/*].*(?:(?:\r?\n|\r)\1[ \t].+)*/m,lookbehind:!0,greedy:!0}}),e.languages.insertBefore("sass","atrule",{"atrule-line":{pattern:/^(?:[ \t]*)[@+=].+/m,greedy:!0,inside:{atrule:/(?:@[\w-]+|[+=])/}}}),delete e.languages.sass.atrule;var t=/\$[-\w]+|#\{\$[-\w]+\}/,n=[/[+*\/%]|[=!]=|<=?|>=?|\b(?:and|not|or)\b/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}];e.languages.insertBefore("sass","property",{"variable-line":{pattern:/^[ \t]*\$.+/m,greedy:!0,inside:{punctuation:/:/,variable:t,operator:n}},"property-line":{pattern:/^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m,greedy:!0,inside:{property:[/[^:\s]+(?=\s*:)/,{pattern:/(:)[^:\s]+/,lookbehind:!0}],punctuation:/:/,variable:t,operator:n,important:e.languages.sass.important}}}),delete e.languages.sass.property,delete e.languages.sass.important,e.languages.insertBefore("sass","punctuation",{selector:{pattern:/^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m,lookbehind:!0,greedy:!0}})}(r),r.languages.scss=r.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),r.languages.insertBefore("scss","atrule",{keyword:[/@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),r.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),r.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|hide|show|with)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/,lookbehind:!0}}),r.languages.scss.atrule.inside.rest=r.languages.scss,function(e){var t={pattern:/(\b\d+)(?:%|[a-z]+)/,lookbehind:!0},n={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0},a={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},url:{pattern:/\burl\((["']?).*?\1\)/i,greedy:!0},string:{pattern:/("|')(?:(?!\1)[^\\\r\n]|\\(?:\r\n|[\s\S]))*\1/,greedy:!0},interpolation:null,func:null,important:/\B!(?:important|optional)\b/i,keyword:{pattern:/(^|\s+)(?:(?:else|for|if|return|unless)(?=\s|$)|@[\w-]+)/,lookbehind:!0},hexcode:/#[\da-f]{3,6}/i,color:[/\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i,{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:n,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,boolean:/\b(?:false|true)\b/,operator:[/~|[+!\/%<>?=]=?|[-:]=|\*[*=]?|\.{2,3}|&&|\|\||\B-\B|\b(?:and|in|is(?: a| defined| not|nt)?|not|or)\b/],number:n,punctuation:/[{}()\[\];:,]/};a.interpolation={pattern:/\{[^\r\n}:]+\}/,alias:"variable",inside:{delimiter:{pattern:/^\{|\}$/,alias:"punctuation"},rest:a}},a.func={pattern:/[\w-]+\([^)]*\).*/,inside:{function:/^[^(]+/,rest:a}},e.languages.stylus={"atrule-declaration":{pattern:/(^[ \t]*)@.+/m,lookbehind:!0,inside:{atrule:/^@[\w-]+/,rest:a}},"variable-declaration":{pattern:/(^[ \t]*)[\w$-]+\s*.?=[ \t]*(?:\{[^{}]*\}|\S.*|$)/m,lookbehind:!0,inside:{variable:/^\S+/,rest:a}},statement:{pattern:/(^[ \t]*)(?:else|for|if|return|unless)[ \t].+/m,lookbehind:!0,inside:{keyword:/^\S+/,rest:a}},"property-declaration":{pattern:/((?:^|\{)([ \t]*))(?:[\w-]|\{[^}\r\n]+\})+(?:\s*:\s*|[ \t]+)(?!\s)[^{\r\n]*(?:;|[^{\r\n,]$(?!(?:\r?\n|\r)(?:\{|\2[ \t])))/m,lookbehind:!0,inside:{property:{pattern:/^[^\s:]+/,inside:{interpolation:a.interpolation}},rest:a}},selector:{pattern:/(^[ \t]*)(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)(?:(?:\r?\n|\r)(?:\1(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)))*(?:,$|\{|(?=(?:\r?\n|\r)(?:\{|\1[ \t])))/m,lookbehind:!0,inside:{interpolation:a.interpolation,comment:a.comment,punctuation:/[{},]/}},func:a.func,string:a.string,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0,greedy:!0},interpolation:a.interpolation,punctuation:/[{}()\[\];:.]/}}(r),function(e){var t=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",t),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var n=e.languages.tsx.tag;n.pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+n.pattern.source+")",n.pattern.flags),n.lookbehind=!0}(r),r.languages.wasm={comment:[/\(;[\s\S]*?;\)/,{pattern:/;;.*/,greedy:!0}],string:{pattern:/"(?:\\[\s\S]|[^"\\])*"/,greedy:!0},keyword:[{pattern:/\b(?:align|offset)=/,inside:{operator:/=/}},{pattern:/\b(?:(?:f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|neg?|nearest|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|sqrt|store(?:8|16|32)?|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))?|memory\.(?:grow|size))\b/,inside:{punctuation:/\./}},/\b(?:anyfunc|block|br(?:_if|_table)?|call(?:_indirect)?|data|drop|elem|else|end|export|func|get_(?:global|local)|global|if|import|local|loop|memory|module|mut|nop|offset|param|result|return|select|set_(?:global|local)|start|table|tee_local|then|type|unreachable)\b/],variable:/\$[\w!#$%&'*+\-./:<=>?@\\^`|~]+/,number:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/,punctuation:/[()]/};const o=r},40485:()=>{!function(e){var t={pattern:/((?:^|[^\\$])(?:\\{2})*)\$(?:\w+|\{[^{}]*\})/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{?|\}$/,alias:"punctuation"},expression:{pattern:/[\s\S]+/,inside:null}}};e.languages.groovy=e.languages.extend("clike",{string:{pattern:/'''(?:[^\\]|\\[\s\S])*?'''|'(?:\\.|[^\\'\r\n])*'/,greedy:!0},keyword:/\b(?:abstract|as|assert|boolean|break|byte|case|catch|char|class|const|continue|def|default|do|double|else|enum|extends|final|finally|float|for|goto|if|implements|import|in|instanceof|int|interface|long|native|new|package|private|protected|public|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|trait|transient|try|void|volatile|while)\b/,number:/\b(?:0b[01_]+|0x[\da-f_]+(?:\.[\da-f_p\-]+)?|[\d_]+(?:\.[\d_]+)?(?:e[+-]?\d+)?)[glidf]?\b/i,operator:{pattern:/(^|[^.])(?:~|==?~?|\?[.:]?|\*(?:[.=]|\*=?)?|\.[@&]|\.\.<|\.\.(?!\.)|-[-=>]?|\+[+=]?|!=?|<(?:<=?|=>?)?|>(?:>>?=?|=)?|&[&=]?|\|[|=]?|\/=?|\^=?|%=?)/,lookbehind:!0},punctuation:/\.+|[{}[\];(),:$]/}),e.languages.insertBefore("groovy","string",{shebang:{pattern:/#!.+/,alias:"comment",greedy:!0},"interpolation-string":{pattern:/"""(?:[^\\]|\\[\s\S])*?"""|(["/])(?:\\.|(?!\1)[^\\\r\n])*\1|\$\/(?:[^/$]|\$(?:[/$]|(?![/$]))|\/(?!\$))*\/\$/,greedy:!0,inside:{interpolation:t,string:/[\s\S]+/}}}),e.languages.insertBefore("groovy","punctuation",{"spock-block":/\b(?:and|cleanup|expect|given|setup|then|when|where):/}),e.languages.insertBefore("groovy","function",{annotation:{pattern:/(^|[^.])@\w+/,lookbehind:!0,alias:"punctuation"}}),t.inside.expression.inside=e.languages.groovy}(Prism)},52503:()=>{!function(e){var t=/\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record(?!\s*[(){}[\]<>=%~.:,;?+\-*/&|^])|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/,n=/(?:[a-z]\w*\s*\.\s*)*(?:[A-Z]\w*\s*\.\s*)*/.source,a={pattern:RegExp(/(^|[^\w.])/.source+n+/[A-Z](?:[\d_A-Z]*[a-z]\w*)?\b/.source),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}},punctuation:/\./}};e.languages.java=e.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"/,lookbehind:!0,greedy:!0},"class-name":[a,{pattern:RegExp(/(^|[^\w.])/.source+n+/[A-Z]\w*(?=\s+\w+\s*[;,=()]|\s*(?:\[[\s,]*\]\s*)?::\s*new\b)/.source),lookbehind:!0,inside:a.inside},{pattern:RegExp(/(\b(?:class|enum|extends|implements|instanceof|interface|new|record|throws)\s+)/.source+n+/[A-Z]\w*\b/.source),lookbehind:!0,inside:a.inside}],keyword:t,function:[e.languages.clike.function,{pattern:/(::\s*)[a-z_]\w*/,lookbehind:!0}],number:/\b0b[01][01_]*L?\b|\b0x(?:\.[\da-f_p+-]+|[\da-f_]+(?:\.[\da-f_p+-]+)?)\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,lookbehind:!0},constant:/\b[A-Z][A-Z_\d]+\b/}),e.languages.insertBefore("java","string",{"triple-quoted-string":{pattern:/"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/,greedy:!0,alias:"string"},char:{pattern:/'(?:\\.|[^'\\\r\n]){1,6}'/,greedy:!0}}),e.languages.insertBefore("java","class-name",{annotation:{pattern:/(^|[^.])@\w+(?:\s*\.\s*\w+)*/,lookbehind:!0,alias:"punctuation"},generics:{pattern:/<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/,inside:{"class-name":a,keyword:t,punctuation:/[<>(),.:]/,operator:/[?&|]/}},import:[{pattern:RegExp(/(\bimport\s+)/.source+n+/(?:[A-Z]\w*|\*)(?=\s*;)/.source),lookbehind:!0,inside:{namespace:a.inside.namespace,punctuation:/\./,operator:/\*/,"class-name":/\w+/}},{pattern:RegExp(/(\bimport\s+static\s+)/.source+n+/(?:\w+|\*)(?=\s*;)/.source),lookbehind:!0,alias:"static",inside:{namespace:a.inside.namespace,static:/\b\w+$/,punctuation:/\./,operator:/\*/,"class-name":/\w+/}}],namespace:{pattern:RegExp(/(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)(?!)[a-z]\w*(?:\.[a-z]\w*)*\.?/.source.replace(//g,(function(){return t.source}))),lookbehind:!0,inside:{punctuation:/\./}}})}(Prism)},32334:()=>{!function(e){e.languages.kotlin=e.languages.extend("clike",{keyword:{pattern:/(^|[^.])\b(?:abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|to|try|typealias|val|var|vararg|when|where|while)\b/,lookbehind:!0},function:[{pattern:/(?:`[^\r\n`]+`|\b\w+)(?=\s*\()/,greedy:!0},{pattern:/(\.)(?:`[^\r\n`]+`|\w+)(?=\s*\{)/,lookbehind:!0,greedy:!0}],number:/\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/,operator:/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/}),delete e.languages.kotlin["class-name"];var t={"interpolation-punctuation":{pattern:/^\$\{?|\}$/,alias:"punctuation"},expression:{pattern:/[\s\S]+/,inside:e.languages.kotlin}};e.languages.insertBefore("kotlin","string",{"string-literal":[{pattern:/"""(?:[^$]|\$(?:(?!\{)|\{[^{}]*\}))*?"""/,alias:"multiline",inside:{interpolation:{pattern:/\$(?:[a-z_]\w*|\{[^{}]*\})/i,inside:t},string:/[\s\S]+/}},{pattern:/"(?:[^"\\\r\n$]|\\.|\$(?:(?!\{)|\{[^{}]*\}))*"/,alias:"singleline",inside:{interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$(?:[a-z_]\w*|\{[^{}]*\})/i,lookbehind:!0,inside:t},string:/[\s\S]+/}}],char:{pattern:/'(?:[^'\\\r\n]|\\(?:.|u[a-fA-F0-9]{0,4}))'/,greedy:!0}}),delete e.languages.kotlin.string,e.languages.insertBefore("kotlin","keyword",{annotation:{pattern:/\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,alias:"builtin"}}),e.languages.insertBefore("kotlin","function",{label:{pattern:/\b\w+@|@\w+\b/,alias:"symbol"}}),e.languages.kt=e.languages.kotlin,e.languages.kts=e.languages.kotlin}(Prism)},52811:(e,t,n)=>{var a={"./prism-groovy":40485,"./prism-java":52503,"./prism-kotlin":32334};function r(e){var t=o(e);return n(t)}function o(e){if(!n.o(a,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return a[e]}r.keys=function(){return Object.keys(a)},r.resolve=o,e.exports=r,r.id=52811},92703:(e,t,n)=>{"use strict";var a=n(50414);function r(){}function o(){}o.resetWarningCache=r,e.exports=function(){function e(e,t,n,r,o,i){if(i!==a){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:r};return n.PropTypes=n,n}},45697:(e,t,n)=>{e.exports=n(92703)()},50414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},64448:(e,t,n)=>{"use strict";var a=n(67294),r=n(27418),o=n(63840);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n
+ + \ No newline at end of file diff --git a/docs/etc/communication.html b/docs/etc/communication.html index 38088fd2a..77c4e360b 100644 --- a/docs/etc/communication.html +++ b/docs/etc/communication.html @@ -13,15 +13,15 @@ - - + +

커뮤니케이션 잘하는 개발자의 4가지 습관

커뮤니케이션 잘 하는 개발자?

스펙 구현형 개발자 vs 문제 해결형 개발자

개발자 != 스펙을 주면 잘 구현하는 사람
구현에 집중한다면 일의 시야가 좁아진다.

의도와 맥락을 이해해서, 더 좋은 스펙을 만들어내려고 하는 개발자가 되어야 한다.

변화를 만들려면 습관이 필요하다.

주니어 개발자는 스펙 구현 개발자에서 시작한다.
-변화를 위해 실제로 행동하는 습관이 필요하다.

좋은 습관 4가지

  1. 해결하려는 문제의도/상황에 대해 묻는다.
  2. 상대방의 말을 듣고 내가 이해한 바를 요약하여 공유한다.
    • 한 번 정리한다면 1... 2... 이렇게 이해했는데 맞을까요?
  3. 안 된다고 말할 때는 상대방에 관점에서 대안을 제시한다.
    • 제약을 덜 받는 다른 방향성/대안 제시
  4. 문제를 해결할 또 다른 방법은 없을지 고민해본다.
    • 단정대신 한 번 더 질문하고 생각

참고 자료

커뮤니케이션 잘하는 개발자의 4가지 습관 - 송범근, INFCON 2023

- - +변화를 위해 실제로 행동하는 습관이 필요하다.

좋은 습관 4가지

  1. 해결하려는 문제의도/상황에 대해 묻는다.
  2. 상대방의 말을 듣고 내가 이해한 바를 요약하여 공유한다.
    • 한 번 정리한다면 1... 2... 이렇게 이해했는데 맞을까요?
  3. 안 된다고 말할 때는 상대방에 관점에서 대안을 제시한다.
    • 제약을 덜 받는 다른 방향성/대안 제시
  4. 문제를 해결할 또 다른 방법은 없을지 고민해본다.
    • 단정대신 한 번 더 질문하고 생각

참고 자료

커뮤니케이션 잘하는 개발자의 4가지 습관 - 송범근, INFCON 2023

+ + \ No newline at end of file diff --git a/docs/etc/develop-with-spring.html b/docs/etc/develop-with-spring.html index 248745a8f..ffb3a4567 100644 --- a/docs/etc/develop-with-spring.html +++ b/docs/etc/develop-with-spring.html @@ -13,8 +13,8 @@ - - + +
@@ -31,8 +31,8 @@ 초기 개발 생산성, 변경 용이성 등을 관찰

공유와 논쟁

문서, 발표 자료로 정리
정리에 시간을 많이 들이면 효율성이 떨어지기에 중요한 부분만 정리
간단한 작성, 검색이 가능한 도구 활용
-나만의 정의와 설명을 만들어가기 -> 한 문장, 한 문단, 5분간 설명, 점점 늘려가며

참고 자료

스프링과 함께 더 나은 개발자 되기 - 토비, INFCON 2023

- - +나만의 정의와 설명을 만들어가기 -> 한 문장, 한 문단, 5분간 설명, 점점 늘려가며

참고 자료

스프링과 함께 더 나은 개발자 되기 - 토비, INFCON 2023

+ + \ No newline at end of file diff --git a/docs/etc/experience-and-self-question.html b/docs/etc/experience-and-self-question.html index 4295e3f5e..f86023d64 100644 --- a/docs/etc/experience-and-self-question.html +++ b/docs/etc/experience-and-self-question.html @@ -13,8 +13,8 @@ - - + +
@@ -27,8 +27,8 @@ 당연하다 생각하는 해결책에 의구심을 가지고 새로운 접근 방식으로 도전한 경험

자신에게 던져봐야할 질문

나는 프로그래밍 자체를 즐기고 있는가?
나는 왜 프로그래머가 되려고 하는가?
나는 나답게 살고 있나?
-나는 주도적으로 살고 있나?

- - +나는 주도적으로 살고 있나?

+ + \ No newline at end of file diff --git a/docs/etc/healthful-growth.html b/docs/etc/healthful-growth.html index d9b67062e..b543daa6f 100644 --- a/docs/etc/healthful-growth.html +++ b/docs/etc/healthful-growth.html @@ -13,8 +13,8 @@ - - + +
@@ -36,8 +36,8 @@ 커뮤니케이션 방법, 신뢰 자산을 확보하는 방법, 문화를 만들어가는 방법, 결정의 기준과 같은 부분을 학습할 수 있다.

보상

시련 뒤에는 항상 보물이 기다리고 있다.
보상을 통해 꾸준함을 유지할 수 있도록 만들어라.

남을 설득하는 방법 배우기

팀원들이 매번 내 의견을 반대한다면 완벽한 논리가 중요한게 아니다.
어떻게 하면 신뢰 자산을 확보할 수 있는가?
-커뮤니케이션, 협업, 소프트 스킬에서 부족함이 있으면 안된다.

- - +커뮤니케이션, 협업, 소프트 스킬에서 부족함이 있으면 안된다.

+ + \ No newline at end of file diff --git a/docs/jpa/key.html b/docs/jpa/key.html index 11b1d5e25..9395b179e 100644 --- a/docs/jpa/key.html +++ b/docs/jpa/key.html @@ -13,8 +13,8 @@ - - + +
@@ -29,8 +29,8 @@ MySQL의 경우 IDENTITY Oracle의 경우 SEQUENCE를 선택한다.

UUID

JPA 3.1.0 UUID 생성 전략이 추가되었다.
Hibernate 6.2부터 JPA 3.1.0을 지원하기 때문에 스프링 부트 3.1 이상인 경우 사용할 수 있다.

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Member {

@Id
@GeneratedValue(strategy = GenerationType.UUID)
@UuidGenerator(style = Style.RANDOM)
private Long id;
}

UuidGenerator를 이용하여 UUID 생성 방식도 설정할 수 있다.
생성 방식은 3가지가 있다.

  • RANDOM - 난수 기반 UUID 생성(uuid v4)
  • TIME – 시간 기반 UUID 생성(uuid v1)
  • AUTO – 기본 옵션, RANDOM과 동일

UUID의 경우 많은 양의 저장 공간을 필요로 하고, 성능 문제가 발생할 수 있기에 UUID를 사용해야 하는 경우 TSID를 고려할 수 있을 것 같다.

참고 자료

자바 ORM 표준 JPA 프로그래밍, 김영한 p.131 ~ p.144
-Generate UUIDs as Primary Keys With Hibernate

- - +Generate UUIDs as Primary Keys With Hibernate

+ + \ No newline at end of file diff --git a/docs/linux/shell.html b/docs/linux/shell.html index 5f31acc64..df22ada6e 100644 --- a/docs/linux/shell.html +++ b/docs/linux/shell.html @@ -13,8 +13,8 @@ - - + +
@@ -23,7 +23,7 @@ ~/.bashrc 파일에 PS1에 대한 값을 설정하여 동일한 계정으로 재접속하여도 설정을 유지할 수 있다.

이스케이프 문자

이스케이프 문자를 사용하여 쉘 프롬프트에 서버에 대한 정보를 추가할 수 있다.

이스케이프 문자설명
\u사용자 이름
\h호스트 이름
\w현재 작업 디렉토리 (전체 경로)
\W현재 작업 디렉토리 (디렉토리 이름)
\d현재 날짜 (YYYY-MM-DD)
\t현재 시간 (HH:MM:SS)
\n줄 바꿈 문자
\$일반 사용자 $ root의 경우 #

색상 설정

\e[ - 색상 변경을 시작하고 싶을 때 사용한다.
색상코드m - 색상을 선택한다.
\e[0m - 색상 변경을 종료하고 싶을 때 사용한다.

빨간색 hello world → "\e[31mhello world!\e[0m"

echo -e 옵션을 사용하여 색상이 정상적으로 적용되었는지 확인할 수 있다.

echo -e "\e[31mhello world! \e[0m"

색상표

색상글자색배경색
Black3040
Red3141
Green3242
Yellow3343
Blue3444
Purple3545
Cyan3646
White3747

~/.bashrc 파일에 적용

sudo vim ~/.bashrc 을 입력하여 설정 파일을 연 후에 적용하고 싶은 문자를 PS1 환경변수에 할당하고 저장한다.

~/.bashrc
PS1="\e[32m[\t TRIPDRAW-DEV \u]\$ \e[0m"

적용은 source 명령어를 이용하면 된다.

source ~/.bashrc

참고 자료

Linux Hint

- - + + \ No newline at end of file diff --git a/docs/linux/swap.html b/docs/linux/swap.html index 5185f1c8c..d94f24084 100644 --- a/docs/linux/swap.html +++ b/docs/linux/swap.html @@ -13,8 +13,8 @@ - - + +
@@ -24,7 +24,7 @@ /etc/fstab 파일을 수정하여 Swap 메모리를 영구적으로 적용할 수 있다.

  • 해당 파일은 리눅스 부팅시 마운트정보를 저장하고 있다.
# vim을 이용하여 해당 파일을 수정한다.
sudo vim /etc/fstab
# 최하단에 다음과 같이 설정하면 된다.
/swapfile swap swap defaults 0 0

Swap 메모리 적용되었는지 확인

리눅스에서는 free 명령어를 통해 메모리를 확인할 수 있다.
-h 옵션을 주면 좀 더 읽기 편한 형태로 출력된다.

free -h
total used free shared buff/cache available
Mem: 905Mi 570Mi 65Mi 0.0Ki 270Mi 186Mi
Swap: 2.0Gi 626Mi 1.4Gi

아래에 Swap 메모리가 적용되어있는 것을 확인할 수 있다.
추가로 swapon 명령어로도 확인할 수 있다.

Swap 메모리 비활성화

swapoff 명령어를 사용하여 비활성화 한 후 파일을 삭제하면 된다.

# swap 설정한 파일 비활성화
sudo swapoff -v /swapfile
# 삭제
sudo rm /swapfile

만약 /etc/fstab에 값을 설정했을 경우 해당 값을 지워야한다.

참고 자료

How to Add Swap Space on Ubuntu 20.04, Lunuxize

- - + + \ No newline at end of file diff --git a/docs/monitoring/intro.html b/docs/monitoring/intro.html index 376d7f261..e4bac50b1 100644 --- a/docs/monitoring/intro.html +++ b/docs/monitoring/intro.html @@ -13,8 +13,8 @@ - - + +
@@ -26,8 +26,8 @@ 핀포인트, 스카우트, 와탭, 제니퍼

로그

가장 세세한 추적
같은 HTTP 요청을 묶어서 확인할 수 있는 방법이 중요하다.
MDC(Mapped Diagnostic Context) 적용

파일로 직접 로그를 남기는 경우 → 일반 로그와 에러 로그 파일을 구분해서 남겨야 한다.
-클라우드에 저장하는 경우 → 검색이 잘 되도록 구분한다.

모니터링

관찰의 경우 전체 → 좁게

알람

알람의 경우 2가지 종류(경고, 심각)로 구분해서 관리한다.

참고 자료

스프링 부트 핵심 원리와 활용, 김영한

- - +클라우드에 저장하는 경우 → 검색이 잘 되도록 구분한다.

모니터링

관찰의 경우 전체 → 좁게

알람

알람의 경우 2가지 종류(경고, 심각)로 구분해서 관리한다.

참고 자료

스프링 부트 핵심 원리와 활용, 김영한

+ + \ No newline at end of file diff --git a/docs/network/load-balancing-algorithm.html b/docs/network/load-balancing-algorithm.html index be4191842..923d2c37b 100644 --- a/docs/network/load-balancing-algorithm.html +++ b/docs/network/load-balancing-algorithm.html @@ -13,8 +13,8 @@ - - + +
@@ -22,8 +22,8 @@ Hashing key는 클라이언트의 IP + port 혹은 IP 주소만으로 결정
사용자가 항상 동일한 서버로 연결되는 것을 보장

최소 응답 시간 방식(Least Response Time Method)

서버의 현재 연결 상태, 응답 시간 고려하여 트래픽을 전송
가장 적은 연결 상태와 가장 짧은 응답 시간을 보이는 서버에 우선적으로 로드를 배분하는 방식

참고 자료

load balancing, AWS
-로드 밸런싱에 대해 알아보자, 테코블

- - +로드 밸런싱에 대해 알아보자, 테코블

+ + \ No newline at end of file diff --git a/docs/network/load-balancing.html b/docs/network/load-balancing.html index 456f6a53a..4b2fcad5b 100644 --- a/docs/network/load-balancing.html +++ b/docs/network/load-balancing.html @@ -13,8 +13,8 @@ - - + +
@@ -25,8 +25,8 @@ 트래픽이 증가하는 경우 AutoScaling을 통해 서버를 자동으로 확장(Scale-out)
트래픽이 감소하는 경우 축소(Scale-in)

보안

분산 서비스 거부 공격(DDoS)이 발생하는 경우 해당 트래픽을 부하 분산할 수 있다.

성능

서버 간 로드를 균등하게 배포하여 애플리케이션의 성능을 향상 시킨다.
클라이언트 요청을 지리적으로 더 가까운 서버로 리다이렉션하여 지연 시간을 단축시킨다.

내결함성(Fault Tolerance)과 확장성(Scalability)

내결함성: 시스템의 일부 구성 요소가 작동하지 않더라도 계속 작동할 수 있는 기능
-확장성: 대규모적인 재설계/재설치 등의 필요없이 확장이 얼마나 쉽고 가능한가에 대한 용이성

참고 자료

load balancing, AWS

- - +확장성: 대규모적인 재설계/재설치 등의 필요없이 확장이 얼마나 쉽고 가능한가에 대한 용이성

참고 자료

load balancing, AWS

+ + \ No newline at end of file diff --git a/docs/nginx/command.html b/docs/nginx/command.html index 2a960a2d1..16dd0472d 100644 --- a/docs/nginx/command.html +++ b/docs/nginx/command.html @@ -13,8 +13,8 @@ - - + +
@@ -24,8 +24,8 @@ conf.d 디렉터리 대신 site-enabled 디렉터리와 symlink를 통해 설정 파일을 연결하는 방법은 더 이상 사용하지 않는다.

NIGNX 설정

nginx 설정의 경우 include 구문을 활용해 구조화하여 설정 파일을 간결하게 유지하는 것이 좋다.

/var/log/nginx/

엔진엑스 로그가 저장되는 디렉터리로 access 로그와 error 로그를 확인할 수 있다.
로그 형식의 경우 설정 파일의 log_format 구문을 이용해서 변경할 수 있다.

명령어

nginx -t

nginx 설정이 정상인지 확인한다.

nginx -T

nginx 설정 확인의 결과를 조금 더 자세하게 출력해준다.

nginx -s <SIGNAL>

여기서 SIGNAL은 다음 중 하나를 선택할 수 있다.

  • quit: 정상적으로 종료(SIGQUIT)
  • reload: 설정 파일 리로드(SIGHUP)
  • reopen: 로그 파일을 다시 열도록 요청(SIGUSR1)
  • stop: 종료 요청(SIGTERM)

여기서 SIGQUIT & SIGTREM 모두 graceful shutdown을 수행한다.

참고 자료

NGINX 쿡북, 데릭 디용기 p.22 ~ p.23
https://docs.nginx.com/
-https://docs.nginx.com/nginx/admin-guide/monitoring/logging/

- - +https://docs.nginx.com/nginx/admin-guide/monitoring/logging/

+ + \ No newline at end of file diff --git a/docs/nginx/static-file.html b/docs/nginx/static-file.html index 9b0626074..733b21385 100644 --- a/docs/nginx/static-file.html +++ b/docs/nginx/static-file.html @@ -13,15 +13,15 @@ - - + +

정적 컨텐츠 제공

root

클라이언트에게 파일을 제공할 때 사용되는 경로를 지정하는 데 사용한다.
root의 경우 locaiton으로 넘어온 경로를 root 경로 뒤에 추가한다.

root
# localhost/images/1.png 호출 /var/www/images/images/1.png 검색
location /images/ {
root /var/www/images;
}

alias

location으로 매칭된 부분을 제거한다.

alias
# localhost/images/1.png 호출 /var/www/images/1.png 검색
location /images/ {
alias /var/www/images;
}

try_files

try_files 디렉티브를 이용해서 파일이 존재하지 않으면 적절한 값을 반환할 수 있다.
-설정하지 않으면 기본으로 404를 반환한다.

location /images/ {
alias /var/www/images;
try_files $uri $uri/ =404;
}

다음과 같이 proxy 설정으로도 구성할 수도 있다.

location /images/ {
root /root;
try_files $uri $uri/ default-image;
}

location default-image {
proxy_pass http://localhost/images/default_image.jpg;
}

참고 자료

Serving Static Content

- - +설정하지 않으면 기본으로 404를 반환한다.

location /images/ {
alias /var/www/images;
try_files $uri $uri/ =404;
}

다음과 같이 proxy 설정으로도 구성할 수도 있다.

location /images/ {
root /root;
try_files $uri $uri/ default-image;
}

location default-image {
proxy_pass http://localhost/images/default_image.jpg;
}

참고 자료

Serving Static Content

+ + \ No newline at end of file diff --git a/docs/performance/throughput-latency.html b/docs/performance/throughput-latency.html index 2e20dc2b0..2b37d994d 100644 --- a/docs/performance/throughput-latency.html +++ b/docs/performance/throughput-latency.html @@ -13,8 +13,8 @@ - - + +
@@ -26,8 +26,8 @@ Latency는 다음과 같이 두 가지로 구분할 수 있다.

사용자가 본 처리 시간: 사용자가 요청을 보내고 응답을 받을 때까지의 시간
시스템에서 본 처리 시간: 시스템이 요청을 받고 응답을 보낼 때까지의 시간

사용자가 본 처리 시간의 경우 네트워크에 대한 시간이 포함된다.
따라서 성능 측정과 개선은 시스템에서 본 처리 시간으로 측정하고 개선하는 것이 더 정확해 보인다.

다음과 같은 시스템이 있다고 가정해보자.

  • 시스템 A: 요청시 평균 200ms 내 응답
  • 시스템 B: 요청시 평균 100ms 내 응답

시스템 B가 요청에 대한 응답이 더 빠르니, 시스템 B의 성능이 더 좋다고 할 수 있다.

참고 자료

아마존 웹 서비스 부하 테스트 입문 - 나카가와 타루하치, 모리시타 켄
-difference between throughput and latency, AWS - 해당 내용은 네트워크 기준이다.

- - +difference between throughput and latency, AWS - 해당 내용은 네트워크 기준이다.

+ + \ No newline at end of file diff --git a/docs/performance/throughput.html b/docs/performance/throughput.html index d8edb5471..ea47abf2b 100644 --- a/docs/performance/throughput.html +++ b/docs/performance/throughput.html @@ -13,8 +13,8 @@ - - + +
@@ -23,8 +23,8 @@ 최대 rps: 1일 평균 rps x 최대 피크 때의 비율(평균 접속 수 대비)

최대 rps * 안전계수(2 ~ 3배)를 Throughput의 목표치로 한다.

계산 예시

DAU: 10만명
1명당 평균 접속수: 10회
평균 접속 수 대비 최대 피크 비율: 4배
-안전계수: 3배

100000 x 10 / 86400 x 4 x 3 -> 약 138rps

참고 자료

아마존 웹 서비스 부하 테스트 입문 - 나카가와 타루하치, 모리시타 켄

- - +안전계수: 3배

100000 x 10 / 86400 x 4 x 3 -> 약 138rps

참고 자료

아마존 웹 서비스 부하 테스트 입문 - 나카가와 타루하치, 모리시타 켄

+ + \ No newline at end of file diff --git a/docs/performance/types.html b/docs/performance/types.html index cdd047b5c..cad8a0873 100644 --- a/docs/performance/types.html +++ b/docs/performance/types.html @@ -13,13 +13,13 @@ - - + +
-

성능 테스트 유형

테스트설명
지연 테스트(latency test)요청에 대한 응답 시간을 측정
처리율 테스트(throughput test)시스템 성능이 급락하기 직전, 최대 처리율 수치를 측정
부하 테스트(load test)비즈니스 이벤트를 대비해 트래픽을 견딜 수 있는지 확인
스트레스 테스트(stress test)시스템의 한계점을 확인
내구 테스트(endurance test)장시간 실행할 경우 성능 이상이 발생하는지 확인
용량 계획 테스트(capacity planning test)리소스를 추가한 만큼 시스템이 확장되는지 확인
저하 테스트(degradation test)시스템이 부분적으로 실패할 경우 어떤 일이 발생하는지 확인(장애 복구 및 회복 등)

참고 자료

자바 최적화 - 벤저민 J. 에번스, 제임스 고프, 크리스 뉴랜드

- - +

성능 테스트 유형

테스트설명
지연 테스트(latency test)요청에 대한 응답 시간을 측정
처리율 테스트(throughput test)시스템 성능이 급락하기 직전, 최대 처리율 수치를 측정
부하 테스트(load test)비즈니스 이벤트를 대비해 트래픽을 견딜 수 있는지 확인
스트레스 테스트(stress test)시스템의 한계점을 확인
내구 테스트(endurance test)장시간 실행할 경우 성능 이상이 발생하는지 확인
용량 계획 테스트(capacity planning test)리소스를 추가한 만큼 시스템이 확장되는지 확인
저하 테스트(degradation test)시스템이 부분적으로 실패할 경우 어떤 일이 발생하는지 확인(장애 복구 및 회복 등)

참고 자료

자바 최적화 - 벤저민 J. 에번스, 제임스 고프, 크리스 뉴랜드

+ + \ No newline at end of file diff --git a/docs/tags.html b/docs/tags.html index 286a8ba5b..a57f77a79 100644 --- a/docs/tags.html +++ b/docs/tags.html @@ -13,13 +13,13 @@ - - + + - - + + \ No newline at end of file diff --git a/docs/tags/book.html b/docs/tags/book.html index eb15416d6..c17cadce3 100644 --- a/docs/tags/book.html +++ b/docs/tags/book.html @@ -13,13 +13,13 @@ - - + +

1개 문서가 "book" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/etc.html b/docs/tags/etc.html index b60280a58..27ecb9f6c 100644 --- a/docs/tags/etc.html +++ b/docs/tags/etc.html @@ -13,13 +13,13 @@ - - + +

4개 문서가 "etc" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/jpa.html b/docs/tags/jpa.html index 4053af132..3c5e1a4cb 100644 --- a/docs/tags/jpa.html +++ b/docs/tags/jpa.html @@ -13,13 +13,13 @@ - - + +

1개 문서가 "JPA" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/latency.html b/docs/tags/latency.html index e303ea475..e3310e819 100644 --- a/docs/tags/latency.html +++ b/docs/tags/latency.html @@ -13,13 +13,13 @@ - - + +

1개 문서가 "latency" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/load-balancing.html b/docs/tags/load-balancing.html index 25469beda..23fcc5d2f 100644 --- a/docs/tags/load-balancing.html +++ b/docs/tags/load-balancing.html @@ -13,13 +13,13 @@ - - + +

2개 문서가 "load balancing" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/monitoring.html b/docs/tags/monitoring.html index 537623e03..f5c73b2a0 100644 --- a/docs/tags/monitoring.html +++ b/docs/tags/monitoring.html @@ -13,13 +13,13 @@ - - + +

1개 문서가 "monitoring" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/network.html b/docs/tags/network.html index 7646a2be9..2e7fc3434 100644 --- a/docs/tags/network.html +++ b/docs/tags/network.html @@ -13,13 +13,13 @@ - - + +

2개 문서가 "network" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/nginx.html b/docs/tags/nginx.html index 4b3bf74eb..66cbd86ff 100644 --- a/docs/tags/nginx.html +++ b/docs/tags/nginx.html @@ -13,13 +13,13 @@ - - + +

2개 문서가 "nginx" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/package.html b/docs/tags/package.html index fc2e03c83..e9475ee9b 100644 --- a/docs/tags/package.html +++ b/docs/tags/package.html @@ -13,13 +13,13 @@ - - + +

1개 문서가 "package" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/performance.html b/docs/tags/performance.html index 098563478..69652a523 100644 --- a/docs/tags/performance.html +++ b/docs/tags/performance.html @@ -13,13 +13,13 @@ - - + +

1개 문서가 "performance" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/postmortem.html b/docs/tags/postmortem.html index 90772edb9..42257a729 100644 --- a/docs/tags/postmortem.html +++ b/docs/tags/postmortem.html @@ -13,13 +13,13 @@ - - + +

1개 문서가 "postmortem" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/test.html b/docs/tags/test.html index 19c1e4c30..7012e7df9 100644 --- a/docs/tags/test.html +++ b/docs/tags/test.html @@ -13,13 +13,13 @@ - - + +

5개 문서가 "test" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/tags/throughput.html b/docs/tags/throughput.html index 0a8b532fe..89fd8fdaf 100644 --- a/docs/tags/throughput.html +++ b/docs/tags/throughput.html @@ -13,13 +13,13 @@ - - + +

2개 문서가 "throughput" 태그에 분류되었습니다

모든 태그 보기
- - + + \ No newline at end of file diff --git a/docs/test/benefit.html b/docs/test/benefit.html index addeeb6cd..3d2cd311b 100644 --- a/docs/test/benefit.html +++ b/docs/test/benefit.html @@ -13,8 +13,8 @@ - - + +
@@ -24,8 +24,8 @@ 리팩터링할 때 자신감을 가지고 변경 사항을 반영할 수 있다.

더 나은 문서 자료

하나의 행위만 집중해 검증하는 테스트는 실행 가능한 문서와 같다.
이 때 테스트는 명확하고 간결해야지만 문서 자료로서의 역할을 훌륭히 수행할 수 있다.

더 단순한 리뷰

정확성, 극단 상황, 오류 상황 등의 다양한 측면에서 코드를 검사해주는 테스트가 준비되어 있다면 리뷰어가 검증하는 시간을 크게 줄여준다.

사려 깊은 설계

새로 작성한 코드의 테스트를 작성하는 일은 실질적으로 해당 코드의 API가 잘 설계되어 있는지를 시험하는 행위다.
테스트하기 어려운 코드는 너무 많은 책임을 가지고 있거나, 의존성이 복잡한 경우가 많다.
-잘 설계된 코드라면 모듈화가 잘 되어있어야 한다.

고품질의 릴리스를 빠르게

자동화된 테스트를 갖춘다면 새로운 버전을 릴리스할 때 불안에 떨지 않아도 된다.

참고 자료

구글 엔지니어는 이렇게 일한다, 타이터스 윈터스, 톰 맨쉬렉, 하이럼 라이트 p.288

- - +잘 설계된 코드라면 모듈화가 잘 되어있어야 한다.

고품질의 릴리스를 빠르게

자동화된 테스트를 갖춘다면 새로운 버전을 릴리스할 때 불안에 떨지 않아도 된다.

참고 자료

구글 엔지니어는 이렇게 일한다, 타이터스 윈터스, 톰 맨쉬렉, 하이럼 라이트 p.288

+ + \ No newline at end of file diff --git a/docs/test/first.html b/docs/test/first.html index 3abaed9cf..f783e06e2 100644 --- a/docs/test/first.html +++ b/docs/test/first.html @@ -13,8 +13,8 @@ - - + +
@@ -27,8 +27,8 @@ JUnit과 같은 자동화된 테스트 도구를 사용해야 한다.

Timely(적시에)

테스트는 적시에 작성해야 한다.
단위 테스트는 테스트 하려는 실제 코드를 구현하기 직전에 구현해야 한다.(TDD)
실제 코드를 구현한 다음에 테스트 코드를 작성한다면, 테스트 하기 어려운 코드를 작성했다는 것을 뒤늦게 발견할 수 있다.

참고 자료

A clean test, TDD Manifasto
-Clean Code 9장 단위테스트, 로버트 C. 마틴

- - +Clean Code 9장 단위테스트, 로버트 C. 마틴

+ + \ No newline at end of file diff --git a/docs/test/heuristics.html b/docs/test/heuristics.html index bd1c54f72..16e3ccd71 100644 --- a/docs/test/heuristics.html +++ b/docs/test/heuristics.html @@ -13,13 +13,13 @@ - - + +
-

TDD heuristics

TDD heuristics

  1. 여러분이 작성하고 싶은 코드를 작성하도록 만드는 테스트를 작성하라.
  2. 실패시켜라. 통과시켜라. 그리고 정리하라.
  3. 최상의 결과를 추구하지 말라.
  4. 실패하는 가장 간단하고, 가장 구체적이며, 가장 퇴화한 테스트를 작성하라.
  5. 가능하면 일반화하라.
  6. 코드가 틀렸다고 느껴지면 잠시 멈춰서 설계를 고쳐라.
  7. 더 복잡한 다음 경우로 넘어가기 전, 지금 다루고 있는 더 단순한 경우를 모조리 테스트하라.
  8. 현재 테스트를 통과시키기 위해 너무 많은 구현을 해야 한다면, 테스트를 지우고 더 쉽게 통과할 수 있는 더 단순한 테스트를 작성하라.
  9. 테스트 공간(test space)을 전부 포괄하는 신중하고 점진적인 패턴을 따르라.
  10. 필요 없는 것을 여러분의 테스트에 넣지 말라.
  11. 테스트에 실제 서비스 데이터를 사용하지 말라.
  12. 테스트 구조를 제품 코드 구조로부터 분리하라.
  13. 테스트가 구체적(specific)이 될수록 코드는 일반적(generic)이 된다.
  14. 변환을 적용한 결과 최적이 아닌 해답에 도달했다면 다른 변환을 시도해보라.
  15. 디버거 사용을 피하라

참고 자료

소프트웨어 장인 정신 이야기, 로버트 C. 마틴 p.44 ~ p.209

- - +

TDD heuristics

TDD heuristics

  1. 여러분이 작성하고 싶은 코드를 작성하도록 만드는 테스트를 작성하라.
  2. 실패시켜라. 통과시켜라. 그리고 정리하라.
  3. 최상의 결과를 추구하지 말라.
  4. 실패하는 가장 간단하고, 가장 구체적이며, 가장 퇴화한 테스트를 작성하라.
  5. 가능하면 일반화하라.
  6. 코드가 틀렸다고 느껴지면 잠시 멈춰서 설계를 고쳐라.
  7. 더 복잡한 다음 경우로 넘어가기 전, 지금 다루고 있는 더 단순한 경우를 모조리 테스트하라.
  8. 현재 테스트를 통과시키기 위해 너무 많은 구현을 해야 한다면, 테스트를 지우고 더 쉽게 통과할 수 있는 더 단순한 테스트를 작성하라.
  9. 테스트 공간(test space)을 전부 포괄하는 신중하고 점진적인 패턴을 따르라.
  10. 필요 없는 것을 여러분의 테스트에 넣지 말라.
  11. 테스트에 실제 서비스 데이터를 사용하지 말라.
  12. 테스트 구조를 제품 코드 구조로부터 분리하라.
  13. 테스트가 구체적(specific)이 될수록 코드는 일반적(generic)이 된다.
  14. 변환을 적용한 결과 최적이 아닌 해답에 도달했다면 다른 변환을 시도해보라.
  15. 디버거 사용을 피하라

참고 자료

소프트웨어 장인 정신 이야기, 로버트 C. 마틴 p.44 ~ p.209

+ + \ No newline at end of file diff --git a/docs/test/stairstep.html b/docs/test/stairstep.html index 82cf6f95f..831ecbc6d 100644 --- a/docs/test/stairstep.html +++ b/docs/test/stairstep.html @@ -13,15 +13,15 @@ - - + +

계단 테스트

계단 테스트(Stairstep Test)

추후에 필요로 할 클래스, 함수, 다른 구조를 만들도록 강제하기 위해 작성하는 테스트
아무런 단정문이 없을 수도 있고, 기능이 조금 더 구현된다면 제거하고 포괄적인 테스트로 대신할 수 있다.
-복잡도를 필요한 수준까지 점진적으로 증가시킬 수 있게 도와주는 계단 역할을 하기 때문에 계단 테스트라고 부른다.

참고 자료

소프트웨어 장인 정신 이야기, 로버트 C. 마틴 p.74

- - +복잡도를 필요한 수준까지 점진적으로 증가시킬 수 있게 도와주는 계단 역할을 하기 때문에 계단 테스트라고 부른다.

참고 자료

소프트웨어 장인 정신 이야기, 로버트 C. 마틴 p.74

+ + \ No newline at end of file diff --git a/docusaurus.html b/docusaurus.html index d219ccdd7..470aaeb99 100644 --- a/docusaurus.html +++ b/docusaurus.html @@ -13,12 +13,12 @@ - - + +
-

Docusaurus

· 약 11분

팀 블로그 또는 문서화를 위해 Docusaurus를 사용하는 방법을 정리하려고 한다.

설치

공식 홈페이지에 들어가서 최신 버전을 설치한다.

yarn create docusaurus

배포

배포 안내 문서
+

Docusaurus

· 약 11분

팀 블로그 또는 문서화를 위해 Docusaurus를 사용하는 방법을 정리하려고 한다.

설치

공식 홈페이지에 들어가서 최신 버전을 설치한다.

yarn create docusaurus

배포

배포 안내 문서
netlify나 vercel 같은 서버리스 플랫폼을 추천하고 있고, 간단하고, 빠른 시간 안에 배포를 할 수 있다.
이 글에서는 github pages를 이용해서 배포하는 방법을 설명한다.

레포지토리 생성

github pages를 이용하려면 예시와 같이 username.github.io 형태의 레포지토리를 생성해야 한다.
이때 organization을 사용하는 경우 organization.github.io 형태의 레포지토리를 생성해서 사용한다.

설정 파일 수정

docusaurus.config
module.exports = {
// ...
url: 'https://greeng00se.github.io',
baseUrl: '/',
projectName: 'greeng00se.github.io',
organizationName: 'greeng00se',
trailingSlash: false,
// ...
};

토큰 설정

github action을 위해 배포용 토큰을 하나 생성하여 생성한 레포지토리에 Repository secrets으로 설정한다.
@@ -39,7 +39,7 @@ jq가 설치되어 있지 않으면 mac 기준 brew를 이용해서 설치할 수 있다.

brew install jq

다음 명령어를 이용하여 .env와 config.json을 이용하여 크롤링을 한다.

docker run -it --env-file=.env -e "CONFIG=$(cat ./config.json | jq -r tostring)" algolia/docsearch-scraper

docusaurus 설정

전에 확인한 APP ID, Search-Only API KEY, IndexName을 이용하여 docusaurus.config 파일에 설정한다.

docusaurus.config
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
...
algolia: {
appId: 'MVIU5UEMOM', // Application ID
apiKey: 'b68f378013817d9a190df88cdde226a0', // Search-Only API Key
indexName: 'teco', // config.json에 설정한 인덱스명
contextualSearch: true,
},
})

부가 설정

화면 상단 Github Icon

파일 최하단에 아래 css 구문을 추가한다.

/src/css/custom.css
.header-github-link:hover {
opacity: 0.6;
}

.header-github-link:before {
content: '';
width: 24px;
height: 24px;
display: flex;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}

html[data-theme='dark'] .header-github-link:before {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}

themeconfig -> navbar에 github link를 설정한다.

docusaurus.config
navbar: {
title: 'HELLO',
items: [
{
href: 'https://github.com/greeng00se',
position: 'right',
className: 'header-github-link',
'aria-label': 'GitHub repository',
},
],
},

코드블럭

java나 kotlin의 경우 기본적으로 하이라이팅을 지원해 주지 않는다.
prism 설정을 아래와 같이 변경해 준다.

docusaurus.config
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
additionalLanguages: ['java', 'kotlin'],
}

mermaid

mermaid를 사용하려면 @docusaurus/theme-mermaid 를 설치해야 한다.

yarn add @docusaurus/theme-mermaid

설치 후 아래와 같이 설정을 추가한다.

docusaurus.config
const config = {
...
markdown: {
mermaid: true,
},
themes: [
'@docusaurus/theme-mermaid'
],
};

themeConfig에서 mermaid의 테마를 지정할 수 있다.

docusaurus.config
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
...
mermaid: {
theme: {
light: 'neutral',
dark: 'dark'
},
},
}),

국제화 설정

국제화 설정을 한다면 Older Entries 형태의 설명이 다음 페이지 로 변경된다.
설정파일에서 i18n에 있는 로케일 설정을 ko로 변경하면 된다.

docusaurus.config
i18n: {
defaultLocale: "ko",
locales: ["ko"],
},

블로그 글 author

팀원 별로 문서를 관리한다면 다음과 같이 어떤 팀원이 글을 작성했는지 설정해야 한다.

author

authors.yml 파일을 이용하여 사용자에 대한 기본 설정을 할 수 있다.

/blog/authors.yml
herb:
name: 허브
title: Backend
url: https://github.com/greeng00se
image_url: https://github.com/greeng00se.png

mallang:
name: 말랑
title: Backend
url: https://github.com/shin-mallang
image_url: https://github.com/shin-mallang.png

블로그 글을 작성할 때 다음과 같이 authors에 넣어주기만 하면 된다.

---
slug: 1
title: Hello World
authors: [herb, mallang]
tags: [hello, docusaurus]
---

첫 번째 문서 내용
- - + + \ No newline at end of file diff --git a/grasp.html b/grasp.html index a836dcaa2..aa85bc3ed 100644 --- a/grasp.html +++ b/grasp.html @@ -13,15 +13,15 @@ - - + +
-

일반적인 책임 할당을 위한 패턴

· 약 9분

GRASP(General Responsibility Assignment Software Pattern)

크레이그 라만의 Applying UML and Patterns이라는 책에서 나온 책임 할당을 위한 패턴

각 패턴마다 Solution과 Problem로 구성되어 있다.

정보 전문가 패턴(Information Expert)

Q: 객체에 책임을 할당하는 기본 원칙은 무엇인가?

A: 책임을 수행하는 데 필요한 정보를 가진 클래스(정보 전문가)에게 책임을 할당한다.

정보와 행동을 가까운 곳에 위치시키기 때문에 캡슐화를 유지할 수 있다.

필요한 정보를 가진 객체들로 책임이 분산된다.

창조자 패턴(Creator)

Q: 누가 객체 A를 생성하는가?

A: 다음의 조건을 최대한 많이 만족하는 객체에게 객체 생성 책임을 할당해야 한다.

  • B가 A 객체를 포함 또는 참조한다.
  • B가 A 객체를 기록한다.
  • B가 A 객체를 긴밀하게 사용한다.
  • B가 A 객체의 초기값을 가지고 있다.

생성 예정인 객체와 연관되어 있는 객체가 생성 책임을 가지고 있게 된다면, 이미 해당 객체와 결합되어있다고 생각할 수 있다. 따라서 전체적인 결합도를 낮게 유지할 수 있다.

낮은 결합도 패턴(Low Coupling)

Q: 의존성을 낮추고 변화의 영향을 줄이며 재사용성을 증가시키는 방법은?

A: 전체적인 결합이 낮게 유지되도록 책임을 할당해야 한다.

결합도(Coupling) +

일반적인 책임 할당을 위한 패턴

· 약 9분

GRASP(General Responsibility Assignment Software Pattern)

크레이그 라만의 Applying UML and Patterns이라는 책에서 나온 책임 할당을 위한 패턴

각 패턴마다 Solution과 Problem로 구성되어 있다.

정보 전문가 패턴(Information Expert)

Q: 객체에 책임을 할당하는 기본 원칙은 무엇인가?

A: 책임을 수행하는 데 필요한 정보를 가진 클래스(정보 전문가)에게 책임을 할당한다.

정보와 행동을 가까운 곳에 위치시키기 때문에 캡슐화를 유지할 수 있다.

필요한 정보를 가진 객체들로 책임이 분산된다.

창조자 패턴(Creator)

Q: 누가 객체 A를 생성하는가?

A: 다음의 조건을 최대한 많이 만족하는 객체에게 객체 생성 책임을 할당해야 한다.

  • B가 A 객체를 포함 또는 참조한다.
  • B가 A 객체를 기록한다.
  • B가 A 객체를 긴밀하게 사용한다.
  • B가 A 객체의 초기값을 가지고 있다.

생성 예정인 객체와 연관되어 있는 객체가 생성 책임을 가지고 있게 된다면, 이미 해당 객체와 결합되어있다고 생각할 수 있다. 따라서 전체적인 결합도를 낮게 유지할 수 있다.

낮은 결합도 패턴(Low Coupling)

Q: 의존성을 낮추고 변화의 영향을 줄이며 재사용성을 증가시키는 방법은?

A: 전체적인 결합이 낮게 유지되도록 책임을 할당해야 한다.

결합도(Coupling) 객체 사이의 의존성이 과한 경우 결합도가 높다고 말한다.

  • 오브젝트 p.17

결합도를 낮춘다면 다음과 같은 이점이 있다.

  • 다른 구성 요소의 변화에 영향을 받지 않는다.
  • 재사용이 편리해진다.
  • 해당 클래스에 대한 이해가 쉬워진다. (의존하는 클래스가 적기 때문에)

높은 응집도 패턴(High Cohesion)

Q. 객체를 관리하기 쉽게 하려면 어떻게 해야 할까?

A. 높은 응집도를 유지할 수 있게 책임을 할당해야 한다.

응집도(Cohesion) 연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에게 위임하는 객체를 가리켜 응집도가 높다고 말한다.

  • 오브젝트 p.26

변경의 이유에 따라 클래스를 분리한다면 응집도를 높일 수 있고, 응집도가 높아진다면 다음과 같은 이점이 있다.

  • 해당 클래스에 대한 이해가 쉬워진다. (할당된 책임만을 수행하고 있기 때문에)
  • 유지보수가 쉬워진다.
  • 낮은 결합도 또한 지원한다.
  • 응집도가 높은 클래스는 특정한 목적에 사용할 수 있기 때문에 재사용하기 좋다.

컨트롤러 패턴(Controller)

Q. 사용자의 요청을 처리하는 것은 누가 담당해야 하는가?

A. 사용자의 요청을 처리하는 Controller 객체를 만들어서 사용해야 한다.

어떤 서브시스템이 존재한다고 가정할 때

  • 직접적으로 객체에 접근하여 프로그램을 사용한다면 결합도가 상승한다.
  • 서브 시스템에 들어오는 요청을 처리해주는 컨트롤러가 있다면 사용하는 입장에서는 해당 컨트롤러만 알면 된다.
  • 만약 서브 시스템의 변경이 생겼을 때 외부에 미치는 영향도 줄어든다.

다형성 패턴(Polymorphism)

Q. 객체의 타입에 따라 행동이 바뀐다면 책임을 어떻게 할당해야 할까?

A. OOP가 지원하는 다형성을 적극적으로 활용한다. (인터페이스를 두고 행동에 대한 부분을 구현)

객체의 종류에 따라 분기하는 조건문이 아닌 다형성을 사용하는 것이 좋은 방법이다.

새로운 타입이 추가되었을 때 조건문을 사용한다면 기존의 조건문을 수정해야 하지만 다형성을 활용하면 쉽게 확장할 수 있다.

변경 보호 패턴(Protected Variations)

Q. 어떻게 하면 변경이 다른 요소에 영향을 미치지 않도록 방지할 수 있을까?

A. 변화가 예상되는 지점을 식별하고, 주위에 안정된 인터페이스를 형성하도록 책임을 할당해야 한다.

간접 참조 패턴(Indirection)

Q. 두 객체 사이의 직접적인 연결을 피하고 싶다면 어떻게 해야 할까?

A. 두 객체 사이에 또 다른 객체를 두어 직접적인 연결을 피할 수 있다.

중재자 패턴을 사용하여 두 객체 사이에 또 하나의 객체를 추가하여 복잡한 관계를 단순화할 수 있다.

중간에 인터페이스를 둔다면 변경 보호 패턴(Protected Variations)에 해당된다.

순수한 가공물 패턴(Pure Fabrication)

Q. 책임을 할당한 도메인 객체가 Low Coupling, High Cohesion, 재사용성 등의 목적을 위반한다면 어떻게 해야 할까?

A. 도메인 개념을 포함하지 않는 클래스를 하나 만들고 매우 응집된 책임을 할당할 수 있다.

행동을 추가할 때, 해당 책임을 수행할 도메인 개념이 존재하지 않는다면 도메인과 무관한 인공적인 객체를 만든다음 해당 객체에게 책임을 할당한다.

객체가 데이터베이스에 저장해야 할 값을 가지고 있다고, 정보 전문가 패턴을 적용하여 데이터베이스에 저장하라는 책임을 가지라고 하지 않는다.

예) 상점과 고객 클래스가 있고 서로 다른 통화를 사용하고 있다고 가정

  • 서로 다른 통화를 사용하고 있기 때문에 거래를 하려면 환전을 해야한다.
  • 두 클래스 다 환전에 대한 책임을 부여하기 애매하다면 환전을 책임하는 클래스를 추가하고 사용할 수 있다.

참고 자료

오브젝트 5장. 책임 할당하기, 조영호

Applying UML and Patterns Chapter 16, Chapter 21 GRASP, Craig Larman

GRASP, 한빛 네트워크

- - + + \ No newline at end of file diff --git a/index.html b/index.html index 71d9464f1..2035c7e6e 100644 --- a/index.html +++ b/index.html @@ -13,13 +13,13 @@ - - + +
- - + + \ No newline at end of file diff --git a/innodb-lock.html b/innodb-lock.html index 1b6a260ad..89b7ff256 100644 --- a/innodb-lock.html +++ b/innodb-lock.html @@ -13,12 +13,12 @@ - - + +
-

InnoDB 스토리지 엔진의 잠금

· 약 6분

InnoDB 스토리지 엔진의 잠금

MySQL에서 제공하는 잠금과 별개로 스토리지 엔진 내부에서 로우 단위의 잠금을 지원한다.
+

InnoDB 스토리지 엔진의 잠금

· 약 6분

InnoDB 스토리지 엔진의 잠금

MySQL에서 제공하는 잠금과 별개로 스토리지 엔진 내부에서 로우 단위의 잠금을 지원한다.
보통 명시적으로 잠금을 사용하는 경우는 드물고, 격리 수준에 따라 묵시적으로 잠금이 사용된다.

동시성 제어 방식에는 낙관적인 방식과 비관적인 방식이 있다.
InnoDB는 기본적으로 MVCC(다중 버전 동시성 제어)를 통해 낙관적인 방식을 사용하고 락을 통해 특정 상황에서 비관적인 방식을 사용한다.

낙관적 동시성 제어(OCC, Optimistic concurrency control)

트랜잭션이 서로 충돌하지 않는다고 가정하는 방식

비관적 동시성 제어(PCC, Pessimistic Concurrency Control)

트랜잭션이 충돌하는 가정하에 잠금을 거는 방식
일반적으로 Shared Lock, Exclusive Lock을 통해 이를 구현한다.

Shared & Exclusive Locks

InnoDB는 로우 단위의 잠금을 수행할 때 공유 잠금과 배타적 잠금을 사용한다.

공유 잠금(S, shared lock)

데이터 조회를 위한 락, 읽기 잠금(read lock)으로도 불린다.
@@ -38,7 +38,7 @@ MySQL Innodb Locks, cecil1018
MySQL 8.0 InnoDB Locks, MySQL
Locks Set by Different SQL Statements in InnoDB, MySQL

- - + + \ No newline at end of file diff --git a/intellij-settings.html b/intellij-settings.html index 8b0ea05c1..1e5346ef9 100644 --- a/intellij-settings.html +++ b/intellij-settings.html @@ -13,13 +13,13 @@ - - + +
-

IntelliJ 설정

· 약 1분

Import 자동 적용

Prefrences > Editor > General > Auto Import > Add unambiguous imports on the fly

auto-import

저장시 동작

Prefrences > Tools > Actions on Save

actions-on-save

Reformat Code: Code Reformmating

Optimize imports: 사용하지 않는 Import 제거

Rearrange: Code Style > Arrangement 설정 기반 코드 재정렬

메소드 추출, 변수 추출시 final 적용

Prefrences > Editor > Code Style > Java > Code Generation > Final Modifier

final-modifier

- - +

IntelliJ 설정

· 약 1분

Import 자동 적용

Prefrences > Editor > General > Auto Import > Add unambiguous imports on the fly

auto-import

저장시 동작

Prefrences > Tools > Actions on Save

actions-on-save

Reformat Code: Code Reformmating

Optimize imports: 사용하지 않는 Import 제거

Rearrange: Code Style > Arrangement 설정 기반 코드 재정렬

메소드 추출, 변수 추출시 final 적용

Prefrences > Editor > Code Style > Java > Code Generation > Final Modifier

final-modifier

+ + \ No newline at end of file diff --git a/java-class-file.html b/java-class-file.html index b814c5811..40b6355ea 100644 --- a/java-class-file.html +++ b/java-class-file.html @@ -13,12 +13,12 @@ - - + +
-

자바 클래스 파일 구조

· 약 6분

클래스 파일

자바 소스코드가 실행이 되려면 자바 컴파일러(javac)를 통해 소스코드를 클래스파일로 변환해야 한다.
+

자바 클래스 파일 구조

· 약 6분

클래스 파일

자바 소스코드가 실행이 되려면 자바 컴파일러(javac)를 통해 소스코드를 클래스파일로 변환해야 한다.
컴파일된 클래스파일은 어떤 구조로 되어있을까?

클래스 파일의 데이터 형식

8비트 바이트의 스트림으로 구성된다.
16비트 및 32비트의 데이터는 각각 2개, 4개의 연속된 8비트를 읽어서 구성된다.
멀티바이트의 경우 항상 big endian 순서로 저장된다.

u1 → unsigned 1byte
@@ -36,7 +36,7 @@ Class file in Java, File Format
java se11 Class 파일 형식, Oracle
java se17 Class 파일 형식, Oracle

- - + + \ No newline at end of file diff --git a/java-spring-springboot.html b/java-spring-springboot.html index 0be460864..e76fe2a37 100644 --- a/java-spring-springboot.html +++ b/java-spring-springboot.html @@ -13,12 +13,12 @@ - - + +
-

자바 17, 스프링 6.0, 스프링 부트 3.1

· 약 5분

자바 17, 스프링 6.0, 스프링 부트 3.1

팀 프로젝트를 진행하면서 스프링 부트 3.1을 사용하게 되었다.
+

자바 17, 스프링 6.0, 스프링 부트 3.1

· 약 5분

자바 17, 스프링 6.0, 스프링 부트 3.1

팀 프로젝트를 진행하면서 스프링 부트 3.1을 사용하게 되었다.
2.7 버전을 사용할 수도 있었지만 LTS 기간과 취약점 패치로 인한 버전업 등을 고려했을 때 3.1과 자바 17을 사용하는 것이 더 효율적이라고 판단했다.

자바 변경 사항

우아한테크코스 레벨 2까지는 자바 11을 사용했었다.
따라서 자바 11부터 자바 17까지의 변경사항을 정식 릴리즈 기준으로 정리해보려고 한다.

Switch Expressions(Java 14)

Java 14에서는 기존의 Switch 문을 간결하게 작성할 수 있는 Switch 식이 추가되었다.

enum RESULT {
WIN, LOSE, DRAW
}

RESULT result = RESULT.WIN;

int prize = switch (result) {
case WIN -> 10_000_000;
case LOSE, DRAW -> 5_000_000;
default -> 0;
};

주요 특징은 다음과 같다.

  • -> 연산자를 이용하여 각 case에 대한 결과를 바로 반환할 수 있다.
  • case를 콤마(,)로 연결하여 하나의 case에 여러 값을 지정할 수 있다.
  • break 문이 필요 없다.
  • default 블록을 통해 기본 값을 지정할 수 있다.

Text Block(Java 15)

Java 15에는 새로운 문자열 표현방식이 추가되었다.
긴 문자열을 + 연산자의 도움 없이 가독성있게 작성할 수 있다.

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
@Query("""
SELECT p FROM Post p
WHERE p.title LIKE %:keyword%
OR p.content LIKE %:keyword%
""")
List<Post> findPostsByTitleOrContentContainingKeyword(String keyword);
}

NPE 메시지(Java 15)

String name = null;
name.chars();

/**
# before
java.lang.NullPointerException
at com.example.DiscountPolicyTest.test(NullPointerExceptionTest.java:61)

# after
Cannot invoke "String.chars()" because "name" is null
java.lang.NullPointerException: Cannot invoke "String.chars()" because "name" is null
*/

Record(Java 16)

Lombok의 @Data, kotlin의 data 클래스와 유사한 기능을 제공한다.
@@ -34,7 +34,7 @@ What's New in Spring Framework 6.x
Spring Boot 3.0 Release Notes
Spring Boot 3.1 Release Notes

- - + + \ No newline at end of file diff --git a/jenkins.html b/jenkins.html index 7f279be3c..218251796 100644 --- a/jenkins.html +++ b/jenkins.html @@ -13,12 +13,12 @@ - - + +
-

Jenkins로 CI/CD 설정

· 약 8분

설정 환경

소프트웨어 이미지: Amazon Linux 2023 AMI
+

- - + + \ No newline at end of file diff --git a/jsr-310.html b/jsr-310.html index f2fc6be8d..b81363745 100644 --- a/jsr-310.html +++ b/jsr-310.html @@ -13,19 +13,19 @@ - - + +
-

JSR-310

· 약 2분

이전에 많은 문제가 있던 자바의 클래스(Calendar, Date)를 대체하는 날짜와 시간 API
+

JSR-310

· 약 2분

이전에 많은 문제가 있던 자바의 클래스(Calendar, Date)를 대체하는 날짜와 시간 API
ISO-8601을 기반으로 작성
설계 목표 → 불변, Fluent API, 명확하고 명시적, 확장 가능성

ISO-8601

날짜와 시간에 관련된 데이터를 다루는 국제 표준

LocalDate, LocalTime, LocalDateTime

날짜와 시간을 표현하는 클래스

Instant

유닉스 시간(1970-01-01, 00:00:00 UTC) 기준으로 특정 지점까지의 시간을 초로 표현하는 클래스
기계의 관점에서 시간 표현

Duration, Period

간격을 표현하는 클래스

TemporalAdjusters

복잡한 날짜 조정이 필요할 때 사용
필요한 경우 다음 인터페이스를 구현하여 커스텀 TemporalAdjuster를 구현 가능

@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}

DateTimeFormatter

날짜와 시간 포맷 클래스
특정 날짜 패턴이나, DateTimeFormatterBuilder를 이용해서 커스텀한 포맷을 생성 가능

ZoneId, ZoneOffset

ZoneId는 지역 ID는 ‘지역/도시’ 형식, ZoneOffset은 시차 UTC 기준 고정된 시간 차이 이용
ZoneId의 경우 IANA Time Zone Database에서 제공하는 지역 집합 정보 사용

Instant instant = Instant.now();
LocalDateTime utc = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

참고 자료

- - + + \ No newline at end of file diff --git a/kotlin-null.html b/kotlin-null.html index 8dbca0ec2..32da59739 100644 --- a/kotlin-null.html +++ b/kotlin-null.html @@ -13,12 +13,12 @@ - - + +
-

Kotlin에서 null을 다루는 방법

· 약 5분

nullable 타입

코틀린은 NullPointerException 예외를 최대한 발생시키지 않기 위해 타입 시스템이 설계되어 있다.
+

Kotlin에서 null을 다루는 방법

· 약 5분

nullable 타입

코틀린은 NullPointerException 예외를 최대한 발생시키지 않기 위해 타입 시스템이 설계되어 있다.
이는 실행 시점이 아닌 컴파일 시 미리 오류가 발생할 가능성이 있는 부분을 미리 감지하여 NPE 발생의 가능성을 줄여준다.

코틀린의 경우 nullable 타입을 다음과 같이 표현한다.

val number: Int?

타입 뒤에 ?를 붙여 해당 값이 null이 될 수 있다는 것을 의미한다.
만약 ?를 붙이지 않을 때 null을 받는 경우 컴파일 시 오류가 발생한다.

?. Safe Calls 연산자

자바에서 NPE를 발생시키지 않기 위해 null을 처리하는 가장 간단한 방법으로는 분기를 사용하는 방법이 있다.

코틀린은 안전한 호출 연산자인 ?. 연산자를 지원한다.
따라서 참조 값이 null이 아닐 경우에만 메서드 호출을 할 수 있다.
@@ -30,7 +30,7 @@ 사용하기 쉽지만, 리스크가 크고 혹시나 해당 값이 추후에는 null이 될 수 있기 때문에 지양해야 된다고 생각한다.

val length: Int = word!!.length

as? 안전한 캐스팅

타입 변환을 할 때 지정한 타입으로 변경할 수 없다면 ClassCastException이 발생한다.
코틀린에서는 as 뒤에 ?를 붙여 안전하게 타입 변환을 할 수 있다.
따라서 미리 변환 가능한 타입인지 확인하지 않고, 안전하게 타입을 변환 할 수 있다.

타입 변환이 불가능 할 경우 예외를 발생시키지 않고 null을 반환한다.

val value: Int? = something as? Int

List에서의 null 처리

List에는 null이 아닌 값만 반환하는 filterNotNull 유틸리티 메서드를 제공한다.

val foodsWithNull: List<String?> = listOf("Pizza", "Cheese", null, "Potato")
val foods = foodsWithNull.filterNotNull()

참고 자료

- - + + \ No newline at end of file diff --git a/ladder-retrospective.html b/ladder-retrospective.html index 8858096b9..5a4551296 100644 --- a/ladder-retrospective.html +++ b/ladder-retrospective.html @@ -13,12 +13,12 @@ - - + +
-

사다리 타기 미션 회고

· 약 11분

사다리 타기

사다리 타기 미션에서는 우가와 페어가 매칭되었다.
+

사다리 타기 미션 회고

· 약 11분

사다리 타기

사다리 타기 미션에서는 우가와 페어가 매칭되었다.
이전 미션과 달리 TDD로 진행하는 것이 필수였기 때문에 익숙하지 않았지만, 우가와 미션에 관한 소통이 잘 되어서 큰 문제 없이 미션을 마무리할 수 있었다.

우가와 이야기가 잘 통해서 그런지 1단계는 크게 어렵지 않게 진행할 수 있었는데, 2단계에서 많이 고전한 것 같다.

2단계에서는 2가지 방법으로 구현해봤다.

  1. LadderGame에서 Position 기준으로 사다리 게임을 진행하는 방법
  2. Player에게 Ladder를 넘겨서 Ladder에게 Position을 넘겨주며 메시지를 보내는 방법

Position 기준으로 사다리 게임을 진행하는 방법

사실상 index를 Ladder에게 넘겨주고, 해당 index에 대한 결과를 받는 방법과 유사했다.
구현하고 나니 다른 클래스들이 Position에 대한 의존도가 너무 높은 것 같았다.
또한 Players가 별다른 책임을 가지고 있지 않다고 느꼈다.

public LadderGameResult play() {
final Map<Player, Item> result = new LinkedHashMap<>();
// 사용자 수만큼 Position을 가져와서 사다리 게임을 진행한다.
for (Position position : Position.range(players.count())) {
final Position resultPosition = ladder.play(position);
result.put(players.get(position), items.get(resultPosition));
}
return new LadderGameResult(result);
}

Player에게 Ladder를 전달하여 게임을 진행하는 방법

Position에 대한 값을 가지고 있는 Player에게 Ladder를 넘겨서, Player가 Ladder에게 메시지를 보내도록 구현하였다.
@@ -50,7 +50,7 @@ 또한 페어 진행이 느린 것 같다고 말해줘서 안정적으로 시간 안에 미션을 완료할 수 있었다.
페어프로그래밍 진행 속도에 대해 조금 더 생각을 해봐야겠다!

항상 지나갈 때마다 웃어주는데, 나도 자주 웃어야겠다고 생각했다.
웃는 것만으로도 사람이 밝아 보여서 너무 좋은 것 같다!

- - + + \ No newline at end of file diff --git a/level2-interview-retrospective.html b/level2-interview-retrospective.html index a25c49d85..8f40904e7 100644 --- a/level2-interview-retrospective.html +++ b/level2-interview-retrospective.html @@ -13,12 +13,12 @@ - - + +
-

레벨 2 - 레벨 인터뷰 회고

· 약 4분

레벨 인터뷰

레벨 1 때는 준비해둔 내용으로 인터뷰를 진행해서 그렇게 특별한 부분이 없었다.
+

레벨 2 - 레벨 인터뷰 회고

· 약 4분

레벨 인터뷰

레벨 1 때는 준비해둔 내용으로 인터뷰를 진행해서 그렇게 특별한 부분이 없었다.
따라서 레벨 1 레벨 인터뷰 회고는 레벨 1 회고를 작성할 때 끼워넣었다.
이번에는 범위도 제한되어 있어 어떻게 준비해야 할지 당황했고, 답변에도 부족한 부분이 많았었다.
기억이 사라지기 전에 큰 문제 없이 답변한 내용을 제외하고, 기억 남는 것 위주로 작성해 보려고 한다.

API 문서 도구 선택

큰 문제 없이 답변을 했는데 앞으로도 팀 프로젝트를 하면서 도움 될 것 같은 내용이 있어서 남겨두려고 한다.
@@ -31,7 +31,7 @@ 생각할 시간을 가졌을 때 "다시 말씀드려도 될까요?"라고 말하고 답변을 이어나가기
기술적으로 깊이가 부족하다고 생각이 많이 들어서 조금 더 깊게 공부하고 정리하기
이전에 공부했던거 되돌아 보는 시간 가지기

- - + + \ No newline at end of file diff --git a/mock-static-method.html b/mock-static-method.html index 302595e99..395dbde9e 100644 --- a/mock-static-method.html +++ b/mock-static-method.html @@ -13,12 +13,12 @@ - - + +
-

Mockito 이용해서 static 메서드 모킹하기

· 약 3분

개요

정적 팩터리 메서드를 모킹한다는 것은 객체지향적인 관점에서 볼 때 안티패턴이다.
+

Mockito 이용해서 static 메서드 모킹하기

· 약 3분

개요

정적 팩터리 메서드를 모킹한다는 것은 객체지향적인 관점에서 볼 때 안티패턴이다.
하지만 특수한 경우에는 정적 메서드를 모킹하는 것이 필요할 수 있다고 생각한다.

예를 들어 레거시 코드를 테스트 한다던지, IO 관련한 부분을 테스트 할 때 정말 필요한 부분에만 적용할 수 있을 것이다.

프로젝트를 진행하며 ImageIo.write 메서드가 호출되는 지 검증이 필요했다.
해당 static 메서드를 호출하는 부분을 따로 RouteImageUploader 클래스로 최대한 분리했다.
이미지 저장 기능 자체가 외부로 나가는 상호작용이고, 호출 횟수를 검사하는데는 mock을 사용하는게 적절하다고 판단했다.

public void upload(BufferedImage bufferedImage) {
File file = new File(파일경로);
try {
ImageIO.write(bufferedImage, ROUTE_IMAGE_FORMAT, file);
} catch (IOException e) {
throw new DrawException(IMAGE_SAVE_FAIL);
}
}

Mocking static methods

Mockito 3.4.0 이후에는 static method를 모킹할 수 있는 Mockito.mockStatic 메서드를 지원한다.
@@ -27,7 +27,7 @@ 항상 상황을 고려하고 간결함을 포기할 만큼 중요한 부분인지 적절한 트레이드오프를 고려하자.

참고 자료

Mocking static methods
Mockito mock static methods
Enable mocking static methods in Mockito

- - + + \ No newline at end of file diff --git a/mysql-lock.html b/mysql-lock.html index 2f27cacc2..c018c33a0 100644 --- a/mysql-lock.html +++ b/mysql-lock.html @@ -13,12 +13,12 @@ - - + +
-

MySQL 엔진의 잠금

· 약 5분

MySQL 엔진의 잠금

MySQL에서의 락은 스토리지 엔진 레벨과, MySQL 엔진 레벨로 나눌 수 있다.
+

MySQL 엔진의 잠금

· 약 5분

MySQL 엔진의 잠금

MySQL에서의 락은 스토리지 엔진 레벨과, MySQL 엔진 레벨로 나눌 수 있다.
MySQL 엔진 레벨의 잠금은 모든 스토리지 엔진에 영향을 미친다.

글로벌 락(Global lock)

MySQL에서 제공하는 잠금 중 가장 넓은 범위를 가지고 있는 잠금이다.

  • 영향을 미치는 범위는 해당 서버 전체이다.
  • 작업 대상 테이블, 데이터베이스 상관 없이 동일하게 영향을 받는다.

한 세션에서 글로벌 락을 획득하면 해제 될 때 까지 조회를 제외한 대부분의 명령이 대기 상태가 된다.
데이터베이스에 존재하는 MyISAM이나 MEMORY 테이블에 대해 일관된 백업을 받아야할 때 사용한다.
InnoDB 스토리지 엔진에서는 백업 시 조금 더 가벼운 백업 락을 사용할 수 있다.

-- GLOBAL LOCK
FLUSH TABLES WITH READ LOCK;
-- UNLOCK
UNLOCK TABLES;

-- BACKUP LOCK
LOCK INSTANCE FOR BACKUP;
-- UNLOCK
UNLOCK INSTANCE;
- - + + \ No newline at end of file diff --git a/order-retrospective.html b/order-retrospective.html index 9851244f4..b87956015 100644 --- a/order-retrospective.html +++ b/order-retrospective.html @@ -13,12 +13,12 @@ - - + +
-

장바구니 주문 미션 회고

· 약 5분

장바구니 주문 미션

배포 및 협업을 할 수 있는 미션이었다.
+

장바구니 주문 미션 회고

· 약 5분

장바구니 주문 미션

배포 및 협업을 할 수 있는 미션이었다.
마코, 우가, 우코, 우스 그리고 나까지 합쳐서 5명이 한 팀이 되었다.

배포

이전 미션들과 달리 AWS를 이용해 배포를 해야 했다.
각자 하나의 EC2 인스턴스를 제공받을 수 있었고, 팀 별로 DB를 위한 추가 인스턴스를 제공받았다.
배포 스크립트를 작성하는 경험을 해볼 수 있었다.
@@ -35,7 +35,7 @@ 추가로 현업에서는 고가용성 내결함성 등을 위하여 클러스터를 구성하여 사용하는 경우가 많고, 이 경우 readOnly 설정이 되어있다면 읽기 전용 DB로 질의가 들어가서 부하 분산의 효과가 있다고 한다.

DAO에 @Transactional 적용

DAO에 트랜잭션을 보장해 보는 건 어떻겠냐고 리뷰가 달려서 고민을 많이 했다.
Service 계층에 이미 트랜잭션을 보장해 주고 있기에 필요 없지 않을까 생각했었다.
DAO를 다른 곳에서 사용하더라도 트랜잭션을 보장하기 위해(확장성 고려) @Transactional을 적용하는 것도 괜찮은 것 같다.

- - + + \ No newline at end of file diff --git a/page/10.html b/page/10.html index 50b0a202e..c64b834ac 100644 --- a/page/10.html +++ b/page/10.html @@ -13,19 +13,21 @@ - - + +
-

· 약 6분

./route.png

이미지 생성의 책임

위 와이어 프레임에서 여행 히스토리여행에 대한 감상을 위한 경로 이미지의 경우 네이버 지도를 사용하여 해당 기능을 구현할 수 없으니 당연히 맵 API에서 제공하는 도형 그리기 API(네이버 맵 API 기준 Polyline)를 사용할 수 없다.
-따라서 이미지를 직접 생성하거나, 클라이언트에서 직접 위경도를 이용하여 그려야 한다.

해당 요구사항을 해결하기 위해서는 다음과 같은 기능을 가진 라이브러리가 필요하다.

  • 이미지 생성
  • 선과 점 표현
  • 투명한 배경색

현재 클라이언트의 바쁜 일정과 기능 구현에 있어 약간의 연산이 들어간다는 부분을 고려하여 백엔드에서 이미지를 생성하기로 결정을 내렸다.

고려한 기술

백엔드에서 이미지 생성을 하기 위해 다음과 같은 라이브러리 또는 기술들을 확인해 보았다.

  • Python의 Matplotlib
  • AWT(Abstract Window Toolkit) [최종 선택]
  • 이미지 처리 라이브러리 및 Java에서 내부적으로 Matplotlib 사용할 수 있는 라이브러리 (원하는 기능 없음)
  • Java Swing, Java FX (단순한 선 그리기 + 점 찍기라 불필요)

Python & Matplotlib

데이터 시각화 라이브러리
-이미지 생성 및 로컬에 저장까지 걸리는 시간: 0.2초

  • 코드가 간단해서 유지 보수성이 좋다.
  • AWS Lambda 같은 서버리스 컴퓨팅 서비스나 FastAPI와 같은 웹 프레임워크로 추가적인 API를 구현해야 한다.
  • Spring Boot에서 추가적인 API 호출을 해야하고, 확장성과 비동기 처리 등 고려 해야 할 부분이 많다.

Java AWT 이외의 라이브러리

Python이 아닌 Java에서의 라이브러리도 고려를 해봤지만 요구사항에 적합하지 않거나, 적은 요구사항에 비해 무거운 라이브러리들이 많아서 제외했다.

라이브러리설명제외 이유
SwingAWT 이후에 나온 GUI 라이브러리, 네이티브 UI를 사용하지 않고 모든 운영체제 상에서 동일한 UI를 가지도록 함요구사항에 비해 무겁고 복잡도가 높음
JavaFXSwing 이후에 나온 GUI 라이브러리, 3차원 그래픽을 지원함요구사항에 비해 무겁고 복잡도가 높음
simple-java-plotAWT로 구현된 플로팅 라이브러리AWT 기반이긴 하지만 직접 AWT를 사용하는 것에 비해 메리트가 없음, 커스텀 설정 기능이 없음
matplotlib4jMatplotlib를 Java에서 사용할 수 있게 하는 라이브러리내부적으로 파이썬 사용하기에 무거움, 배경 투명화 기능 없음

Java & AWT(Abstract Window Toolkit)

그래픽과 이미지를 그리기 위한 도구
-이미지 생성 및 로컬에 저장까지 걸리는 시간: 1.75초

  • 플로팅 라이브러리를 사용하는 것보다 구현의 난이도가 다소 존재한다.
  • 이미지 생성 시간이 다소 소요되기 때문에 빠른 응답 반환을 위해 비동기 처리를 고려할 수 있을 것 같다.
  • 추가적인 api 호출을 하지 않아도 된다.

기술 선택

AWT의 경우 Matplotlib에 비해 구현의 난이도가 다소 있고, 이미지 생성 시간이 더 많이 걸리는 단점이 있다.
-하지만 추가적인 api 호출을 하지 않아도 되는 부분, Python을 사용하는 경우 추가적인 웹 프레임워크의 학습 비용을 고려하여 AWT를 사용하기로 결정했다.

유지 보수

AWT라는 생소한 기술을 사용하기 때문에 유지 보수성을 위해 팀원들과 공유하는 것이 중요하다고 생각했다.
-따라서 다음과 같은 방법으로 공유하기로 했다.

  1. 코드 리뷰와 PR을 통해 작성한 AWT 코드에 대한 설명 및 리뷰 받는다.
  2. AWT를 사용한 부분을 문서화하여 공유한다.

레벨 3를 마무리하며 내용 추가

기술 선택을 하기 위한 실행 시간 측정에 오류가 있었다.
-AWT를 사용하는 부분에서 애플리케이션 실행 시간을 제외하면 파이썬과 비슷한 시간안에 이미지를 생성할 수 있었다.

- - +

· 약 3분

개요

정적 팩터리 메서드를 모킹한다는 것은 객체지향적인 관점에서 볼 때 안티패턴이다.
+하지만 특수한 경우에는 정적 메서드를 모킹하는 것이 필요할 수 있다고 생각한다.

예를 들어 레거시 코드를 테스트 한다던지, IO 관련한 부분을 테스트 할 때 정말 필요한 부분에만 적용할 수 있을 것이다.

프로젝트를 진행하며 ImageIo.write 메서드가 호출되는 지 검증이 필요했다.
+해당 static 메서드를 호출하는 부분을 따로 RouteImageUploader 클래스로 최대한 분리했다.
+이미지 저장 기능 자체가 외부로 나가는 상호작용이고, 호출 횟수를 검사하는데는 mock을 사용하는게 적절하다고 판단했다.

public void upload(BufferedImage bufferedImage) {
File file = new File(파일경로);
try {
ImageIO.write(bufferedImage, ROUTE_IMAGE_FORMAT, file);
} catch (IOException e) {
throw new DrawException(IMAGE_SAVE_FAIL);
}
}

Mocking static methods

Mockito 3.4.0 이후에는 static method를 모킹할 수 있는 Mockito.mockStatic 메서드를 지원한다.
+mockStatic을 사용하면 MockedStatic<T>이 반환되는데 사용 후 꼭 close를 해줘야 한다.

JUnit의 @BeforeAll로 설정하고 @AfterAll 메서드로 종료하는 방법도 있지만 MockedStatic<T>의 상위 인터페이스인 ScopedMock이 AutoCloseable을 구현하고 있기에 try-with-resources를 사용하는 방법이 더욱 좋은 것 같다.

// given
BufferedImage bufferedImage = new BufferedImage(800, 800, BufferedImage.TYPE_INT_ARGB);
RouteImageUploader routeImageUploader = new RouteImageUploader();

// expect
try (MockedStatic<ImageIO> imageIO = Mockito.mockStatic(ImageIO.class)) {
routeImageUploader.upload(bufferedImage);
imageIO.verify(
() -> ImageIO.write(any(BufferedImage.class), any(String.class), any(File.class)),
times(1)
);
}

마치며

정적 메서드를 모킹하는 것은 안티패턴이으로 적절한 추상화를 이용해 테스트 하기 좋은 코드를 만드는 연습을 하자.
+하지만 추상화를 하면 할 수록 코드의 복잡도는 증가한다.
+항상 상황을 고려하고 간결함을 포기할 만큼 중요한 부분인지 적절한 트레이드오프를 고려하자.

참고 자료

Mocking static methods
+Mockito mock static methods
+Enable mocking static methods in Mockito

+ + \ No newline at end of file diff --git a/page/11.html b/page/11.html index 22b51db95..5cea58a95 100644 --- a/page/11.html +++ b/page/11.html @@ -13,28 +13,19 @@ - - + +
-

· 약 5분

자바 17, 스프링 6.0, 스프링 부트 3.1

팀 프로젝트를 진행하면서 스프링 부트 3.1을 사용하게 되었다.
-2.7 버전을 사용할 수도 있었지만 LTS 기간과 취약점 패치로 인한 버전업 등을 고려했을 때 3.1과 자바 17을 사용하는 것이 더 효율적이라고 판단했다.

자바 변경 사항

우아한테크코스 레벨 2까지는 자바 11을 사용했었다.
-따라서 자바 11부터 자바 17까지의 변경사항을 정식 릴리즈 기준으로 정리해보려고 한다.

Switch Expressions(Java 14)

Java 14에서는 기존의 Switch 문을 간결하게 작성할 수 있는 Switch 식이 추가되었다.

enum RESULT {
WIN, LOSE, DRAW
}

RESULT result = RESULT.WIN;

int prize = switch (result) {
case WIN -> 10_000_000;
case LOSE, DRAW -> 5_000_000;
default -> 0;
};

주요 특징은 다음과 같다.

  • -> 연산자를 이용하여 각 case에 대한 결과를 바로 반환할 수 있다.
  • case를 콤마(,)로 연결하여 하나의 case에 여러 값을 지정할 수 있다.
  • break 문이 필요 없다.
  • default 블록을 통해 기본 값을 지정할 수 있다.

Text Block(Java 15)

Java 15에는 새로운 문자열 표현방식이 추가되었다.
-긴 문자열을 + 연산자의 도움 없이 가독성있게 작성할 수 있다.

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
@Query("""
SELECT p FROM Post p
WHERE p.title LIKE %:keyword%
OR p.content LIKE %:keyword%
""")
List<Post> findPostsByTitleOrContentContainingKeyword(String keyword);
}

NPE 메시지(Java 15)

String name = null;
name.chars();

/**
# before
java.lang.NullPointerException
at com.example.DiscountPolicyTest.test(NullPointerExceptionTest.java:61)

# after
Cannot invoke "String.chars()" because "name" is null
java.lang.NullPointerException: Cannot invoke "String.chars()" because "name" is null
*/

Record(Java 16)

Lombok의 @Data, kotlin의 data 클래스와 유사한 기능을 제공한다.
-Record를 선언하는 경우 접근자, 생성자, equals & hashcode, toString이 제공된다.
-데이터 전송 용도로 적합해 보인다.

public record PostDto(String title, String content) {
}

추가적인 변경사항

이외에도 stream의 toList, 인스턴스의 타입을 간편하게 체크하는 Pattern Matching Instanceof, Sealed class 등이 추가되었다.

스프링, 스프링 부트 변경 사항

스프링과 스프링 부트에도 많은 변경 사항이 있었다.
-따라서 필요해보이는 몇개 정도만 정리했다.

스프링 요구사항

Java 17, Jakarta EE 9 이상이어야 한다.

네임스페이스 변경

Jakarta EE 9가 적용되면서 네임스페이스도 전반적으로 javax -> jakarta로 변경되었다.

PathPatternParser - trailing slash 허용하지 않음

6.0 이전의 경우 기본 설정 기준으로 @GetMapping("/hello")@GetMapping("/hello/")가 동일했다.
-6.0 이후의 PathPatternParser가 기본으로 사용되고, /hello/hello/는 서로 다른 URL로 매칭된다.

PathPatternParser used by default (with the ability to opt into PathMatcher).

HTTP interface client

자바 인터페이스와 어노테이션을 이용하여 HTTP 요청을 위한 서비스를 정의할 수 있는 방법이 추가되었다.
-자세한 내용은 토비님의 강의를 참고하면 좋을 것 같다.

스프링 부트 최소 요구사항

Gradle 7.3, Java 17, Kotlin 1.6, Jakarta EE 9, Spring Framework 6
-이외에도 서드파티들의 최신 릴리즈 버전을 사용함으로, 문제가 발생하는 경우 해당 버전에 맞는 릴리즈 노트를 참고할 수 있을 것 같다.

참고 자료

어느 월급쟁이개발자 의 스프링 부트 따라잡기
-자바 9-16 주요 특징 복습하기
-Java EE에서 Jakarta EE로의 전환
-Spring 6의 새로운 HTTP Interface와 3 가지 REST Clients 라이브 코딩
-What's New in Spring Framework 6.x
-Spring Boot 3.0 Release Notes
-Spring Boot 3.1 Release Notes

- - +

· 약 6분

./route.png

이미지 생성의 책임

위 와이어 프레임에서 여행 히스토리여행에 대한 감상을 위한 경로 이미지의 경우 네이버 지도를 사용하여 해당 기능을 구현할 수 없으니 당연히 맵 API에서 제공하는 도형 그리기 API(네이버 맵 API 기준 Polyline)를 사용할 수 없다.
+따라서 이미지를 직접 생성하거나, 클라이언트에서 직접 위경도를 이용하여 그려야 한다.

해당 요구사항을 해결하기 위해서는 다음과 같은 기능을 가진 라이브러리가 필요하다.

  • 이미지 생성
  • 선과 점 표현
  • 투명한 배경색

현재 클라이언트의 바쁜 일정과 기능 구현에 있어 약간의 연산이 들어간다는 부분을 고려하여 백엔드에서 이미지를 생성하기로 결정을 내렸다.

고려한 기술

백엔드에서 이미지 생성을 하기 위해 다음과 같은 라이브러리 또는 기술들을 확인해 보았다.

  • Python의 Matplotlib
  • AWT(Abstract Window Toolkit) [최종 선택]
  • 이미지 처리 라이브러리 및 Java에서 내부적으로 Matplotlib 사용할 수 있는 라이브러리 (원하는 기능 없음)
  • Java Swing, Java FX (단순한 선 그리기 + 점 찍기라 불필요)

Python & Matplotlib

데이터 시각화 라이브러리
+이미지 생성 및 로컬에 저장까지 걸리는 시간: 0.2초

  • 코드가 간단해서 유지 보수성이 좋다.
  • AWS Lambda 같은 서버리스 컴퓨팅 서비스나 FastAPI와 같은 웹 프레임워크로 추가적인 API를 구현해야 한다.
  • Spring Boot에서 추가적인 API 호출을 해야하고, 확장성과 비동기 처리 등 고려 해야 할 부분이 많다.

Java AWT 이외의 라이브러리

Python이 아닌 Java에서의 라이브러리도 고려를 해봤지만 요구사항에 적합하지 않거나, 적은 요구사항에 비해 무거운 라이브러리들이 많아서 제외했다.

라이브러리설명제외 이유
SwingAWT 이후에 나온 GUI 라이브러리, 네이티브 UI를 사용하지 않고 모든 운영체제 상에서 동일한 UI를 가지도록 함요구사항에 비해 무겁고 복잡도가 높음
JavaFXSwing 이후에 나온 GUI 라이브러리, 3차원 그래픽을 지원함요구사항에 비해 무겁고 복잡도가 높음
simple-java-plotAWT로 구현된 플로팅 라이브러리AWT 기반이긴 하지만 직접 AWT를 사용하는 것에 비해 메리트가 없음, 커스텀 설정 기능이 없음
matplotlib4jMatplotlib를 Java에서 사용할 수 있게 하는 라이브러리내부적으로 파이썬 사용하기에 무거움, 배경 투명화 기능 없음

Java & AWT(Abstract Window Toolkit)

그래픽과 이미지를 그리기 위한 도구
+이미지 생성 및 로컬에 저장까지 걸리는 시간: 1.75초

  • 플로팅 라이브러리를 사용하는 것보다 구현의 난이도가 다소 존재한다.
  • 이미지 생성 시간이 다소 소요되기 때문에 빠른 응답 반환을 위해 비동기 처리를 고려할 수 있을 것 같다.
  • 추가적인 api 호출을 하지 않아도 된다.

기술 선택

AWT의 경우 Matplotlib에 비해 구현의 난이도가 다소 있고, 이미지 생성 시간이 더 많이 걸리는 단점이 있다.
+하지만 추가적인 api 호출을 하지 않아도 되는 부분, Python을 사용하는 경우 추가적인 웹 프레임워크의 학습 비용을 고려하여 AWT를 사용하기로 결정했다.

유지 보수

AWT라는 생소한 기술을 사용하기 때문에 유지 보수성을 위해 팀원들과 공유하는 것이 중요하다고 생각했다.
+따라서 다음과 같은 방법으로 공유하기로 했다.

  1. 코드 리뷰와 PR을 통해 작성한 AWT 코드에 대한 설명 및 리뷰 받는다.
  2. AWT를 사용한 부분을 문서화하여 공유한다.

레벨 3를 마무리하며 내용 추가

기술 선택을 하기 위한 실행 시간 측정에 오류가 있었다.
+AWT를 사용하는 부분에서 애플리케이션 실행 시간을 제외하면 파이썬과 비슷한 시간안에 이미지를 생성할 수 있었다.

+ + \ No newline at end of file diff --git a/page/12.html b/page/12.html index 8d18bb6f4..74e6a5e36 100644 --- a/page/12.html +++ b/page/12.html @@ -13,26 +13,28 @@ - - + +
-

· 약 5분

웹소켓

단일 TCP 연결을 통해 클라이언트와 서버 간 전이중 양방향 통신을 지원하는 프로토콜
-웹 환경에서 연속된 데이터를 실시간으로 처리할 수 있다.

웹소켓은 HTTP의 포트를 그대로 사용하고 각각 포트 80과 포트 443을 사용하여 HTTP(ws://) 및 HTTPS(wss://)로 서버에 연결한다.

웹소켓 등장 배경

웹소켓이 등장하기 이전, 실시간성을 보장하기 위해 Polling, Long polling, Streaming 같은 기술을 사용했어야 했다.
-이는 실시간성이나 양방향성을 만족시키지 못했고, HTTP를 이용하기 때문에 과도한 오버헤드가 발생했다.

polling, long polling, streaming

Polling: 주기적으로 서버에 요청을 보내 수신할 정보가 있는지 확인하는 방법

  • 서버에서 보낼 내용이 없어도 클라이언트는 알 수 없다.
  • 계속해서 요청을 보내 확인을 해야하기 때문에 서버에 불필요한 부하를 주어야 한다.

Long Polling: 클라이언트의 요청에 대해 응답을 보내지 않고 있다가 이벤트가 발생했을때 응답하는 방법

  • 폴링 방식보다 서버에 적은 부하를 줄 수 있지만, 요청의 주기가 짧으면 폴링과 차이가 없어진다.

Streaming: 클라이언트가 request를 보내면 커넥션을 맺고, 이 커넥션을 유지하면서 서버가 계속 데이터를 보내는 방법

  • 클라이언트가 서버에 요청을 하고 싶다면 새로운 커넥션을 맺어야 한다.

웹소켓의 동작

1. Upgrade 요청

WebSocket 프로토콜로 전환하는 HTTP 요청을 보낸다.
-이는 HTTP와 같이 80, 443 포트를 사용한다.
-웹소켓으로 전환하기 위해서는 Upgrade: websocket, Connection: Upgrade 헤더가 필요하다.
-Sec-WebSocket-Key는 서버에서 Sec-WebSocket-Accept를 계산하여 응답하고 이 값이 예상한 값과 다르면 연결이 수립되지 않는다.
-Sec-WebSocket-Protocol의 경우 서브프로토콜의 목록으로 서버 측에서는 해당 목록 중 하나를 선택하여 반환해야 한다.
-만약 서버측에서 여러 개 지원이 가능한 경우 지원 가능한 프로토콜 중 첫번째 프로토콜을 클라이언트측으로 보낸다.

GET /chats HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
Sec-WebSocket-Protocol: v10.stomp, v11.stomp
Sec-WebSocket-Version: 13
Origin: http://localhost:8080

2. Switching Protocols

서버는 101 Switching Protocols 응답을 반환한다.
-Sec-WebSocket-Accept은 Sec-WebSocket-Key 뒤에 258EAFA5-E914-47DA-95CA-C5AB0DC85B11를 붙이고 SHA1로 해싱 후 Base64로 인코딩하여 반환한다.
-이는 서버 웹소켓 프로토콜의 지원 여부를 클라이언트에게 명확히 알리기 위해 존재한다.

HTTP/1.1 101 Switching Protocols 
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=
Sec-WebSocket-Protocol: v10.stomp

3. 통신 후 종료

연결이 수립되면 웹소켓 프레임 단위로 양방향 통신을 한다.
-연결 종료를 원하는 경우 클라이언트, 서버 모두 연결 종료를 요청할 수 있다.

참고 자료

https://datatracker.ietf.org/doc/html/rfc6455
-https://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
-https://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
-https://docs.spring.io/spring-framework/reference/web/websocket.html

- - +

· 약 5분

자바 17, 스프링 6.0, 스프링 부트 3.1

팀 프로젝트를 진행하면서 스프링 부트 3.1을 사용하게 되었다.
+2.7 버전을 사용할 수도 있었지만 LTS 기간과 취약점 패치로 인한 버전업 등을 고려했을 때 3.1과 자바 17을 사용하는 것이 더 효율적이라고 판단했다.

자바 변경 사항

우아한테크코스 레벨 2까지는 자바 11을 사용했었다.
+따라서 자바 11부터 자바 17까지의 변경사항을 정식 릴리즈 기준으로 정리해보려고 한다.

Switch Expressions(Java 14)

Java 14에서는 기존의 Switch 문을 간결하게 작성할 수 있는 Switch 식이 추가되었다.

enum RESULT {
WIN, LOSE, DRAW
}

RESULT result = RESULT.WIN;

int prize = switch (result) {
case WIN -> 10_000_000;
case LOSE, DRAW -> 5_000_000;
default -> 0;
};

주요 특징은 다음과 같다.

  • -> 연산자를 이용하여 각 case에 대한 결과를 바로 반환할 수 있다.
  • case를 콤마(,)로 연결하여 하나의 case에 여러 값을 지정할 수 있다.
  • break 문이 필요 없다.
  • default 블록을 통해 기본 값을 지정할 수 있다.

Text Block(Java 15)

Java 15에는 새로운 문자열 표현방식이 추가되었다.
+긴 문자열을 + 연산자의 도움 없이 가독성있게 작성할 수 있다.

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
@Query("""
SELECT p FROM Post p
WHERE p.title LIKE %:keyword%
OR p.content LIKE %:keyword%
""")
List<Post> findPostsByTitleOrContentContainingKeyword(String keyword);
}

NPE 메시지(Java 15)

String name = null;
name.chars();

/**
# before
java.lang.NullPointerException
at com.example.DiscountPolicyTest.test(NullPointerExceptionTest.java:61)

# after
Cannot invoke "String.chars()" because "name" is null
java.lang.NullPointerException: Cannot invoke "String.chars()" because "name" is null
*/

Record(Java 16)

Lombok의 @Data, kotlin의 data 클래스와 유사한 기능을 제공한다.
+Record를 선언하는 경우 접근자, 생성자, equals & hashcode, toString이 제공된다.
+데이터 전송 용도로 적합해 보인다.

public record PostDto(String title, String content) {
}

추가적인 변경사항

이외에도 stream의 toList, 인스턴스의 타입을 간편하게 체크하는 Pattern Matching Instanceof, Sealed class 등이 추가되었다.

스프링, 스프링 부트 변경 사항

스프링과 스프링 부트에도 많은 변경 사항이 있었다.
+따라서 필요해보이는 몇개 정도만 정리했다.

스프링 요구사항

Java 17, Jakarta EE 9 이상이어야 한다.

네임스페이스 변경

Jakarta EE 9가 적용되면서 네임스페이스도 전반적으로 javax -> jakarta로 변경되었다.

PathPatternParser - trailing slash 허용하지 않음

6.0 이전의 경우 기본 설정 기준으로 @GetMapping("/hello")@GetMapping("/hello/")가 동일했다.
+6.0 이후의 PathPatternParser가 기본으로 사용되고, /hello/hello/는 서로 다른 URL로 매칭된다.

PathPatternParser used by default (with the ability to opt into PathMatcher).

HTTP interface client

자바 인터페이스와 어노테이션을 이용하여 HTTP 요청을 위한 서비스를 정의할 수 있는 방법이 추가되었다.
+자세한 내용은 토비님의 강의를 참고하면 좋을 것 같다.

스프링 부트 최소 요구사항

Gradle 7.3, Java 17, Kotlin 1.6, Jakarta EE 9, Spring Framework 6
+이외에도 서드파티들의 최신 릴리즈 버전을 사용함으로, 문제가 발생하는 경우 해당 버전에 맞는 릴리즈 노트를 참고할 수 있을 것 같다.

참고 자료

어느 월급쟁이개발자 의 스프링 부트 따라잡기
+자바 9-16 주요 특징 복습하기
+Java EE에서 Jakarta EE로의 전환
+Spring 6의 새로운 HTTP Interface와 3 가지 REST Clients 라이브 코딩
+What's New in Spring Framework 6.x
+Spring Boot 3.0 Release Notes
+Spring Boot 3.1 Release Notes

+ + \ No newline at end of file diff --git a/page/13.html b/page/13.html index c285990dc..b1df6b910 100644 --- a/page/13.html +++ b/page/13.html @@ -13,33 +13,26 @@ - - + +
-

· 약 11분

팀 블로그 또는 문서화를 위해 Docusaurus를 사용하는 방법을 정리하려고 한다.

설치

공식 홈페이지에 들어가서 최신 버전을 설치한다.

yarn create docusaurus

배포

배포 안내 문서
-netlify나 vercel 같은 서버리스 플랫폼을 추천하고 있고, 간단하고, 빠른 시간 안에 배포를 할 수 있다.
-이 글에서는 github pages를 이용해서 배포하는 방법을 설명한다.

레포지토리 생성

github pages를 이용하려면 예시와 같이 username.github.io 형태의 레포지토리를 생성해야 한다.
-이때 organization을 사용하는 경우 organization.github.io 형태의 레포지토리를 생성해서 사용한다.

설정 파일 수정

docusaurus.config
module.exports = {
// ...
url: 'https://greeng00se.github.io',
baseUrl: '/',
projectName: 'greeng00se.github.io',
organizationName: 'greeng00se',
trailingSlash: false,
// ...
};

토큰 설정

github action을 위해 배포용 토큰을 하나 생성하여 생성한 레포지토리에 Repository secrets으로 설정한다.
-이 글에서는 토큰을 클래식 방식으로 생성했고 스코프는 [repo, user, workflow] 을 설정했다.

github

브랜치 생성

github에서 gh-pages 브랜치를 하나 생성한다.
-repository -> settings -> pages -> branch에서 생성한 gh-pages로 브랜치를 변경한다.
-설정한 브랜치가 배포 브랜치가 되며, 해당 브랜치에 있는 파일들을 이용해서 정적 웹사이트를 제공한다.

워크플로 작성

Docusaurus 2.0 기준 Node.js 16.14 이상의 버전을 사용해야 합니다.
-배포시에는 Repository secrets으로 설정한 DEPLOY_TOKEN 을 이용합니다.

.github/workflows/deploy.yml
name: blog

on:
push:
branches: [main]

jobs:
deploy:
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 18
cache: yarn

- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build website
run: yarn build

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.DEPLOY_TOKEN }}
publish_dir: ./build
user_name: github-actions[bot]
user_email: 41898282+github-actions[bot]@users.noreply.github.com

댓글 기능

giscus를 이용하여 댓글 기능을 추가한다.

giscus 설정

  1. 공개 저장소여야 한다.
  2. giscus 앱이 설치되어 있어야 한다.
  3. Discussions 기능이 해당 저장소에서 활성화되어 있어야 한다.

자세한 내용은 giscus를 확인하자.

docusaurus 설정

swizzling을 이용하여 컴포넌트를 감싼다.
-기존에 게시물을 giscus가 포함된 리액트 컴포넌트로 감싸는 형태가 된다.
-아래 명령어를 이용하여 BlogPostItem을 추출할 수 있다.

yarn run swizzle @docusaurus/theme-classic BlogPostItem -- --wrap

명령어를 입력하면 /src/theme/BlogPostItem/index.js 위치에 파일이 생성된다.
-파일의 내용을 아래와 같이 수정하고, 이때 setAttribute 부분은 적절하게 자신의 giscus 설정을 이용한다.

/src/theme/BlogPostItem/index.js
import OriginalBlogPostItem from "@theme-original/BlogPostItem";
import React, { useEffect, useRef } from "react";
// @ts-expect-error internal code
import { useColorMode } from "@docusaurus/theme-common";
import { useBlogPost } from "@docusaurus/theme-common/internal";

const giscusSelector = "iframe.giscus-frame";

function BlogPostItem(props) {
const { colorMode } = useColorMode();
const { isBlogPostPage } = useBlogPost();
const giscusTheme = colorMode === "dark" ? "dark" : "light";
const containerRef = useRef(null);

useEffect(() => {
if (!isBlogPostPage) return;

const giscusEl = containerRef.current.querySelector(giscusSelector);

const createGiscusEl = () => {
const script = document.createElement("script");

script.src = "https://giscus.app/client.js";
script.setAttribute("data-repo", "teco-chat/teco-chat.github.io");
script.setAttribute("data-repo-id", "R_kgDOJZ5j0Q");
script.setAttribute("data-category", "Announcements");
script.setAttribute("data-category-id", "DIC_kwDOJZ5j0c4CXS_Q");
script.setAttribute("data-mapping", "pathname");
script.setAttribute("data-strict", "0");
script.setAttribute("data-reactions-enabled", "1");
script.setAttribute("data-emit-metadata", "0");
script.setAttribute("data-input-position", "bottom");
script.setAttribute("data-theme", giscusTheme);
script.setAttribute("data-lang", "ko");
script.crossOrigin = "anonymous";
script.async = true;

containerRef.current.appendChild(script);
};

const postThemeMessage = () => {
const message = {
setConfig: {
theme: giscusTheme,
}
};

giscusEl.contentWindow.postMessage({ giscus: message }, "https://giscus.app");
};

giscusEl ? postThemeMessage() : createGiscusEl();
}, [giscusTheme]);

return (
<>
<OriginalBlogPostItem {...props} />
{isBlogPostPage && <div ref={containerRef} />}
</>
);
}

export default BlogPostItem;

알고리아 설정 및 직접 관리하기

알고리아를 사용하면 검색 기능을 추가할 수 있다.
-유료 플랜이나 netlify를 사용하는 경우 크롤러를 따로 제공해 주는 것 같다.

무료 플랜은 직접 인덱스를 수집하는 방법과, docsearch를 이용하는 방법이 있다.
-docsearch에 등록한다면 일주일에 한 번씩 크롤링이 진행된다.
-이 글에서는 직접 인덱스를 수집하는 방법을 사용한다.

알고리아 애플리케이션 생성 및 키 확인

회원가입을 하고 새로운 애플리케이션 생성을 누른다.
-생성을 다 마치면 다음과 같이 api 키를 확인할 수 있다.

algolia

키 생성

직접 인덱스를 수집하기 위한 키를 생성한다.
-addObject, editSettings, deleteIndex acl(접근 제어 목록)이 있으면 된다.

key

.env 파일 생성

프로젝트 폴더 상단에 .env 파일을 생성한다.

.env
APPLICATION_ID=MVIU5UEMOM
API_KEY=인덱스_생성용_키

config 파일 생성

마찬가지로 최상단에 config.json 파일을 생성한다. -설정 파일은 해당 링크를 참고한다.
-또는 Docusaurus의 설정 파일을 참고한다.

config.json
{
"index_name": "teco",
"start_urls": [
"https://teco-chat.github.io/"
],
"sitemap_urls": [
"https://teco-chat.github.io/sitemap.xml"
],
"sitemap_alternate_links": true,
"stop_urls": [
"/tests"
],
"selectors": {
"lvl0": {
"selector": "(//ul[contains(@class,'menu__list')]//a[contains(@class, 'menu__link menu__link--sublist menu__link--active')]/text() | //nav[contains(@class, 'navbar')]//a[contains(@class, 'navbar__link--active')]/text())[last()]",
"type": "xpath",
"global": true,
"default_value": "Documentation"
},
"lvl1": "header h1",
"lvl2": "article h2",
"lvl3": "article h3",
"lvl4": "article h4",
"lvl5": "article h5, article td:first-child",
"lvl6": "article h6",
"text": "article p, article li, article td:last-child"
},
"strip_chars": " .,;:#",
"custom_settings": {
"separatorsToIndex": "_",
"attributesForFaceting": [
"language",
"version",
"type",
"docusaurus_tag"
],
"attributesToRetrieve": [
"hierarchy",
"content",
"anchor",
"url",
"url_without_anchor",
"type"
]
},
"conversation_id": [
"833762294"
],
"nb_hits": 46250
}

docker 이용하여 크롤링

docker와 jq가 필요하다.
-jq가 설치되어 있지 않으면 mac 기준 brew를 이용해서 설치할 수 있다.

brew install jq

다음 명령어를 이용하여 .env와 config.json을 이용하여 크롤링을 한다.

docker run -it --env-file=.env -e "CONFIG=$(cat ./config.json | jq -r tostring)" algolia/docsearch-scraper

docusaurus 설정

전에 확인한 APP ID, Search-Only API KEY, IndexName을 이용하여 docusaurus.config 파일에 설정한다.

docusaurus.config
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
...
algolia: {
appId: 'MVIU5UEMOM', // Application ID
apiKey: 'b68f378013817d9a190df88cdde226a0', // Search-Only API Key
indexName: 'teco', // config.json에 설정한 인덱스명
contextualSearch: true,
},
})

부가 설정

화면 상단 Github Icon

파일 최하단에 아래 css 구문을 추가한다.

/src/css/custom.css
.header-github-link:hover {
opacity: 0.6;
}

.header-github-link:before {
content: '';
width: 24px;
height: 24px;
display: flex;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}

html[data-theme='dark'] .header-github-link:before {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}

themeconfig -> navbar에 github link를 설정한다.

docusaurus.config
navbar: {
title: 'HELLO',
items: [
{
href: 'https://github.com/greeng00se',
position: 'right',
className: 'header-github-link',
'aria-label': 'GitHub repository',
},
],
},

코드블럭

java나 kotlin의 경우 기본적으로 하이라이팅을 지원해 주지 않는다.
-prism 설정을 아래와 같이 변경해 준다.

docusaurus.config
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
additionalLanguages: ['java', 'kotlin'],
}

mermaid

mermaid를 사용하려면 @docusaurus/theme-mermaid 를 설치해야 한다.

yarn add @docusaurus/theme-mermaid

설치 후 아래와 같이 설정을 추가한다.

docusaurus.config
const config = {
...
markdown: {
mermaid: true,
},
themes: [
'@docusaurus/theme-mermaid'
],
};

themeConfig에서 mermaid의 테마를 지정할 수 있다.

docusaurus.config
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
...
mermaid: {
theme: {
light: 'neutral',
dark: 'dark'
},
},
}),

국제화 설정

국제화 설정을 한다면 Older Entries 형태의 설명이 다음 페이지 로 변경된다.
-설정파일에서 i18n에 있는 로케일 설정을 ko로 변경하면 된다.

docusaurus.config
i18n: {
defaultLocale: "ko",
locales: ["ko"],
},

블로그 글 author

팀원 별로 문서를 관리한다면 다음과 같이 어떤 팀원이 글을 작성했는지 설정해야 한다.

author

authors.yml 파일을 이용하여 사용자에 대한 기본 설정을 할 수 있다.

/blog/authors.yml
herb:
name: 허브
title: Backend
url: https://github.com/greeng00se
image_url: https://github.com/greeng00se.png

mallang:
name: 말랑
title: Backend
url: https://github.com/shin-mallang
image_url: https://github.com/shin-mallang.png

블로그 글을 작성할 때 다음과 같이 authors에 넣어주기만 하면 된다.

---
slug: 1
title: Hello World
authors: [herb, mallang]
tags: [hello, docusaurus]
---

첫 번째 문서 내용
- - +

· 약 5분

웹소켓

단일 TCP 연결을 통해 클라이언트와 서버 간 전이중 양방향 통신을 지원하는 프로토콜
+웹 환경에서 연속된 데이터를 실시간으로 처리할 수 있다.

웹소켓은 HTTP의 포트를 그대로 사용하고 각각 포트 80과 포트 443을 사용하여 HTTP(ws://) 및 HTTPS(wss://)로 서버에 연결한다.

웹소켓 등장 배경

웹소켓이 등장하기 이전, 실시간성을 보장하기 위해 Polling, Long polling, Streaming 같은 기술을 사용했어야 했다.
+이는 실시간성이나 양방향성을 만족시키지 못했고, HTTP를 이용하기 때문에 과도한 오버헤드가 발생했다.

polling, long polling, streaming

Polling: 주기적으로 서버에 요청을 보내 수신할 정보가 있는지 확인하는 방법

  • 서버에서 보낼 내용이 없어도 클라이언트는 알 수 없다.
  • 계속해서 요청을 보내 확인을 해야하기 때문에 서버에 불필요한 부하를 주어야 한다.

Long Polling: 클라이언트의 요청에 대해 응답을 보내지 않고 있다가 이벤트가 발생했을때 응답하는 방법

  • 폴링 방식보다 서버에 적은 부하를 줄 수 있지만, 요청의 주기가 짧으면 폴링과 차이가 없어진다.

Streaming: 클라이언트가 request를 보내면 커넥션을 맺고, 이 커넥션을 유지하면서 서버가 계속 데이터를 보내는 방법

  • 클라이언트가 서버에 요청을 하고 싶다면 새로운 커넥션을 맺어야 한다.

웹소켓의 동작

1. Upgrade 요청

WebSocket 프로토콜로 전환하는 HTTP 요청을 보낸다.
+이는 HTTP와 같이 80, 443 포트를 사용한다.
+웹소켓으로 전환하기 위해서는 Upgrade: websocket, Connection: Upgrade 헤더가 필요하다.
+Sec-WebSocket-Key는 서버에서 Sec-WebSocket-Accept를 계산하여 응답하고 이 값이 예상한 값과 다르면 연결이 수립되지 않는다.
+Sec-WebSocket-Protocol의 경우 서브프로토콜의 목록으로 서버 측에서는 해당 목록 중 하나를 선택하여 반환해야 한다.
+만약 서버측에서 여러 개 지원이 가능한 경우 지원 가능한 프로토콜 중 첫번째 프로토콜을 클라이언트측으로 보낸다.

GET /chats HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
Sec-WebSocket-Protocol: v10.stomp, v11.stomp
Sec-WebSocket-Version: 13
Origin: http://localhost:8080

2. Switching Protocols

서버는 101 Switching Protocols 응답을 반환한다.
+Sec-WebSocket-Accept은 Sec-WebSocket-Key 뒤에 258EAFA5-E914-47DA-95CA-C5AB0DC85B11를 붙이고 SHA1로 해싱 후 Base64로 인코딩하여 반환한다.
+이는 서버 웹소켓 프로토콜의 지원 여부를 클라이언트에게 명확히 알리기 위해 존재한다.

HTTP/1.1 101 Switching Protocols 
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=
Sec-WebSocket-Protocol: v10.stomp

3. 통신 후 종료

연결이 수립되면 웹소켓 프레임 단위로 양방향 통신을 한다.
+연결 종료를 원하는 경우 클라이언트, 서버 모두 연결 종료를 요청할 수 있다.

참고 자료

https://datatracker.ietf.org/doc/html/rfc6455
+https://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
+https://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
+https://docs.spring.io/spring-framework/reference/web/websocket.html

+ + \ No newline at end of file diff --git a/page/14.html b/page/14.html index 0ef208bff..211454741 100644 --- a/page/14.html +++ b/page/14.html @@ -13,23 +13,33 @@ - - + +
-

· 약 3분

23년의 6월이 오고, 레벨 2가 끝났다.
-빠르게 지나가서 조금 아쉽다.

학습

회고를 작성하기 전에 레벨 2 동안 보냈던 PR과 회고를 쭉 읽어봤다.
-항상 아쉬운 곳은 있기 마련이지만, 잘 학습한 것 같다.
-미션을 하면서 기술을 어떻게 선택하고, 적용할 것인지 고민하는 과정에서 꽤나 많은 성장을 한 것 같다.

고민은 깊었지만 이론적인 학습이 부족한 레벨 2였다.
-방학 그리고 레벨 3 때는 조금 더 이론적인 부분을 학습하는데 집중해야겠다.

점차 학습 범위가 넓어지면서 자연스럽게 모르는 내용이 쌓여간다.
-필요한 내용은 앞으로 천천히 학습하면 되니까 조급해지지 말아야겠다.

수면

레벨 2를 진행하는 동안 수면이 많이 부족했었고, 결과적으로는 그날의 컨디션을 많이 좌우했던 것 같다.
-앞으로 수면 시간을 늘리고, 좋은 수면 습관을 가지도록 노력해야겠다.

협업

레벨 2 마지막에 협업 미션이 있었다.
-지금까지는 백엔드 크루들과 페어 프로그래밍을 하면서 협업을 경험했다.
-이번에는 프런트엔드 크루와 협업을 했다. 소통은 잘 된 것 같지만 API 명세를 정하는 부분이 아직 미숙한 것 같다.

레벨 3 때부터 본격적으로 프로젝트가 시작된다.
-팀을 위해 어떤 것을 할 수 있을지 고민을 많이 해봐야겠다.

레벨 2를 마무리하며

회고 작성하면서 레벨 2에서 했던 것들을 반추해 봤는데 부족한 점은 많았어도 좋은 방향으로 가고 있는 것 같다. -읽고 싶은 책도 읽고, 부족한 부분 채우면서 쉬어야겠다.

- - +

· 약 11분

팀 블로그 또는 문서화를 위해 Docusaurus를 사용하는 방법을 정리하려고 한다.

설치

공식 홈페이지에 들어가서 최신 버전을 설치한다.

yarn create docusaurus

배포

배포 안내 문서
+netlify나 vercel 같은 서버리스 플랫폼을 추천하고 있고, 간단하고, 빠른 시간 안에 배포를 할 수 있다.
+이 글에서는 github pages를 이용해서 배포하는 방법을 설명한다.

레포지토리 생성

github pages를 이용하려면 예시와 같이 username.github.io 형태의 레포지토리를 생성해야 한다.
+이때 organization을 사용하는 경우 organization.github.io 형태의 레포지토리를 생성해서 사용한다.

설정 파일 수정

docusaurus.config
module.exports = {
// ...
url: 'https://greeng00se.github.io',
baseUrl: '/',
projectName: 'greeng00se.github.io',
organizationName: 'greeng00se',
trailingSlash: false,
// ...
};

토큰 설정

github action을 위해 배포용 토큰을 하나 생성하여 생성한 레포지토리에 Repository secrets으로 설정한다.
+이 글에서는 토큰을 클래식 방식으로 생성했고 스코프는 [repo, user, workflow] 을 설정했다.

github

브랜치 생성

github에서 gh-pages 브랜치를 하나 생성한다.
+repository -> settings -> pages -> branch에서 생성한 gh-pages로 브랜치를 변경한다.
+설정한 브랜치가 배포 브랜치가 되며, 해당 브랜치에 있는 파일들을 이용해서 정적 웹사이트를 제공한다.

워크플로 작성

Docusaurus 2.0 기준 Node.js 16.14 이상의 버전을 사용해야 합니다.
+배포시에는 Repository secrets으로 설정한 DEPLOY_TOKEN 을 이용합니다.

.github/workflows/deploy.yml
name: blog

on:
push:
branches: [main]

jobs:
deploy:
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 18
cache: yarn

- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build website
run: yarn build

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.DEPLOY_TOKEN }}
publish_dir: ./build
user_name: github-actions[bot]
user_email: 41898282+github-actions[bot]@users.noreply.github.com

댓글 기능

giscus를 이용하여 댓글 기능을 추가한다.

giscus 설정

  1. 공개 저장소여야 한다.
  2. giscus 앱이 설치되어 있어야 한다.
  3. Discussions 기능이 해당 저장소에서 활성화되어 있어야 한다.

자세한 내용은 giscus를 확인하자.

docusaurus 설정

swizzling을 이용하여 컴포넌트를 감싼다.
+기존에 게시물을 giscus가 포함된 리액트 컴포넌트로 감싸는 형태가 된다.
+아래 명령어를 이용하여 BlogPostItem을 추출할 수 있다.

yarn run swizzle @docusaurus/theme-classic BlogPostItem -- --wrap

명령어를 입력하면 /src/theme/BlogPostItem/index.js 위치에 파일이 생성된다.
+파일의 내용을 아래와 같이 수정하고, 이때 setAttribute 부분은 적절하게 자신의 giscus 설정을 이용한다.

/src/theme/BlogPostItem/index.js
import OriginalBlogPostItem from "@theme-original/BlogPostItem";
import React, { useEffect, useRef } from "react";
// @ts-expect-error internal code
import { useColorMode } from "@docusaurus/theme-common";
import { useBlogPost } from "@docusaurus/theme-common/internal";

const giscusSelector = "iframe.giscus-frame";

function BlogPostItem(props) {
const { colorMode } = useColorMode();
const { isBlogPostPage } = useBlogPost();
const giscusTheme = colorMode === "dark" ? "dark" : "light";
const containerRef = useRef(null);

useEffect(() => {
if (!isBlogPostPage) return;

const giscusEl = containerRef.current.querySelector(giscusSelector);

const createGiscusEl = () => {
const script = document.createElement("script");

script.src = "https://giscus.app/client.js";
script.setAttribute("data-repo", "teco-chat/teco-chat.github.io");
script.setAttribute("data-repo-id", "R_kgDOJZ5j0Q");
script.setAttribute("data-category", "Announcements");
script.setAttribute("data-category-id", "DIC_kwDOJZ5j0c4CXS_Q");
script.setAttribute("data-mapping", "pathname");
script.setAttribute("data-strict", "0");
script.setAttribute("data-reactions-enabled", "1");
script.setAttribute("data-emit-metadata", "0");
script.setAttribute("data-input-position", "bottom");
script.setAttribute("data-theme", giscusTheme);
script.setAttribute("data-lang", "ko");
script.crossOrigin = "anonymous";
script.async = true;

containerRef.current.appendChild(script);
};

const postThemeMessage = () => {
const message = {
setConfig: {
theme: giscusTheme,
}
};

giscusEl.contentWindow.postMessage({ giscus: message }, "https://giscus.app");
};

giscusEl ? postThemeMessage() : createGiscusEl();
}, [giscusTheme]);

return (
<>
<OriginalBlogPostItem {...props} />
{isBlogPostPage && <div ref={containerRef} />}
</>
);
}

export default BlogPostItem;

알고리아 설정 및 직접 관리하기

알고리아를 사용하면 검색 기능을 추가할 수 있다.
+유료 플랜이나 netlify를 사용하는 경우 크롤러를 따로 제공해 주는 것 같다.

무료 플랜은 직접 인덱스를 수집하는 방법과, docsearch를 이용하는 방법이 있다.
+docsearch에 등록한다면 일주일에 한 번씩 크롤링이 진행된다.
+이 글에서는 직접 인덱스를 수집하는 방법을 사용한다.

알고리아 애플리케이션 생성 및 키 확인

회원가입을 하고 새로운 애플리케이션 생성을 누른다.
+생성을 다 마치면 다음과 같이 api 키를 확인할 수 있다.

algolia

키 생성

직접 인덱스를 수집하기 위한 키를 생성한다.
+addObject, editSettings, deleteIndex acl(접근 제어 목록)이 있으면 된다.

key

.env 파일 생성

프로젝트 폴더 상단에 .env 파일을 생성한다.

.env
APPLICATION_ID=MVIU5UEMOM
API_KEY=인덱스_생성용_키

config 파일 생성

마찬가지로 최상단에 config.json 파일을 생성한다. +설정 파일은 해당 링크를 참고한다.
+또는 Docusaurus의 설정 파일을 참고한다.

config.json
{
"index_name": "teco",
"start_urls": [
"https://teco-chat.github.io/"
],
"sitemap_urls": [
"https://teco-chat.github.io/sitemap.xml"
],
"sitemap_alternate_links": true,
"stop_urls": [
"/tests"
],
"selectors": {
"lvl0": {
"selector": "(//ul[contains(@class,'menu__list')]//a[contains(@class, 'menu__link menu__link--sublist menu__link--active')]/text() | //nav[contains(@class, 'navbar')]//a[contains(@class, 'navbar__link--active')]/text())[last()]",
"type": "xpath",
"global": true,
"default_value": "Documentation"
},
"lvl1": "header h1",
"lvl2": "article h2",
"lvl3": "article h3",
"lvl4": "article h4",
"lvl5": "article h5, article td:first-child",
"lvl6": "article h6",
"text": "article p, article li, article td:last-child"
},
"strip_chars": " .,;:#",
"custom_settings": {
"separatorsToIndex": "_",
"attributesForFaceting": [
"language",
"version",
"type",
"docusaurus_tag"
],
"attributesToRetrieve": [
"hierarchy",
"content",
"anchor",
"url",
"url_without_anchor",
"type"
]
},
"conversation_id": [
"833762294"
],
"nb_hits": 46250
}

docker 이용하여 크롤링

docker와 jq가 필요하다.
+jq가 설치되어 있지 않으면 mac 기준 brew를 이용해서 설치할 수 있다.

brew install jq

다음 명령어를 이용하여 .env와 config.json을 이용하여 크롤링을 한다.

docker run -it --env-file=.env -e "CONFIG=$(cat ./config.json | jq -r tostring)" algolia/docsearch-scraper

docusaurus 설정

전에 확인한 APP ID, Search-Only API KEY, IndexName을 이용하여 docusaurus.config 파일에 설정한다.

docusaurus.config
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
...
algolia: {
appId: 'MVIU5UEMOM', // Application ID
apiKey: 'b68f378013817d9a190df88cdde226a0', // Search-Only API Key
indexName: 'teco', // config.json에 설정한 인덱스명
contextualSearch: true,
},
})

부가 설정

화면 상단 Github Icon

파일 최하단에 아래 css 구문을 추가한다.

/src/css/custom.css
.header-github-link:hover {
opacity: 0.6;
}

.header-github-link:before {
content: '';
width: 24px;
height: 24px;
display: flex;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}

html[data-theme='dark'] .header-github-link:before {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}

themeconfig -> navbar에 github link를 설정한다.

docusaurus.config
navbar: {
title: 'HELLO',
items: [
{
href: 'https://github.com/greeng00se',
position: 'right',
className: 'header-github-link',
'aria-label': 'GitHub repository',
},
],
},

코드블럭

java나 kotlin의 경우 기본적으로 하이라이팅을 지원해 주지 않는다.
+prism 설정을 아래와 같이 변경해 준다.

docusaurus.config
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
additionalLanguages: ['java', 'kotlin'],
}

mermaid

mermaid를 사용하려면 @docusaurus/theme-mermaid 를 설치해야 한다.

yarn add @docusaurus/theme-mermaid

설치 후 아래와 같이 설정을 추가한다.

docusaurus.config
const config = {
...
markdown: {
mermaid: true,
},
themes: [
'@docusaurus/theme-mermaid'
],
};

themeConfig에서 mermaid의 테마를 지정할 수 있다.

docusaurus.config
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
...
mermaid: {
theme: {
light: 'neutral',
dark: 'dark'
},
},
}),

국제화 설정

국제화 설정을 한다면 Older Entries 형태의 설명이 다음 페이지 로 변경된다.
+설정파일에서 i18n에 있는 로케일 설정을 ko로 변경하면 된다.

docusaurus.config
i18n: {
defaultLocale: "ko",
locales: ["ko"],
},

블로그 글 author

팀원 별로 문서를 관리한다면 다음과 같이 어떤 팀원이 글을 작성했는지 설정해야 한다.

author

authors.yml 파일을 이용하여 사용자에 대한 기본 설정을 할 수 있다.

/blog/authors.yml
herb:
name: 허브
title: Backend
url: https://github.com/greeng00se
image_url: https://github.com/greeng00se.png

mallang:
name: 말랑
title: Backend
url: https://github.com/shin-mallang
image_url: https://github.com/shin-mallang.png

블로그 글을 작성할 때 다음과 같이 authors에 넣어주기만 하면 된다.

---
slug: 1
title: Hello World
authors: [herb, mallang]
tags: [hello, docusaurus]
---

첫 번째 문서 내용
+ + \ No newline at end of file diff --git a/page/15.html b/page/15.html index 2e7f74eef..22ff4cf0b 100644 --- a/page/15.html +++ b/page/15.html @@ -13,25 +13,23 @@ - - + +
-

· 약 4분

레벨 인터뷰

레벨 1 때는 준비해둔 내용으로 인터뷰를 진행해서 그렇게 특별한 부분이 없었다.
-따라서 레벨 1 레벨 인터뷰 회고는 레벨 1 회고를 작성할 때 끼워넣었다.
-이번에는 범위도 제한되어 있어 어떻게 준비해야 할지 당황했고, 답변에도 부족한 부분이 많았었다.
-기억이 사라지기 전에 큰 문제 없이 답변한 내용을 제외하고, 기억 남는 것 위주로 작성해 보려고 한다.

API 문서 도구 선택

큰 문제 없이 답변을 했는데 앞으로도 팀 프로젝트를 하면서 도움 될 것 같은 내용이 있어서 남겨두려고 한다.
-백엔드 팀원이 함께 의사결정을 했고, 미션 기간이 짧은 만큼 팀 차원에서 비교적 학습하기 쉬운 Swagger를 선택했다.
-추가로 들어가는 시간 대비 하이 리턴이라고 생각했다고 답변했다.

팀 차원의 학습 비용을 언급해서, 다음과 같은 좋은 피드백을 받았다.

특히 팀으로 의사결정하는 과정을 공유해 준 점이 좋았고 기술적 의사결정 과정에서 팀의 학습비용을 고려한 점이 좋았음.
-앞으로도 학습 비용은 주요하게 고려해야 할 사항

PUT과 PATCH & 토큰과 세션

PUT과 PATCH 차이를 설명하는 부분에서는 PATCH를 사용할 때 페이로드가 적어진다는 내용을 빼먹고 답변을 했다.
-토큰과 세션의 경우 기술을 잘 모르는 사람에게 설명해달라는 제약조건이 추가되었다.

해당 내용을 답변하면서 기술적인 깊이가 많이 부족했다는 생각이 든다.
-실제로 레벨 2 때 이론적인 학습 시간이 매우 적었고, 집중력도 많이 부족했다.
-앞으로 어떻게 깊이를 채울지 고민을 할 수 있는 질문들이었다.

추가로 기술을 잘 모르는 사람에게 설명하는 가정을 두고 학습을 한다면 큰 도움이 될 거라는 피드백을 받았다.

그 외 개선할 점

인터뷰할 때 특유의 말버릇을 개선하기
-생각할 시간을 가졌을 때 "다시 말씀드려도 될까요?"라고 말하고 답변을 이어나가기
-기술적으로 깊이가 부족하다고 생각이 많이 들어서 조금 더 깊게 공부하고 정리하기
-이전에 공부했던거 되돌아 보는 시간 가지기

- - +

· 약 3분

23년의 6월이 오고, 레벨 2가 끝났다.
+빠르게 지나가서 조금 아쉽다.

학습

회고를 작성하기 전에 레벨 2 동안 보냈던 PR과 회고를 쭉 읽어봤다.
+항상 아쉬운 곳은 있기 마련이지만, 잘 학습한 것 같다.
+미션을 하면서 기술을 어떻게 선택하고, 적용할 것인지 고민하는 과정에서 꽤나 많은 성장을 한 것 같다.

고민은 깊었지만 이론적인 학습이 부족한 레벨 2였다.
+방학 그리고 레벨 3 때는 조금 더 이론적인 부분을 학습하는데 집중해야겠다.

점차 학습 범위가 넓어지면서 자연스럽게 모르는 내용이 쌓여간다.
+필요한 내용은 앞으로 천천히 학습하면 되니까 조급해지지 말아야겠다.

수면

레벨 2를 진행하는 동안 수면이 많이 부족했었고, 결과적으로는 그날의 컨디션을 많이 좌우했던 것 같다.
+앞으로 수면 시간을 늘리고, 좋은 수면 습관을 가지도록 노력해야겠다.

협업

레벨 2 마지막에 협업 미션이 있었다.
+지금까지는 백엔드 크루들과 페어 프로그래밍을 하면서 협업을 경험했다.
+이번에는 프런트엔드 크루와 협업을 했다. 소통은 잘 된 것 같지만 API 명세를 정하는 부분이 아직 미숙한 것 같다.

레벨 3 때부터 본격적으로 프로젝트가 시작된다.
+팀을 위해 어떤 것을 할 수 있을지 고민을 많이 해봐야겠다.

레벨 2를 마무리하며

회고 작성하면서 레벨 2에서 했던 것들을 반추해 봤는데 부족한 점은 많았어도 좋은 방향으로 가고 있는 것 같다. +읽고 싶은 책도 읽고, 부족한 부분 채우면서 쉬어야겠다.

+ + \ No newline at end of file diff --git a/page/16.html b/page/16.html index 81ddb38c6..93abe542f 100644 --- a/page/16.html +++ b/page/16.html @@ -13,29 +13,25 @@ - - + +
-

· 약 5분

장바구니 주문 미션

배포 및 협업을 할 수 있는 미션이었다.
-마코, 우가, 우코, 우스 그리고 나까지 합쳐서 5명이 한 팀이 되었다.

배포

이전 미션들과 달리 AWS를 이용해 배포를 해야 했다.
-각자 하나의 EC2 인스턴스를 제공받을 수 있었고, 팀 별로 DB를 위한 추가 인스턴스를 제공받았다.
-배포 스크립트를 작성하는 경험을 해볼 수 있었다.
-배포 스크립트에 시간을 많이 투자하진 않았고, 다음과 같이 간단하게 작성했다.

echo "Start Deploy Script"
REPOSITORY_NAME=/home/ubuntu/jwp-shopping-order
PROJECT_NAME=jwp-shopping-order

echo "Change Directory"
cd $REPOSITORY_NAME

echo "Git Pull"
git pull origin step2

echo "Build"
./gradlew bootJar

echo "Copy, Start Server"
mv ./build/libs/$PROJECT_NAME.jar .

PID=$(pgrep -f $PROJECT_NAME)

if [ -n $PID ]; then
kill -9 $PID
sleep 5
fi

nohup java -Dspring.profiles.active=prod -jar $PROJECT_NAME.jar 1>stdout.txt 2>err.txt &

협업

일단 우스랑 우코가 먼저 잠실로 와줘서 너무 감사했다.
-백엔드가 아닌 다른 크루들과 해보는 첫 협업이라 약간 두근거렸다.
-예상외로 대화가 잘 되어서, 빠르게 명세를 정할 수 있었다.

부족했던 부분

여러가지 방법에 대한 장단점을 고려해보기

백엔드와 테이블 명세나 쿠폰 구현에 대해서 이야기할 때 장단에 대해 많이 고려하지 못한 것 같다.
-조금 더 시간을 많이 들여서 장단점을 고려했다면 더 좋은 결과물이 나오지 않았을까?
-앞으로 선택의 순간에서 조금 더 시간을 들여보는 것도 좋을 것 같다.

새로 배운 부분

expose headers

웹 페이지에서 Location 헤더를 받을 수 없는 문제가 있었다.
-기본적으로 허용 목록에 존재하는 응답헤더만 반환한다는 것을 모르고 있었다.
-이를 expose headers 설정을 통해 해결할 수 있었다.
-nginx 설정에 다음과 같이 추가해 주었다.

add_header 'Access-Control-Expose-Headers' 'Location'

읽기 전용 트랜잭션

단순 조회 요청에 대한 성능을 향상시켜준다는 것이라고 간단히만 알고 있었다.
-이번에 코멘트가 달려서 조금 더 자세히 공부해 보기로 했다.
-Transactional(readOnly = true)를 사용하는 경우 다음과 같이 동작한다.

setReadOnly(true) 설정이 된 Connection으로 연결을 시도를 한다. 이 설정을 하는 경우 DB마다 다르게 동작한다.

  • h2의 Connection 구현체는 readOnly 설정을 무시하는 방향으로 구현되어 Transactional 적용되지 않는다.
  • MySQL 8.0(InnoDB 사용 시)의 경우 읽기 전용으로 알려진 트랜잭션의 경우 트랜잭션 ID를 설정하지 않기 때문에 조회 속도가 더 빨라진다.

ORM 프레임워크를 사용한다면 prepareTransactionalConnection를 호출한다고 한다.
-추가로 현업에서는 고가용성 내결함성 등을 위하여 클러스터를 구성하여 사용하는 경우가 많고, 이 경우 readOnly 설정이 되어있다면 읽기 전용 DB로 질의가 들어가서 부하 분산의 효과가 있다고 한다.

DAO에 @Transactional 적용

DAO에 트랜잭션을 보장해 보는 건 어떻겠냐고 리뷰가 달려서 고민을 많이 했다.
-Service 계층에 이미 트랜잭션을 보장해 주고 있기에 필요 없지 않을까 생각했었다.
-DAO를 다른 곳에서 사용하더라도 트랜잭션을 보장하기 위해(확장성 고려) @Transactional을 적용하는 것도 괜찮은 것 같다.

- - +

· 약 4분

레벨 인터뷰

레벨 1 때는 준비해둔 내용으로 인터뷰를 진행해서 그렇게 특별한 부분이 없었다.
+따라서 레벨 1 레벨 인터뷰 회고는 레벨 1 회고를 작성할 때 끼워넣었다.
+이번에는 범위도 제한되어 있어 어떻게 준비해야 할지 당황했고, 답변에도 부족한 부분이 많았었다.
+기억이 사라지기 전에 큰 문제 없이 답변한 내용을 제외하고, 기억 남는 것 위주로 작성해 보려고 한다.

API 문서 도구 선택

큰 문제 없이 답변을 했는데 앞으로도 팀 프로젝트를 하면서 도움 될 것 같은 내용이 있어서 남겨두려고 한다.
+백엔드 팀원이 함께 의사결정을 했고, 미션 기간이 짧은 만큼 팀 차원에서 비교적 학습하기 쉬운 Swagger를 선택했다.
+추가로 들어가는 시간 대비 하이 리턴이라고 생각했다고 답변했다.

팀 차원의 학습 비용을 언급해서, 다음과 같은 좋은 피드백을 받았다.

특히 팀으로 의사결정하는 과정을 공유해 준 점이 좋았고 기술적 의사결정 과정에서 팀의 학습비용을 고려한 점이 좋았음.
+앞으로도 학습 비용은 주요하게 고려해야 할 사항

PUT과 PATCH & 토큰과 세션

PUT과 PATCH 차이를 설명하는 부분에서는 PATCH를 사용할 때 페이로드가 적어진다는 내용을 빼먹고 답변을 했다.
+토큰과 세션의 경우 기술을 잘 모르는 사람에게 설명해달라는 제약조건이 추가되었다.

해당 내용을 답변하면서 기술적인 깊이가 많이 부족했다는 생각이 든다.
+실제로 레벨 2 때 이론적인 학습 시간이 매우 적었고, 집중력도 많이 부족했다.
+앞으로 어떻게 깊이를 채울지 고민을 할 수 있는 질문들이었다.

추가로 기술을 잘 모르는 사람에게 설명하는 가정을 두고 학습을 한다면 큰 도움이 될 거라는 피드백을 받았다.

그 외 개선할 점

인터뷰할 때 특유의 말버릇을 개선하기
+생각할 시간을 가졌을 때 "다시 말씀드려도 될까요?"라고 말하고 답변을 이어나가기
+기술적으로 깊이가 부족하다고 생각이 많이 들어서 조금 더 깊게 공부하고 정리하기
+이전에 공부했던거 되돌아 보는 시간 가지기

+ + \ No newline at end of file diff --git a/page/17.html b/page/17.html index 00f8138e9..629fcfb96 100644 --- a/page/17.html +++ b/page/17.html @@ -13,26 +13,29 @@ - - + +
-

· 약 5분

개요

원래 목적인 크루들의 학습에 도움을 주기 위해 어떤 기능을 추가해야 할지 고민을 많이 했다.
-레벨 2가 거의 끝나가는 시점, 그동안 했던 것을 정리해 보려고 한다.

나의 채팅 확인하고 이어하는 기능

GPT에도 있는 기능인데, 내가 이전에 했던 채팅을 이어할 수 있는 기능을 추가했다.
-예전에 어떤 질문을 남겼는지, 또한 해당 채팅을 이어서 할 수 있다.

chat1

좋아요와 댓글 기능

다른 사람들이 질문한 내용에 반응할 수 있는 무언가가 있었으면 좋겠다는 의견들이 많았다.
-누가 좋아요를 눌렀는지, 어떤 채팅이 좋아요를 가장 많이 받았는지 확인할 수 있는 기능을 추가했다.
-또한 댓글 추가 및 삭제 기능도 추가했다.

키워드 추출

어떻게 키워드 추출을 할지 고민을 많이 했는데, 일단 GPT를 이용해서 키워드를 추출하기로 했다.
-해당 부분은 첫 질문에 대한 키워드만 추출하도록 했다.
-백엔드에선 말랑이 이벤트 이용해서 첫 채팅 요청이 이루어지면, 비동기로 키워드를 추출하는 질문을 추가로 날리도록 구현하였다.
-CSV 형식으로 GPT에게 답변을 입력해달라고 요청받는데, 이 부분이 문제(프롬프트 엔지니어링 부분이 반환된다.)가 좀 있는 것 같아서 개선이 필요한 것 같다.

chat2

다른 크루의 채팅 복사해서 이어하는 기능

다른 크루들의 채팅을 읽다가 궁금한 점이 있다면 복사해서 바로 질문을 할 수 있는 기능을 추가했다.
-채팅이 복사된 후 바로 GPT와 대화를 할 수 있는 메인 화면으로 이동한다.

사용성 고려하기

chat3

위 화면은 회원가입 창이다.
-사실 가장 마음에 드는 부분이고, 회원가입(닉네임만 입력하지만)할 때 익명을 원하는 사람들의 고민을 도와주게 끔 음식, 과일, 과자 등의 요소들을 입력하도록 유도했다! -추가로 GPT의 답변이 오면 자동으로 화면을 스크롤 해주는 것과 같이 사용성을 개선해 보려고 노력했지만 쉽지 않았다.
-제일 하고 싶은 것은 실제 GPT를 사용하는 것처럼 stream/text 값을 처리하고 싶은데 이 부분은 방학 때 기회가 되면 도전해 봐야겠다.

향후 계획

실제 크루들이 사용해 주는 서비스를 직접 만들어보면서 사용자의 입장에서 고민도 하게 되는 것 같다.
-크루들이 직접 사용해 주니까 너무 고맙고, 한편으로는 신기하다.
-일단 방학 때 stream/text 관련된 부분 동작되도록 구현해보려고 하고, 그 외의 부분은 조금 더 고민해야될 것 같다.

- - +

· 약 5분

장바구니 주문 미션

배포 및 협업을 할 수 있는 미션이었다.
+마코, 우가, 우코, 우스 그리고 나까지 합쳐서 5명이 한 팀이 되었다.

배포

이전 미션들과 달리 AWS를 이용해 배포를 해야 했다.
+각자 하나의 EC2 인스턴스를 제공받을 수 있었고, 팀 별로 DB를 위한 추가 인스턴스를 제공받았다.
+배포 스크립트를 작성하는 경험을 해볼 수 있었다.
+배포 스크립트에 시간을 많이 투자하진 않았고, 다음과 같이 간단하게 작성했다.

echo "Start Deploy Script"
REPOSITORY_NAME=/home/ubuntu/jwp-shopping-order
PROJECT_NAME=jwp-shopping-order

echo "Change Directory"
cd $REPOSITORY_NAME

echo "Git Pull"
git pull origin step2

echo "Build"
./gradlew bootJar

echo "Copy, Start Server"
mv ./build/libs/$PROJECT_NAME.jar .

PID=$(pgrep -f $PROJECT_NAME)

if [ -n $PID ]; then
kill -9 $PID
sleep 5
fi

nohup java -Dspring.profiles.active=prod -jar $PROJECT_NAME.jar 1>stdout.txt 2>err.txt &

협업

일단 우스랑 우코가 먼저 잠실로 와줘서 너무 감사했다.
+백엔드가 아닌 다른 크루들과 해보는 첫 협업이라 약간 두근거렸다.
+예상외로 대화가 잘 되어서, 빠르게 명세를 정할 수 있었다.

부족했던 부분

여러가지 방법에 대한 장단점을 고려해보기

백엔드와 테이블 명세나 쿠폰 구현에 대해서 이야기할 때 장단에 대해 많이 고려하지 못한 것 같다.
+조금 더 시간을 많이 들여서 장단점을 고려했다면 더 좋은 결과물이 나오지 않았을까?
+앞으로 선택의 순간에서 조금 더 시간을 들여보는 것도 좋을 것 같다.

새로 배운 부분

expose headers

웹 페이지에서 Location 헤더를 받을 수 없는 문제가 있었다.
+기본적으로 허용 목록에 존재하는 응답헤더만 반환한다는 것을 모르고 있었다.
+이를 expose headers 설정을 통해 해결할 수 있었다.
+nginx 설정에 다음과 같이 추가해 주었다.

add_header 'Access-Control-Expose-Headers' 'Location'

읽기 전용 트랜잭션

단순 조회 요청에 대한 성능을 향상시켜준다는 것이라고 간단히만 알고 있었다.
+이번에 코멘트가 달려서 조금 더 자세히 공부해 보기로 했다.
+Transactional(readOnly = true)를 사용하는 경우 다음과 같이 동작한다.

setReadOnly(true) 설정이 된 Connection으로 연결을 시도를 한다. 이 설정을 하는 경우 DB마다 다르게 동작한다.

  • h2의 Connection 구현체는 readOnly 설정을 무시하는 방향으로 구현되어 Transactional 적용되지 않는다.
  • MySQL 8.0(InnoDB 사용 시)의 경우 읽기 전용으로 알려진 트랜잭션의 경우 트랜잭션 ID를 설정하지 않기 때문에 조회 속도가 더 빨라진다.

ORM 프레임워크를 사용한다면 prepareTransactionalConnection를 호출한다고 한다.
+추가로 현업에서는 고가용성 내결함성 등을 위하여 클러스터를 구성하여 사용하는 경우가 많고, 이 경우 readOnly 설정이 되어있다면 읽기 전용 DB로 질의가 들어가서 부하 분산의 효과가 있다고 한다.

DAO에 @Transactional 적용

DAO에 트랜잭션을 보장해 보는 건 어떻겠냐고 리뷰가 달려서 고민을 많이 했다.
+Service 계층에 이미 트랜잭션을 보장해 주고 있기에 필요 없지 않을까 생각했었다.
+DAO를 다른 곳에서 사용하더라도 트랜잭션을 보장하기 위해(확장성 고려) @Transactional을 적용하는 것도 괜찮은 것 같다.

+ + \ No newline at end of file diff --git a/page/18.html b/page/18.html index c515993b3..67e15047d 100644 --- a/page/18.html +++ b/page/18.html @@ -13,25 +13,26 @@ - - + +
-

· 약 5분

요구사항

지하철 미션에는 다음과 같은 요구사항이 있었다.

  • 거리별 추가 요금 정책
  • 노선별 추가 요금 정책
  • 연령별 요금 할인 정책

인터페이스 사용

요금 정책은 다음과 같이 인터페이스로 표현할 수 있다.
-요금을 계산하는 메서드는 최단 경로 계산의 결과, 사용자의 정보, 요금을 받아 요금을 계산한다.

public interface FarePolicy {
int calculate(Path path, Passenger passenger, int fare);
}

public class BaseFarePolicy implements FarePolicy { ... }
public class DistanceFarePolicy implements FarePolicy { ... }
public class AgeDiscountFarePolicy implements FarePolicy { ... }

composite1

모든 요금 정책을 포함하는 새로운 요금 정책 만들기

나머지 구현체를 모두 가지고 있는 하나의 구현체를 만들었다.
-이 또한 FarePolicy를 구현한 형태가 되고, 필드로는 나머지 구현체들을 가지고 있다.

public class SubwayFarePolicy implements FarePolicy {

private final List<FarePolicy> farePolicies;

public SubwayFarePolicy(final List<FarePolicy> farePolicies) {
this.farePolicies = farePolicies;
}

@Override
public int calculate(final Path path, final Passenger passenger, final int fare) {
int calculatedFare = fare;
for (FarePolicy farePolicy : farePolicies) {
calculatedFare = farePolicy.calculate(path, passenger, calculatedFare);
}
return calculatedFare;
}
}

따라서 그림으로 본다면 다음과 같은 구조가 된다.

composite2

정책의 순서

지하철 요구사항은 순서가 중요했다.
-금액의 총합을 구하고, 그 후에 할인 정책이 들어가야했다.
-따라서 자식들의 순서를 관리할 때 주의를 기울여야 했다.
-Configuration 클래스에 다음과 같이 순서를 직접 적용시켰다.

@Configuration
public class FareConfiguration {

@Bean
public FarePolicy farePolicy() {
return new SubwayFarePolicy(List.of(
new BaseFarePolicy(),
new DistanceFarePolicy(),
new AgeDiscountFarePolicy()
));
}
}

컴포지트 패턴이란?

composite3

GOF의 디자인 패턴 책에서는 컴포지트 패턴을 다음과 같이 설명하고 있다.

부분과 전체의 계층을 표현하기 위해 객체들을 모아 트리 구조로 구성합니다.
-사용자로 하여금 개별 객체와 복합 객체를 모두 동일하게 다룰 수 있도록 하는 패턴입니다.

컴포지트 패턴은 인터페이스를 구현한 개별 객체가 존재하고, 그 개별 객체들을 포함하는 하나의 구현체가 따로 존재하는 패턴이다.
-이 때 사용자는 개별 객체와 합성 객체(개별 객체들을 포함하고 있는)를 똑같이 사용할 수 있다.

컴포지트 패턴의 구성요소

Component

  • 집합 관계에 정의될 모든 객체에 대한 인터페이스
  • ex) 요금 정책(FarePolicy)

Leaf

  • 개별 객체, 객체 합성에 기본이 되는 객체의 행동
  • ex) 거리 별 요금 정책(DistanceFarePolicy)

Composite

  • 여러 개의 개발 객체를 포함하는 합성 객체
  • ex) 지하철 요금 정책(SubwayFarePolicy)

Client

  • 인터페이스를 사용하는 클라이언트

컴포지트 패턴의 사용과 주요 목표

부분 - 전체의 관계를 표현하고 싶을 때
-Client 기준으로 Composite와 Leaf의 차이를 알지 못해도 잘 사용할 수 있도록 해야될 때

패턴 사용시 주의해야할 부분

패턴은 공통으로 사용 가능한 역할, 책임, 협력의 템플릿이다.
-반복되는 문제를 효율적으로 해결할 수 있지만 패턴에 매몰되서는 안된다.
-패턴을 맹목적으로 사용해서는 안되고, 현재의 요구사항에 따라 패턴을 유동적으로 수정해가면서 적용하는 것이 좋다.
-항상 트레이드오프를 생각하자!

참고 자료

컴포지트 패턴, GoF의 디자인 패턴
-디자인 패턴과 프레임워크, 오브젝트

- - +

· 약 5분

개요

원래 목적인 크루들의 학습에 도움을 주기 위해 어떤 기능을 추가해야 할지 고민을 많이 했다.
+레벨 2가 거의 끝나가는 시점, 그동안 했던 것을 정리해 보려고 한다.

나의 채팅 확인하고 이어하는 기능

GPT에도 있는 기능인데, 내가 이전에 했던 채팅을 이어할 수 있는 기능을 추가했다.
+예전에 어떤 질문을 남겼는지, 또한 해당 채팅을 이어서 할 수 있다.

chat1

좋아요와 댓글 기능

다른 사람들이 질문한 내용에 반응할 수 있는 무언가가 있었으면 좋겠다는 의견들이 많았다.
+누가 좋아요를 눌렀는지, 어떤 채팅이 좋아요를 가장 많이 받았는지 확인할 수 있는 기능을 추가했다.
+또한 댓글 추가 및 삭제 기능도 추가했다.

키워드 추출

어떻게 키워드 추출을 할지 고민을 많이 했는데, 일단 GPT를 이용해서 키워드를 추출하기로 했다.
+해당 부분은 첫 질문에 대한 키워드만 추출하도록 했다.
+백엔드에선 말랑이 이벤트 이용해서 첫 채팅 요청이 이루어지면, 비동기로 키워드를 추출하는 질문을 추가로 날리도록 구현하였다.
+CSV 형식으로 GPT에게 답변을 입력해달라고 요청받는데, 이 부분이 문제(프롬프트 엔지니어링 부분이 반환된다.)가 좀 있는 것 같아서 개선이 필요한 것 같다.

chat2

다른 크루의 채팅 복사해서 이어하는 기능

다른 크루들의 채팅을 읽다가 궁금한 점이 있다면 복사해서 바로 질문을 할 수 있는 기능을 추가했다.
+채팅이 복사된 후 바로 GPT와 대화를 할 수 있는 메인 화면으로 이동한다.

사용성 고려하기

chat3

위 화면은 회원가입 창이다.
+사실 가장 마음에 드는 부분이고, 회원가입(닉네임만 입력하지만)할 때 익명을 원하는 사람들의 고민을 도와주게 끔 음식, 과일, 과자 등의 요소들을 입력하도록 유도했다! +추가로 GPT의 답변이 오면 자동으로 화면을 스크롤 해주는 것과 같이 사용성을 개선해 보려고 노력했지만 쉽지 않았다.
+제일 하고 싶은 것은 실제 GPT를 사용하는 것처럼 stream/text 값을 처리하고 싶은데 이 부분은 방학 때 기회가 되면 도전해 봐야겠다.

향후 계획

실제 크루들이 사용해 주는 서비스를 직접 만들어보면서 사용자의 입장에서 고민도 하게 되는 것 같다.
+크루들이 직접 사용해 주니까 너무 고맙고, 한편으로는 신기하다.
+일단 방학 때 stream/text 관련된 부분 동작되도록 구현해보려고 하고, 그 외의 부분은 조금 더 고민해야될 것 같다.

+ + \ No newline at end of file diff --git a/page/19.html b/page/19.html index 628a5290a..48f56ea11 100644 --- a/page/19.html +++ b/page/19.html @@ -13,33 +13,25 @@ - - + +
-

· 약 8분

지하철 미션

점점 일정이 많아지는 느낌이 들면서 회고가 늦어진다.
-지하철 미션은 밀리랑 페어를 진행했다.
-간단한 CRUD만 있던 이전 미션들과 달리, 조금 복잡한 도메인 요구사항이 있었다.
-이때 API, 테이블, 도메인 설계를 해야 했는데 어떤 것부터 해야 할지 고민을 많이 했다.
-API와 테이블 구조를 우리가 정할 수 있는 상황이었고, 도메인 로직이 복잡했기 때문에 도메인을 먼저 구현했다.

노선의 구간 추가 및 삭제

노선을 저장하는 방법에 대해서 밀리와 이야기를 나눴다.

  1. 구간을 데이터베이스에서 전부 제거하고 전부 추가하는 방법
  2. 변경된 요소만 데이터베이스에 반영하는 방법

페어 시간이 짧아서 더욱 간단한 1번을 선택했고, 시간 내 요구사항을 만족시키기 위해 더 간단하게 구현하는 방법을 선택하는 것도 좋은 트레이드오프였던 것 같다.
-추후 페어가 끝나고 리뷰어인 서브웨이가 일부분만 반영하는 것으로 개선해 보는 것도 좋을 것 같다고 코멘트를 남겨주셔서 추가 및 제거된 요소만 반영하도록 변경했다.

부족했던 부분

미션의 난이도가 올라간 만큼, 페어 할 땐 컨디션 관리도 잘하려고 노력하고 미션 할 때도 집중해서 잘 끝낸 것 같다.
-우아한테크코스를 진행하면서 알아야 하는 게 많아지면서 가끔 조바심을 가질 때가 있는 것 같은데, 조바심을 경계할 필요가 있을 것 같다.
-부족한 부분은 인정하고, 앞으로 나아가야겠다.

새로 학습한 부분

컴포지트 패턴으로 요금 정책 추상화

요금 정책은 기본요금 정책, 거리별 요금 정책, 연령별 할인 정책이 있었다.
-요금을 더하는 부분과, 할인하는 부분이 있어서 이 둘을 분리할까 생각했지만, 이 정도 크기의 애플리케이션에서는 오히려 분리하지 않고 하나로 합치는 게 더 좋다고 생각했다.
-또한 분리하지 않는다면 정책의 순서가 중요한데, 연령별 할인 정책을 마지막에 두어야 했기 때문에 책임 연쇄 패턴도 고려를 했지만 조금 더 간결해 보이는 컴포지트 패턴을 선택했다.

도메인에 특정 기술의 의존성을 분리

처음에 도메인 패키지에 jgrapht 라이브러리를 의존하고 있는 클래스를 두어서 도메인 패키지가 jgrapht와 강결합이 되어버렸다.
-따라서 도메인 패키지 내에는 경로 검색에 대한 인터페이스를 두고, 세부 구현은 도메인 패키지 외부로 분리했다.
-최대한 간결하게 구현한다고 생각을 해도, 이런 부분은 인터페이스를 두어 결합을 피하는 것이 좋을 것 같다.

컴포지트 패턴

컴포지트 패턴은 인터페이스를 구현한 개별 객체가 존재하고, 그 개별 객체들을 포함하는 하나의 구현체가 따로 존재하는 패턴이다.
-이때 사용자는 개별 객체와 합성 객체(개별 객체들을 포함하고 있는)를 똑같이 사용할 수 있다.

인수 테스트 작성

인수 테스트는 사용자 스토리 시나리오 기반 테스트다.
-브라운이 해주신 강의 + 유튜브에 있는 브라운의 강의를 보고 지하철 미션에 인수 테스트를 적용해 보았다.
-메서드, 변수명을 전부 한글로 작성했는데 전체적인 흐름을 알기 편하고 읽기도 좋았다.
-그리고 인수 테스트에 필요한 Steps를 만드는 과정이 너무 재밌었다.

결과는 아래와 같다.

@Nested
public class 노선을_전체_조회할_때 {

@Test
void 상행종점역_부터_하행종점역으로_정렬된_결과를_반환한다() {
// given
노선_생성_요청("2호선", "초록", 0);
노선에_구간이_존재하지_않을_때_초기_구간_생성_요청("2호선", "잠실", "잠실새내", 5);
구간_생성_요청("2호선", "잠실새내", "종합운동장", 오른쪽, 5);

노선_생성_요청("9호선", "고동", 0);
노선에_구간이_존재하지_않을_때_초기_구간_생성_요청("9호선", "봉은사", "종합운동장", 3);
구간_생성_요청("9호선", "종합운동장", "삼전", 오른쪽, 7);

// when
final var 조회_결과 = 노선_전체_조회_요청();

// then
요청_결과의_상태를_검증한다(조회_결과, 정상_요청);
노선_전체_조회_결과를_확인한다(
조회_결과,
노선_정보("2호선", "초록", 0, "잠실", "잠실새내", "종합운동장"),
노선_정보("9호선", "고동", 0, "봉은사", "종합운동장", "삼전")
);
}
}

페어에게 배울 부분

의견 조율하기

밀리가 필요한 부분에서 의견을 적극적으로 내줘서 진행이 수월했다.
-의사소통이 매우 잘 돼서 좋았고 덕분에 시간 내에 요구사항을 만족해 미션을 제출할 수 있었던 것 같다.

꼼꼼하게 코딩하기

밀리는 코딩을 엄청 꼼꼼하게 하는 것 같다.
-변수명, 메서드명을 중요하게 생각했고, 좋은 변수명을 잘 짓는 것 같다.
-또한 코딩할 때 내가 평소에 사용하는 코딩 컨벤션에 맞춰주는 것 같아서 페어 할 때 편했다!

편한 분위기

전체적으로 페어 할 때 편하게 진행했던 것 같다.
-일정도 그렇고, 페어 진행할 때도 그렇고 큰 문제가 없었던 것 같아서 좋았다.
-나는 과연 다른 사람들에게 편한 사람일까?

- - +

· 약 5분

요구사항

지하철 미션에는 다음과 같은 요구사항이 있었다.

  • 거리별 추가 요금 정책
  • 노선별 추가 요금 정책
  • 연령별 요금 할인 정책

인터페이스 사용

요금 정책은 다음과 같이 인터페이스로 표현할 수 있다.
+요금을 계산하는 메서드는 최단 경로 계산의 결과, 사용자의 정보, 요금을 받아 요금을 계산한다.

public interface FarePolicy {
int calculate(Path path, Passenger passenger, int fare);
}

public class BaseFarePolicy implements FarePolicy { ... }
public class DistanceFarePolicy implements FarePolicy { ... }
public class AgeDiscountFarePolicy implements FarePolicy { ... }

composite1

모든 요금 정책을 포함하는 새로운 요금 정책 만들기

나머지 구현체를 모두 가지고 있는 하나의 구현체를 만들었다.
+이 또한 FarePolicy를 구현한 형태가 되고, 필드로는 나머지 구현체들을 가지고 있다.

public class SubwayFarePolicy implements FarePolicy {

private final List<FarePolicy> farePolicies;

public SubwayFarePolicy(final List<FarePolicy> farePolicies) {
this.farePolicies = farePolicies;
}

@Override
public int calculate(final Path path, final Passenger passenger, final int fare) {
int calculatedFare = fare;
for (FarePolicy farePolicy : farePolicies) {
calculatedFare = farePolicy.calculate(path, passenger, calculatedFare);
}
return calculatedFare;
}
}

따라서 그림으로 본다면 다음과 같은 구조가 된다.

composite2

정책의 순서

지하철 요구사항은 순서가 중요했다.
+금액의 총합을 구하고, 그 후에 할인 정책이 들어가야했다.
+따라서 자식들의 순서를 관리할 때 주의를 기울여야 했다.
+Configuration 클래스에 다음과 같이 순서를 직접 적용시켰다.

@Configuration
public class FareConfiguration {

@Bean
public FarePolicy farePolicy() {
return new SubwayFarePolicy(List.of(
new BaseFarePolicy(),
new DistanceFarePolicy(),
new AgeDiscountFarePolicy()
));
}
}

컴포지트 패턴이란?

composite3

GOF의 디자인 패턴 책에서는 컴포지트 패턴을 다음과 같이 설명하고 있다.

부분과 전체의 계층을 표현하기 위해 객체들을 모아 트리 구조로 구성합니다.
+사용자로 하여금 개별 객체와 복합 객체를 모두 동일하게 다룰 수 있도록 하는 패턴입니다.

컴포지트 패턴은 인터페이스를 구현한 개별 객체가 존재하고, 그 개별 객체들을 포함하는 하나의 구현체가 따로 존재하는 패턴이다.
+이 때 사용자는 개별 객체와 합성 객체(개별 객체들을 포함하고 있는)를 똑같이 사용할 수 있다.

컴포지트 패턴의 구성요소

Component

  • 집합 관계에 정의될 모든 객체에 대한 인터페이스
  • ex) 요금 정책(FarePolicy)

Leaf

  • 개별 객체, 객체 합성에 기본이 되는 객체의 행동
  • ex) 거리 별 요금 정책(DistanceFarePolicy)

Composite

  • 여러 개의 개발 객체를 포함하는 합성 객체
  • ex) 지하철 요금 정책(SubwayFarePolicy)

Client

  • 인터페이스를 사용하는 클라이언트

컴포지트 패턴의 사용과 주요 목표

부분 - 전체의 관계를 표현하고 싶을 때
+Client 기준으로 Composite와 Leaf의 차이를 알지 못해도 잘 사용할 수 있도록 해야될 때

패턴 사용시 주의해야할 부분

패턴은 공통으로 사용 가능한 역할, 책임, 협력의 템플릿이다.
+반복되는 문제를 효율적으로 해결할 수 있지만 패턴에 매몰되서는 안된다.
+패턴을 맹목적으로 사용해서는 안되고, 현재의 요구사항에 따라 패턴을 유동적으로 수정해가면서 적용하는 것이 좋다.
+항상 트레이드오프를 생각하자!

참고 자료

컴포지트 패턴, GoF의 디자인 패턴
+디자인 패턴과 프레임워크, 오브젝트

+ + \ No newline at end of file diff --git a/page/2.html b/page/2.html index bfd044125..0bd542479 100644 --- a/page/2.html +++ b/page/2.html @@ -13,27 +13,41 @@ - - + +
-

· 약 6분

성능 테스트

API의 요청이 많은 상황에서 서버가 어떻게 동작하는지 확인하는 테스트

시스템에 부하가 걸리면 문제 상황이 발생할 수 있다.
-다양한 상황에 대비해서 성능 테스트를 해야한다.

./test.png

스모크 테스트(Smoke Test)

최소한의 부하를 주어 시스템이 정상적으로 동작하는지 확인하는 테스트

VU를 최소한으로 두고, 짧은 시간을 가지고 테스트한다.
-다른 테스트를 시작하기 전에 스모크 테스트를 함으로써 테스트 스크립트에 오류가 없는지 확인할 수 있고, 성능 지표가 정상적으로 수집, 모니터링 되고 있는지 확인할 수 있다.

가상 사용자(VU)

가상 사용자는 서버 애플리케이션에 대해 특정 테스트를 실행한다.
-이는 다른 가상 사용자와 독립적으로 실행되며, 여러 가상 사용자를 사용하여 동시 연결을 할 수 있다.
-스레드라고 생각하면 된다.

스파이크 테스트(Spike Test)

사용량이 급증하는 상황에서 시스템이 견디고 성능에 문제가 없는지 확인하는 테스트

티켓 발급, 할인 쿠폰 발급과 같은 이벤트를 하는 경우 대규모 트래픽이 들어온다.
-스파이크 테스트를 통해 급증하는 부하 상황에서 시스템이 어떻게 동작하고, 부하를 잘 버티는지 확인할 수 있다.

부하 테스트(Load Test)

목푯값에 해당되는 부하를 견딜 수 있을지 확인하는 테스트

일반적인 부하 상황에서 시스템이 어떻게 동작하는지 확인하는 테스트다.
-램프업 또는 묙푯값에 해당하는 부하 기간동안 성능이 문제가 있는지 확인하고, 시스템 변경 후에도 부하 테스트를 돌려 동일하게 목푯값을 처리하는지 확인할 수 있다.

램프 업(Ramp-up)

부하 테스트를 위해 설정한 가상 사용자 수에 도달하는 데 걸리는 시간

스트레스 테스트(Stress Test)

시스템의 최대치에 해당되는 부하를 받았을 때 시스템이 어떻게 동작하는지 확인하는 테스트

그래프를 봤을 때 부하 테스트와 유사한 형태로 보이지만, 부하량이 다르다.
-일반적으로 평균적인 목푯값 대비 작게는 50% 이상, 필요의 경우 그 이상으로 부하를 준다.
-스트레스 테스트는 부하 테스트를 실행한 후에만 실행해야 한다. 부하 테스트가 이루어지지 않은 상황에서 스트레스 테스트를 실행하는 경우에는 병목 지점이나 문제 상황을 찾기 어려워진다.
-또한 부하 테스트에서 사용한 스크립트를 VU값(스레드 수)만 수정하여 재사용하는 것이 좋다.

내구 테스트(Endurance Test)

평균 사용률로 일정 부하를 지속적으로 주며 시스템이 문제되는 지점을 확인하는 테스트

흡수 테스트(Soak Test)라고도 하며, 기본적인 부하 테스트의 변형이라고 볼 수 있다.
-다른 테스트와 달리 긴 시간동안 테스트를 하는 것이 특징이며, 메모리 누수 문제와 같이 장시간 애플리케이션을 실행할 때 시스템의 문제가 발생하는 부분을 확인하는 것이 목적이다.

중단점 테스트(Breakpoint Test)

임계 지점을 찾기 위해 부하를 점진적으로 증가시키며 진행하는 테스트

문제되는 부분을 더 빨리 찾기 위해 다른 테스트를 통과한 다음에 중단점 테스트를 진행하고, 이 때 점진적으로 부하를 늘려나가는 것이 좋다.
-스트레스 테스트를 성능 튜닝과 반복해서 진행한다면, 시스템을 더욱 발전시킬 수 있다.
-다만 Auto Scaling이 적용된 클라우드 환경에서는 진행하지 않아야 한다.

참고 자료

Load test types, k6
-자바 최적화 - 벤저민 J. 에번스, 제임스 고프, 크리스 뉴랜드
-아마존 웹 서비스 부하 테스트 입문 - 나카가와 타루하치, 모리시타 켄

- - +

· 약 13분

톰캣 구현

우아한테크코스를 지원할 때 객체지향과 관련된 미션도 기대를 많이 했지만 레벨 4에 진행하는 미션이 정말 하고 싶었다.
+그래서 미션을 할 수 있을까라는 걱정 반, 미션에 대한 기대 반으로 부푼 마음을 가지고 미션을 시작했던 것 같다.

이번 미션에서는 적절하게 추상화하고, 미션의 본질을 이해하려고 노력했다.
+톰캣 구현 미션은 RFC 2616에 명시된 스펙(완벽하지 않지만 미션에서 주어진 요구사항만 만족하도록)으로 요청을 받아 처리 후 반환하는데 집중했다.

다이어그램

Catalina는 Tomcat의 서블릿 컨테이너, Coyote는 HTTP 1.1 웹 서버를 지원하는 구성 요소라고 생각하고 아래와 같이 구성했다.
+사실 내부 구조를 깊게 공부할 시간을 가지지 못해서 각 구성 요소가 왜 해당 위치에 있는지 완벽하게 설명하지는 못하지만 미션을 진행하면서 이건 여기에 있으면 좋을 것 같은데? 라는 생각이 들면 적절한 패키지에 위치시키는 방향으로 진행을 했다.
+또한 적절하게 인터페이스를 사용하여 의존성 방향을 단방향으로 하려고 노력했다.

코드 리뷰

크루 중 한 명이 나의 리뷰어가 되고, 내가 다른 크루의 리뷰어가 되는 형태로 진행이 되었다.
+나의 리뷰어는 디노, 리뷰이는 필립이었다.

디노(매의 눈이 아닌 공룡의 눈?)가 매우 꼼꼼하게 코드 리뷰를 해주어서 조금 더 나은 코드를 작성할 수 있었고, 필립의 코드에서는 꼼꼼하게 예외처리 하는 부분을 배울 수 있었다.
+한 가지 아쉬운 점은 필립에게 작성한 나의 코멘트들이 미션을 진행하면서 경험 기반으로 작성한 내용이 많아 근거가 조금 부족했고, 정리되지 않은 부분이 많았던 것 같다.
+다음 미션부터 리뷰할 때 조금 더 시간을 투자해서 더 좋은 내용을 크루들과 공유할 수 있도록 노력해야겠다.

SessionConfig

미션을 진행 중 catalina 패키지의 Session 관련 부분을 보면서 중복 로직을 개선해 볼 수 있을 것 같아 컨트리뷰트를 시도했다.
+세션 쿠키의 이름을 가져오는 Util 클래스의 코드를 수정했는데 기본 값은 JSESSIONID 지만 설정에 따라서 세션 쿠키명을 다르게 사용할 수 있기 때문에 해당 로직이 있는 것으로 생각했다.
+기존의 코드는 명시된 주석의 내용과 코드의 흐름이 일치하지 않아서 약간 이해하기 어려웠다.

초기에 요청했던 PR은 기존의 코드보다 전체적으로 비교 연산을 한 번 줄일 수 있었고, context가 null인 경우 바로 기본 값을 반환함으로써 성능 개선의 효과가 있을 거라고 생각했다.
+메인테이너인 Mark Thomas 형이 해당 로직의 경우 컴파일러가 해당 부분을 최적화 할 수 있을 거라고 기대한다고 했고, 가독성을 개선시켜보라고 조언해주셨다.
+컴파일러 최적화는 고려해보지 못한 부분인데, 앞으로 학습해야 할 부분이 산더미라고 생각했다.

남겨준 코멘트에 따라 최종적으로는 중복된 코드를 줄이는 방향으로 코드를 수정했다.
+결과적으로 기존 로직 대비 비교 연산을 한 번 줄일 수 있었고, 명시된 주석의 내용과 유사한 흐름의 코드를 작성하여 좋은 방향으로 리팩터링을 했다고 생각한다.

public static String getSessionCookieName(Context context) {

String result = getConfiguredSessionCookieName(context);

if (result == null) {
result = DEFAULT_SESSION_COOKIE_NAME;
}

return result;
}

public static String getSessionUriParamName(Context context) {

String result = getConfiguredSessionCookieName(context);

if (result == null) {
result = DEFAULT_SESSION_PARAMETER_NAME;
}

return result;
}

private static String getConfiguredSessionCookieName(Context context) {

// Priority is:
// 1. Cookie name defined in context
// 2. Cookie name configured for app
// 3. Default defined by spec
if (context != null) {
String cookieName = context.getSessionCookieName();
if (cookieName != null && cookieName.length() > 0) {
return cookieName;
}

SessionCookieConfig scc =
context.getServletContext().getSessionCookieConfig();
cookieName = scc.getName();
if (cookieName != null && cookieName.length() > 0) {
return cookieName;
}
}

return null;
}

HTTP 수업

미션 중간에 진행되었던 HTTP 수업에는 HTTP를 적절하게 활용하는 부분에 대해 학습했다.
+항상 성능 개선을 위해 애플리케이션 단에서 최적화해보려고 노력을 했지만, 더 적은 시간을 투자해서 효율적으로 성능을 개선할 수 있는 방법에 대해 알 수 있었던 수업이었다.
+HTTP 압축, HTTP 캐싱, 리소스 최적화 기법에 대해 학습했다.

스프링 부트에서는 다음 옵션을 설정하여 http의 송수신의 압축을 진행할 수 있다.

server:
compression:
enabled: true

수업 중 해당 압축 성능이 좋다면 왜 스프링 부트에서는 기본 값으로 설정을 하지 않았는지 궁금해졌다.
+궁금증을 해소하지 못했는데 말랑이 잡담 채널에 다음과 같은 issue를 찾아주었다.
+내용을 요약해 보자면 WAS 별로 압축을 하기 위해 설정해야 하는 것이 다르고, 무조건 압축을 하는 것이 최적의 경우가 아닐 수 있기 때문에 기본값으로 설정하지 않는 것 같다.

If you're developing a public-facing application then it's probably likely gzip compression would be worthwhile. If, however, you're a microservice application and you're in a dataceter, you may well prefer to reduce CPU load because you know you'll only be talking to other microservices and you have a reliable gigabit network.

Phil Webb 형님의 말에 따르면 일반적인 애플리케이션을 개발하는 경우 gzip 압축을 사용하는 것이 좋지만, MSA 환경 + 데이터 센터에서 사용하는 경우 오직 다른 MSA 애플리케이션과 통신하고, 고성능 네트워크가 있기 때문에 CPU 부하를 줄이는 것이 우선시 될 수도 있다는 것이었다.

이외에도 의도하지 않은 캐싱을 막기 위해 휴리스틱 캐싱을 제거하거나, 개인 정보 유출을 막기 위해 응답 헤더에 private을 설정, ETag도 학습했다.

ETag

ETag HTTP 응답 헤더는 특정 버전의 리소스를 식별하는 식별자다.
+웹 서버가 내용을 확인하고 변하지 않았으면, 웹 서버로 full 요청을 보내지 않기 때문에, 캐시가 더 효율적이게 된다.
+MDN

Thread 수업

스레드에 대한 수업을 들었지만, 복잡한 내용도 워낙 많았기 때문에 설명하라고 하면 잘 못할 것 같은 느낌이 든다.
+현재 프로젝트, 미션, 테코톡 준비를 병행해야 해서 세부적인 내용은 시간 날 때 복습하려고 한다.

스레드를 이해하고, WAS에 스레드 설정 관련한 실습이 있었는데 테오와 같이 1시간 정도 페어로 Thread 실습을 진행해 보았다.
+학습한 내용은 다음과 같다.

threads.max: Tomcat의 최대 스레드 개수
+max-connections: Tomcat이 유지할 수 있는 최대 커넥션 개수
+accept-count: 최대 연결 수에 도달했을 때 연결 요청에 대해 운영 체제에서 제공하는 대기열의 최대 길이. 해당 Queue에 요청이 쌓이는 것은 Tomcat이 더 이상 요청을 받을 수 없다는 뜻이다. accpet-count queue에도 요청이 가득차면 그 이후에 오는 요청은 거부된다.

마치며

시간은 너무 빠르게 가고 할 일은 많은 것 같다.
+우선순위를 잘 정하고 학습을 진행해야겠다.
+현재 데이터 다루는 부분(DB)에 대한 학습이 많이 부족한 것 같다. 해당 부분은 테코톡이 끝나는대로 채워야겠다.

참고 자료

RFC 2616
+ETag, mdn
+Apache Tomcat 8 Configuration Reference
+Apache Tomcat Tuning, Terry Cho
+maxThreads, maxConnections, acceptCount로 Tomcat 튜닝하기

+ + \ No newline at end of file diff --git a/page/20.html b/page/20.html index 3257e2e72..e343361fc 100644 --- a/page/20.html +++ b/page/20.html @@ -13,26 +13,33 @@ - - + +
-

· 약 8분

장바구니 미션에서는 상품 추가와 상품 수정에 대한 요구사항이 있었다.
-요청에 담긴 Body를 통해 전달받은 값을 DTO로 매핑하여 추가와 수정을 했다.

장바구니 미션에서의 상품 추가 및 수정

중복1

클래스명을 제외하고 필드와 검증로직 그 외 모든게 같은 DTO를 보며 중복이라고 생각했다.
-하지만 반대로 용도가 다르기 때문에 중복이 아니라고 생각하기도 했다.
-위 경우는 중복일까? 중복이 아닐까?

이 부분에 대해서 다음과 같은 리뷰를 받았다.

ProductSaveRequestProductUpdateRequest가 완전히 동일한데, 재사용할 수 없을까? 라는 리뷰를 남겼었어요. 사실 생성과 수정은 서로 달라질 개연성이 높아서 미리 분리해놓는 게 더 좋은 방법이긴 한데, 그래도 중복은 싫어서 저도 요즘 이런저런 방법들을 시도해보는 중 입니다. 허브는 이 부분에 대해 어떤 생각을 가지고 있을지 궁금하네요 ㅎㅎ

질문에 대해 아래와 같이 답변을 했다.

저장과 수정할 때 필요한 필드값이 동일하여 현재 구조에서는 하나로 사용해도 된다고 생각을 하지만, 말씀해주신대로 요구사항이 변경된다면 달라질 가능성이 높다고 판단하였습니다!

중복과 우발적 중복

로버트 마틴님이 집필하신 클린 아키텍처는 아래와 같이 중복을 여러가지 종류로 나누어 설명하고 있다.

  • 진짜 중복: 한 인스턴스가 변경되면, 동일한 변경을 그 인스턴스의 모드 복사본에 반드시 적용해야 한다.
  • 거짓된 중복, 우발적 중복: 중복으로 보이는 두 코드 영역이 각자의 경로로 발전한다면, 즉 서로 다른 속도와 다른 이유로 변경된다면 이 두 코드는 진짜 중복이 아니다.

추가와 수정은 초기에는 중복으로 보이지만 초기 생성시에만 기입하는 데이터들이 추가되거나, 시간이 지나면서 서로 달라질 가능성이 높아진다. -그렇기 때문에 위 상황은 우발적 중복으로 보인다. 그래도 중복을 제거해볼 수 있지 않을까?

하나로 사용하는 건 안좋아보이고, 중복은 제거하고 싶은 마음

지금은 추가, 수정 2가지 경우 밖에 없지만 조금 더 복잡한 요구사항이 주어져서 10가지 경우로 입력을 받으면 어떻게 해야할까?
-서비스 계층에서도 계층의 분리를 위해서 다른 DTO를 사용하고 있다면 20개의 DTO를 만들어야 할까?
-리뷰어가 알려준 의존 역전을 이용한 방법을 통해 이를 해결해보자!

중복 제거 전 코드

현재 코드에서는 아래와 같은 구조로 되어있다.
-Controller와 Service에서 저장, 수정할 때 각각의 DTO를 사용하고 있다. -현재 DTO는 controller, service 패키지 내에 있는 것이 아니라 dto라는 패키지에 위치하고 있다.

├── controller
│   └── ProductController
├── service
│   └── ProductService
├── dto
│   ├── ProductSaveRequest
│   └── ProductUpdateRequest

중복2

인터페이스 작성하기

중복3

서비스 레이어에서 필요로 하는 값들을 인터페이스로 정의한다.
-해당 인터페이스는 서비스에서 사용하기 때문에 service 패키지 내부로 옮겨준다.

├── controller
│   └── ProductController
├── service
│   ├── ProductService
│   ├── ProductSaveRequest
│   └── ProductUpdateRequest
public interface ProductSaveRequest {

String getName();

String getImage();

Long getPrice();
}

// ProductService
public Long save(final ProductSaveRequest request) {
final Product product = new Product(request.getName(), request.getImage(), request.getPrice());
return productDao.saveAndGetId(product);
}

구현체 작성하기

중복4

위에서 작성한 인터페이스를 구현하는 클래스를 작성한다.
-요청은 ProductRequest 클래스로 받고, 서비스에 전달할 땐 해당 인터페이스의 명세만 맞추면 문제없이 사용할 수 있다.

├── controller
│   ├── ProductController
│   └── ProductRequest
├── service
│   ├── ProductService
│   ├── ProductSaveRequest
│   └── ProductUpdateRequest
public class ProductRequest implements ProductSaveRequest, ProductUpdateRequest {

@NotBlank(message = "이름은 공백일 수 없습니다.")
@Size(min = 1, max = 100, message = "이름은 최소 {min}자 이상, {max}자 이하여야 합니다.")
private final String name;

@NotBlank(message = "이미지는 공백일 수 없습니다.")
private final String image;

@Range(message = "가격은 최소 {min}원 이상, {max}원 이하여야 합니다.")
private final long price;

public ProductRequest(final String name, final String image, final long price) {
this.name = name;
this.image = image;
this.price = price;
}

@Override
public String getName() {
return name;
}

@Override
public String getImage() {
return image;
}

@Override
public long getPrice() {
return price;
}
}

// ProductController
@PostMapping("/products")
public ResponseEntity<Void> save(@Valid @RequestBody final ProductRequest request) {
final Long id = productService.save(request);
return ResponseEntity.created(URI.create("/products/" + id)).build();
}

정리

위와 같이 구현한다면 다음과 같은 장점을 얻을 수 있다.

  1. Service에서 모든 클라이언트 요청에 대한 DTO를 알지 않아도 된다.
  2. 공통적으로 사용하는 DTO를 제외하고 DTO 패키지에 대한 결합도가 낮아지고, 각 레이어의 응집도가 증가한다.
  3. 요청 객체만 다르고 서비스에서 동일한 행위를 수행하는 경우 중복을 제거할 수 있다.

위 방법을 지금 미션에서 바로 적용할까 하다가, 나중에 필요할 때 적용하면 더 좋을 것 같아서 미션에는 적용하지 않았다.
-상황에 맞춰 적재적소에 의존 역전을 이용해보는 것도 좋을 것 같다.

참고 자료

클린 아키텍처 16장 독립성, 로버트 C. 마틴
-https://techblog.woowahan.com/2647/
-https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/

- - +

· 약 8분

지하철 미션

점점 일정이 많아지는 느낌이 들면서 회고가 늦어진다.
+지하철 미션은 밀리랑 페어를 진행했다.
+간단한 CRUD만 있던 이전 미션들과 달리, 조금 복잡한 도메인 요구사항이 있었다.
+이때 API, 테이블, 도메인 설계를 해야 했는데 어떤 것부터 해야 할지 고민을 많이 했다.
+API와 테이블 구조를 우리가 정할 수 있는 상황이었고, 도메인 로직이 복잡했기 때문에 도메인을 먼저 구현했다.

노선의 구간 추가 및 삭제

노선을 저장하는 방법에 대해서 밀리와 이야기를 나눴다.

  1. 구간을 데이터베이스에서 전부 제거하고 전부 추가하는 방법
  2. 변경된 요소만 데이터베이스에 반영하는 방법

페어 시간이 짧아서 더욱 간단한 1번을 선택했고, 시간 내 요구사항을 만족시키기 위해 더 간단하게 구현하는 방법을 선택하는 것도 좋은 트레이드오프였던 것 같다.
+추후 페어가 끝나고 리뷰어인 서브웨이가 일부분만 반영하는 것으로 개선해 보는 것도 좋을 것 같다고 코멘트를 남겨주셔서 추가 및 제거된 요소만 반영하도록 변경했다.

부족했던 부분

미션의 난이도가 올라간 만큼, 페어 할 땐 컨디션 관리도 잘하려고 노력하고 미션 할 때도 집중해서 잘 끝낸 것 같다.
+우아한테크코스를 진행하면서 알아야 하는 게 많아지면서 가끔 조바심을 가질 때가 있는 것 같은데, 조바심을 경계할 필요가 있을 것 같다.
+부족한 부분은 인정하고, 앞으로 나아가야겠다.

새로 학습한 부분

컴포지트 패턴으로 요금 정책 추상화

요금 정책은 기본요금 정책, 거리별 요금 정책, 연령별 할인 정책이 있었다.
+요금을 더하는 부분과, 할인하는 부분이 있어서 이 둘을 분리할까 생각했지만, 이 정도 크기의 애플리케이션에서는 오히려 분리하지 않고 하나로 합치는 게 더 좋다고 생각했다.
+또한 분리하지 않는다면 정책의 순서가 중요한데, 연령별 할인 정책을 마지막에 두어야 했기 때문에 책임 연쇄 패턴도 고려를 했지만 조금 더 간결해 보이는 컴포지트 패턴을 선택했다.

도메인에 특정 기술의 의존성을 분리

처음에 도메인 패키지에 jgrapht 라이브러리를 의존하고 있는 클래스를 두어서 도메인 패키지가 jgrapht와 강결합이 되어버렸다.
+따라서 도메인 패키지 내에는 경로 검색에 대한 인터페이스를 두고, 세부 구현은 도메인 패키지 외부로 분리했다.
+최대한 간결하게 구현한다고 생각을 해도, 이런 부분은 인터페이스를 두어 결합을 피하는 것이 좋을 것 같다.

컴포지트 패턴

컴포지트 패턴은 인터페이스를 구현한 개별 객체가 존재하고, 그 개별 객체들을 포함하는 하나의 구현체가 따로 존재하는 패턴이다.
+이때 사용자는 개별 객체와 합성 객체(개별 객체들을 포함하고 있는)를 똑같이 사용할 수 있다.

인수 테스트 작성

인수 테스트는 사용자 스토리 시나리오 기반 테스트다.
+브라운이 해주신 강의 + 유튜브에 있는 브라운의 강의를 보고 지하철 미션에 인수 테스트를 적용해 보았다.
+메서드, 변수명을 전부 한글로 작성했는데 전체적인 흐름을 알기 편하고 읽기도 좋았다.
+그리고 인수 테스트에 필요한 Steps를 만드는 과정이 너무 재밌었다.

결과는 아래와 같다.

@Nested
public class 노선을_전체_조회할_때 {

@Test
void 상행종점역_부터_하행종점역으로_정렬된_결과를_반환한다() {
// given
노선_생성_요청("2호선", "초록", 0);
노선에_구간이_존재하지_않을_때_초기_구간_생성_요청("2호선", "잠실", "잠실새내", 5);
구간_생성_요청("2호선", "잠실새내", "종합운동장", 오른쪽, 5);

노선_생성_요청("9호선", "고동", 0);
노선에_구간이_존재하지_않을_때_초기_구간_생성_요청("9호선", "봉은사", "종합운동장", 3);
구간_생성_요청("9호선", "종합운동장", "삼전", 오른쪽, 7);

// when
final var 조회_결과 = 노선_전체_조회_요청();

// then
요청_결과의_상태를_검증한다(조회_결과, 정상_요청);
노선_전체_조회_결과를_확인한다(
조회_결과,
노선_정보("2호선", "초록", 0, "잠실", "잠실새내", "종합운동장"),
노선_정보("9호선", "고동", 0, "봉은사", "종합운동장", "삼전")
);
}
}

페어에게 배울 부분

의견 조율하기

밀리가 필요한 부분에서 의견을 적극적으로 내줘서 진행이 수월했다.
+의사소통이 매우 잘 돼서 좋았고 덕분에 시간 내에 요구사항을 만족해 미션을 제출할 수 있었던 것 같다.

꼼꼼하게 코딩하기

밀리는 코딩을 엄청 꼼꼼하게 하는 것 같다.
+변수명, 메서드명을 중요하게 생각했고, 좋은 변수명을 잘 짓는 것 같다.
+또한 코딩할 때 내가 평소에 사용하는 코딩 컨벤션에 맞춰주는 것 같아서 페어 할 때 편했다!

편한 분위기

전체적으로 페어 할 때 편하게 진행했던 것 같다.
+일정도 그렇고, 페어 진행할 때도 그렇고 큰 문제가 없었던 것 같아서 좋았다.
+나는 과연 다른 사람들에게 편한 사람일까?

+ + \ No newline at end of file diff --git a/page/21.html b/page/21.html index 88d04af9b..c204dc4a5 100644 --- a/page/21.html +++ b/page/21.html @@ -13,26 +13,26 @@ - - + +
-

· 약 5분

웹 장바구니 미션

장바구니 미션은 블랙캣이랑 진행했다.
-요구사항이 엄청 복잡한 미션은 아니었고, 스프링을 사용하여 기본적인 CRUD를 구현하는 미션이었다.
-2단계에서는 Basic 인증을 통해 자신의 장바구니에만 상품을 담고, 제거할 수 있도록 구현하는 요구사항이 추가되었다.
-Interceptor나 Argument Resolver에 대한 이해도가 높지 않았는데, 이번 미션을 통해 조금 더 알아간 느낌이다.
-이전에 스프링 사용할 때는 아무 생각 없이 코드를 작성하는 경우가 많았는데, 코드를 작성할 때 근거가 생기고 있는 것 같다.

새로 학습한 부분

DTO 우발적 중복

장바구니 미션에서는 상품 추가와 상품 수정에 대한 요구사항이 있었다.

dto1

클래스명을 제외하고 필드와 검증 로직 그 외 모든 게 같은 DTO를 보며 중복이라고 생각을 했고, 반대로 용도가 다르기 때문에 중복이 아니라고 생각하기도 했다.
-로버트 마틴님이 집필하신 클린 아키텍처는 아래와 같이 중복을 여러 가지 종류로 나누어 설명하고 있다.

  • 진짜 중복: 한 인스턴스가 변경되면, 동일한 변경을 그 인스턴스의 모드 복사본에 반드시 적용해야 한다.
  • 우발적 중복: 중복으로 보이는 두 코드 영역이 각자의 경로로 발전한다면, 즉 서로 다른 속도와 다른 이유로 변경된다면 이 두 코드는 진짜 중복이 아니다.

추가와 수정은 초기에는 중복으로 보이지만 초기 생성 시에만 기입하는 데이터들이 추가되거나, 시간이 지나면서 서로 달라질 가능성이 높아진다.
-따라서 리뷰어 웨지가 아래와 같이 의존 역전을 이용하는 방법도 있다고 알려주셨다.

dto2

Interceptor에서 인증한 값 재사용

사실 조회를 두 번 하기 싫어서 다양한 방법을 생각했었는데 이번 미션에서는 ThreadLocal을 사용했다.
-일단 Tomcat은 요청마다 다른 스레드를 사용하고, Interceptor에서 조회해서 만든 Credential을 ThreadLocal에 넣어두었다가 ArgumentResolver에서 꺼낸 다음 ThreadLocal을 clear 하면 문제가 없을 거라 판단했다.

리뷰어인 웨지에게도 어떤 방법을 사용할지 궁금증을 작성했었다.
-웨지는 email에 index를 걸어두고 dao 재조회를 사용할 것이라고 했다.
-재사용하지 않고 db에 인덱스를 걸 생각은 하지 못했는데, 제일 직관적이고 좋은 방법이라고 생각했다.

페어에게 배울 부분

기록

블랙캣은 기록을 굉장히 잘 하는 크루였다.
-노션에 페어를 진행하면서 했던 내용 + 고민했던 부분 + 회고를 꼼꼼하게 기록해서 공유해 주었다.
-추가적으로 이모지를 적극적으로 사용하여 더욱 좋았다!

의견 일치시키기

페어 시간은 한정되어 있고, 기간 내 요구사항을 만족해야 한다.
-따라서 적당히 타협을 봐서 의견을 빠르게 수용해 데드라인을 맞추는 것도 중요하다고 생각한다.
-블랙캣은 내 의견을 잘 들어줬고, 덕분에 막히는 부분 없이 빠르게 미션을 진행할 수 있었다.

빨리 친해졌고, 의사소통이 잘 돼서 재밌게 코딩할 수 있었다!

- - +

· 약 8분

장바구니 미션에서는 상품 추가와 상품 수정에 대한 요구사항이 있었다.
+요청에 담긴 Body를 통해 전달받은 값을 DTO로 매핑하여 추가와 수정을 했다.

장바구니 미션에서의 상품 추가 및 수정

중복1

클래스명을 제외하고 필드와 검증로직 그 외 모든게 같은 DTO를 보며 중복이라고 생각했다.
+하지만 반대로 용도가 다르기 때문에 중복이 아니라고 생각하기도 했다.
+위 경우는 중복일까? 중복이 아닐까?

이 부분에 대해서 다음과 같은 리뷰를 받았다.

ProductSaveRequestProductUpdateRequest가 완전히 동일한데, 재사용할 수 없을까? 라는 리뷰를 남겼었어요. 사실 생성과 수정은 서로 달라질 개연성이 높아서 미리 분리해놓는 게 더 좋은 방법이긴 한데, 그래도 중복은 싫어서 저도 요즘 이런저런 방법들을 시도해보는 중 입니다. 허브는 이 부분에 대해 어떤 생각을 가지고 있을지 궁금하네요 ㅎㅎ

질문에 대해 아래와 같이 답변을 했다.

저장과 수정할 때 필요한 필드값이 동일하여 현재 구조에서는 하나로 사용해도 된다고 생각을 하지만, 말씀해주신대로 요구사항이 변경된다면 달라질 가능성이 높다고 판단하였습니다!

중복과 우발적 중복

로버트 마틴님이 집필하신 클린 아키텍처는 아래와 같이 중복을 여러가지 종류로 나누어 설명하고 있다.

  • 진짜 중복: 한 인스턴스가 변경되면, 동일한 변경을 그 인스턴스의 모드 복사본에 반드시 적용해야 한다.
  • 거짓된 중복, 우발적 중복: 중복으로 보이는 두 코드 영역이 각자의 경로로 발전한다면, 즉 서로 다른 속도와 다른 이유로 변경된다면 이 두 코드는 진짜 중복이 아니다.

추가와 수정은 초기에는 중복으로 보이지만 초기 생성시에만 기입하는 데이터들이 추가되거나, 시간이 지나면서 서로 달라질 가능성이 높아진다. +그렇기 때문에 위 상황은 우발적 중복으로 보인다. 그래도 중복을 제거해볼 수 있지 않을까?

하나로 사용하는 건 안좋아보이고, 중복은 제거하고 싶은 마음

지금은 추가, 수정 2가지 경우 밖에 없지만 조금 더 복잡한 요구사항이 주어져서 10가지 경우로 입력을 받으면 어떻게 해야할까?
+서비스 계층에서도 계층의 분리를 위해서 다른 DTO를 사용하고 있다면 20개의 DTO를 만들어야 할까?
+리뷰어가 알려준 의존 역전을 이용한 방법을 통해 이를 해결해보자!

중복 제거 전 코드

현재 코드에서는 아래와 같은 구조로 되어있다.
+Controller와 Service에서 저장, 수정할 때 각각의 DTO를 사용하고 있다. +현재 DTO는 controller, service 패키지 내에 있는 것이 아니라 dto라는 패키지에 위치하고 있다.

├── controller
│   └── ProductController
├── service
│   └── ProductService
├── dto
│   ├── ProductSaveRequest
│   └── ProductUpdateRequest

중복2

인터페이스 작성하기

중복3

서비스 레이어에서 필요로 하는 값들을 인터페이스로 정의한다.
+해당 인터페이스는 서비스에서 사용하기 때문에 service 패키지 내부로 옮겨준다.

├── controller
│   └── ProductController
├── service
│   ├── ProductService
│   ├── ProductSaveRequest
│   └── ProductUpdateRequest
public interface ProductSaveRequest {

String getName();

String getImage();

Long getPrice();
}

// ProductService
public Long save(final ProductSaveRequest request) {
final Product product = new Product(request.getName(), request.getImage(), request.getPrice());
return productDao.saveAndGetId(product);
}

구현체 작성하기

중복4

위에서 작성한 인터페이스를 구현하는 클래스를 작성한다.
+요청은 ProductRequest 클래스로 받고, 서비스에 전달할 땐 해당 인터페이스의 명세만 맞추면 문제없이 사용할 수 있다.

├── controller
│   ├── ProductController
│   └── ProductRequest
├── service
│   ├── ProductService
│   ├── ProductSaveRequest
│   └── ProductUpdateRequest
public class ProductRequest implements ProductSaveRequest, ProductUpdateRequest {

@NotBlank(message = "이름은 공백일 수 없습니다.")
@Size(min = 1, max = 100, message = "이름은 최소 {min}자 이상, {max}자 이하여야 합니다.")
private final String name;

@NotBlank(message = "이미지는 공백일 수 없습니다.")
private final String image;

@Range(message = "가격은 최소 {min}원 이상, {max}원 이하여야 합니다.")
private final long price;

public ProductRequest(final String name, final String image, final long price) {
this.name = name;
this.image = image;
this.price = price;
}

@Override
public String getName() {
return name;
}

@Override
public String getImage() {
return image;
}

@Override
public long getPrice() {
return price;
}
}

// ProductController
@PostMapping("/products")
public ResponseEntity<Void> save(@Valid @RequestBody final ProductRequest request) {
final Long id = productService.save(request);
return ResponseEntity.created(URI.create("/products/" + id)).build();
}

정리

위와 같이 구현한다면 다음과 같은 장점을 얻을 수 있다.

  1. Service에서 모든 클라이언트 요청에 대한 DTO를 알지 않아도 된다.
  2. 공통적으로 사용하는 DTO를 제외하고 DTO 패키지에 대한 결합도가 낮아지고, 각 레이어의 응집도가 증가한다.
  3. 요청 객체만 다르고 서비스에서 동일한 행위를 수행하는 경우 중복을 제거할 수 있다.

위 방법을 지금 미션에서 바로 적용할까 하다가, 나중에 필요할 때 적용하면 더 좋을 것 같아서 미션에는 적용하지 않았다.
+상황에 맞춰 적재적소에 의존 역전을 이용해보는 것도 좋을 것 같다.

참고 자료

클린 아키텍처 16장 독립성, 로버트 C. 마틴
+https://techblog.woowahan.com/2647/
+https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/

+ + \ No newline at end of file diff --git a/page/22.html b/page/22.html index 67f9674b6..b5a2ab300 100644 --- a/page/22.html +++ b/page/22.html @@ -13,31 +13,26 @@ - - + +
-

· 약 4분

웹 자동차 미션

사이드 프로젝트를 한다고 시간이 많이 없어서 회고가 늦어졌다.
-웹 자동차 미션에서는 비버와 페어가 매칭되었다.
-레벨 2에서 진행하는 첫 미션이라 많이 긴장되었지만, 그래도 비버랑 초반에 맛있는 것도 많이 먹으면서 빨리 친해져서 재밌게 할 수 있었다.

스프링을 조금 사용할 줄 알아서, 비버랑 같이 학습하면서 미션을 진행했다.
-첫 미션이라 그런지 특별한 부분은 없었고, 최대한 깔끔하게 작성하려고 노력했다.
-난이도 높은 미션이 아니었지만 리뷰어인 라빈에게 칭찬을 많이 받아서 기분이 좋았다.
-라빈 감사합니다!

부족했던 부분

컨디션도 좋지 않고 열정도 식은 것 같은 느낌이 들었다.
-미션이 다소 여유롭다고 느껴져서, 시간에 대한 부분도 잘 관리하지 못한 것 같다.
-미션에 잘 집중하지 못해서 페어에게 많이 미안했고, 나 자신에게 아쉬웠던 부분이 많았다.

지난번 회고를 다시 보는데 집중을 잘 못한 경우가 많은 것 같다.
-도전적이지 않거나 시간이 부족하지 않으면 집중을 잘 못하는 것 같다.
-머릿속에서 시간적 여유가 있다고 생각할 때가 가장 위험한 순간인 것 같다.

함께 자라기에서 나온 난이도 높이기가 필요해지는 순간이다.

새로 학습한 부분

중요도가 있는 어노테이션부터 클래스 이름에 가깝게 명시하기

@SuppressWarnings("NonAsciiCharacters")
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@Transactional
@AutoConfigureMockMvc
@SpringBootTest
public class RacingGameIntegrationTest {

페어에게 배울 부분

비버의 성격
-비버가 성격이 좋아서 편하게 페어를 할 수 있었다.
-미션을 진행하면서 성급하지 않고 여유로워서 좋았다.

미션에 집중하는 부분
-내가 미션에 잘 집중하지 못했는데도 같이 페어를 잘 진행한 것 같아서 좋았다.
-비버가 미션에 잘 집중해서 그렇지 않았나 생각했다.
-근육맨 비버라 그런지 체력이 좋아서 그런가?
-중간에 잘 안 쉬고도 집중해서 미션을 진행하는 걸 보고 대단하다고 생각했다.

학습에 대한 열정
-추가적으로 알고 싶은 부분을 따로 학습하는 열정이 좋다고 생각했다.
-비버와 스프링에 대해 알아가는 시간을 많이 가진 부분이 매우 좋았다.
-나도 5월부터 조금 더 화이팅 해야겠다.

- - +

· 약 5분

웹 장바구니 미션

장바구니 미션은 블랙캣이랑 진행했다.
+요구사항이 엄청 복잡한 미션은 아니었고, 스프링을 사용하여 기본적인 CRUD를 구현하는 미션이었다.
+2단계에서는 Basic 인증을 통해 자신의 장바구니에만 상품을 담고, 제거할 수 있도록 구현하는 요구사항이 추가되었다.
+Interceptor나 Argument Resolver에 대한 이해도가 높지 않았는데, 이번 미션을 통해 조금 더 알아간 느낌이다.
+이전에 스프링 사용할 때는 아무 생각 없이 코드를 작성하는 경우가 많았는데, 코드를 작성할 때 근거가 생기고 있는 것 같다.

새로 학습한 부분

DTO 우발적 중복

장바구니 미션에서는 상품 추가와 상품 수정에 대한 요구사항이 있었다.

dto1

클래스명을 제외하고 필드와 검증 로직 그 외 모든 게 같은 DTO를 보며 중복이라고 생각을 했고, 반대로 용도가 다르기 때문에 중복이 아니라고 생각하기도 했다.
+로버트 마틴님이 집필하신 클린 아키텍처는 아래와 같이 중복을 여러 가지 종류로 나누어 설명하고 있다.

  • 진짜 중복: 한 인스턴스가 변경되면, 동일한 변경을 그 인스턴스의 모드 복사본에 반드시 적용해야 한다.
  • 우발적 중복: 중복으로 보이는 두 코드 영역이 각자의 경로로 발전한다면, 즉 서로 다른 속도와 다른 이유로 변경된다면 이 두 코드는 진짜 중복이 아니다.

추가와 수정은 초기에는 중복으로 보이지만 초기 생성 시에만 기입하는 데이터들이 추가되거나, 시간이 지나면서 서로 달라질 가능성이 높아진다.
+따라서 리뷰어 웨지가 아래와 같이 의존 역전을 이용하는 방법도 있다고 알려주셨다.

dto2

Interceptor에서 인증한 값 재사용

사실 조회를 두 번 하기 싫어서 다양한 방법을 생각했었는데 이번 미션에서는 ThreadLocal을 사용했다.
+일단 Tomcat은 요청마다 다른 스레드를 사용하고, Interceptor에서 조회해서 만든 Credential을 ThreadLocal에 넣어두었다가 ArgumentResolver에서 꺼낸 다음 ThreadLocal을 clear 하면 문제가 없을 거라 판단했다.

리뷰어인 웨지에게도 어떤 방법을 사용할지 궁금증을 작성했었다.
+웨지는 email에 index를 걸어두고 dao 재조회를 사용할 것이라고 했다.
+재사용하지 않고 db에 인덱스를 걸 생각은 하지 못했는데, 제일 직관적이고 좋은 방법이라고 생각했다.

페어에게 배울 부분

기록

블랙캣은 기록을 굉장히 잘 하는 크루였다.
+노션에 페어를 진행하면서 했던 내용 + 고민했던 부분 + 회고를 꼼꼼하게 기록해서 공유해 주었다.
+추가적으로 이모지를 적극적으로 사용하여 더욱 좋았다!

의견 일치시키기

페어 시간은 한정되어 있고, 기간 내 요구사항을 만족해야 한다.
+따라서 적당히 타협을 봐서 의견을 빠르게 수용해 데드라인을 맞추는 것도 중요하다고 생각한다.
+블랙캣은 내 의견을 잘 들어줬고, 덕분에 막히는 부분 없이 빠르게 미션을 진행할 수 있었다.

빨리 친해졌고, 의사소통이 잘 돼서 재밌게 코딩할 수 있었다!

+ + \ No newline at end of file diff --git a/page/23.html b/page/23.html index 7c96cfe17..b3ff24e72 100644 --- a/page/23.html +++ b/page/23.html @@ -13,31 +13,31 @@ - - + +
-

· 약 5분

프론트엔트

닉네임을 입력하여 간단히 로그인하는 화면, 채팅 목록을 보여주는 화면도 만들었고 단일 채팅을 확인할 수 있는 화면도 만들었다.
-추가로 채팅을 이어나갈 수 있게 하는 기능도 추가했다.
-자잘하게 신경 쓸 부분이 많아서, 프론트엔드 하는 사람들이 대단하다고 생각되었다.
-여유가 된다면 자신의 채팅을 볼 수 있는 기능이나, 채팅을 이어서 할 수 있는 기능, 댓글 기능도 추가할 예정이다.

백엔드

최대한 빨리 서비스를 크루들에게 제공하기로 정해서, 백엔드는 말랑이 일단 다 만들고 있다.
-말랑이 한 부분이 너무 많아서 내가 못 따라가는 것 같다.
-나중에 백엔드 코드를 이해하는 시간을 가져야겠다.

Http Request Header

아직 인증에 대한 부분을 하지 않아서 요청 헤더에 이름을 보내기로 했다.
-말랑이 한글은 안된다고 말해줘서 Base64로 인코딩하고, 백엔드에서 디코딩 하여 사용하기로 했다.
-아래는 pinia에 있는 name 값을 인코딩 하는 코드다. deprecated 되었다는데, 다른 방법을 사용할 줄 몰라서 일단 이걸 사용했다.

const encodedName = () => {
const uriComponent = unescape(encodeURIComponent(name.value));
return btoa(uriComponent);
};

Elastic Beanstalk

가장 빠르게 백엔드를 배포할 수 있는 방법이 뭘지 고민하다가 Elastic Beanstalk를 사용하기로 했다.
-Elastic Beanstalk를 사용하면 인프라에 대해 잘 알지 못해도 애플리케이션을 빠르게 배포하고 관리할 수 있다.
-모니터링, 로깅, 로드 밸런싱 등 다양한 기능을 제공한다.

Elastic Beanstalk RDS 설정 후 분리

초기 설정 시 RDS를 연결하고 설정 완료 후 분리한다면, Beanstalk 인스턴스 -> RDS 요청 시 인바운드 설정을 안 해도 된다.
-RDS 분리 시 Beanstalk에 기본적으로 설정되어 있는 RDS_HOSTNAME, RDS_PORT, RDS_USERNAME, RDS_PASSWORD와 같은 환경 변수가 같이 제거된다.
-추가로 Elastic Beanstalk로 RDS를 설정하면 기본 데이터베이스 명은 ebdb다.

Elastic Beanstalk nginx 설정

업로드하는 zip 파일 내부에 .platform/nginx/conf.d/ 경로에 설정 파일을 추가하면 nginx 설정을 할 수 있다.

Jenkins

백엔드 코드를 일일히 배포하기 불편해서 Jenkins를 이용하여 Repository에 코드를 push 할 때 자동으로 배포가 되게 설정하기로 했다.
-작년에 확인했을 땐 2022년 12월 31일까지 EC2 ARM 기반 t4g.small이 무료였는데, 다시 들어가 보니 2023년까지 12월 31일까지 t4g.small을 무료로 사용할 수 있었다.
-t4g.small은 램이 2G인데, 예전에는 부족하지 않았다고 생각했는데 Java 17을 써서 그런가 빌드 할 때 램이 많이 부족한 것 같아서 Swap 메모리 2기가를 추가로 설정했다.
-추가로 build.gradle에서 아래와 같이 설정한다면 테스트 시 사용하는 램을 늘릴 수 있다. 기본값은 512MB라고 한다.

test {
maxHeapSize = "1024m"
}

Jenkins Blue Ocean

Blue Ocean은 Jenkins Pipeline을 구성하는 데에 있어 편리하게 해주는 도구다.
-시각화도 잘 되어있고, 설정도 편리한 것 같다.
-오늘 적용해 보니 램이 부족하여 중간에 잘 안되기도 하고 그래서 그냥 "Pipeline만 사용할 걸 그랬나?" 라는 생각이 든다.

참고 자료

Elastic Beanstalk, AWS
-EC2 AWS Graviton, AWS
-Default Memory Settings, AWS

- - +

· 약 4분

웹 자동차 미션

사이드 프로젝트를 한다고 시간이 많이 없어서 회고가 늦어졌다.
+웹 자동차 미션에서는 비버와 페어가 매칭되었다.
+레벨 2에서 진행하는 첫 미션이라 많이 긴장되었지만, 그래도 비버랑 초반에 맛있는 것도 많이 먹으면서 빨리 친해져서 재밌게 할 수 있었다.

스프링을 조금 사용할 줄 알아서, 비버랑 같이 학습하면서 미션을 진행했다.
+첫 미션이라 그런지 특별한 부분은 없었고, 최대한 깔끔하게 작성하려고 노력했다.
+난이도 높은 미션이 아니었지만 리뷰어인 라빈에게 칭찬을 많이 받아서 기분이 좋았다.
+라빈 감사합니다!

부족했던 부분

컨디션도 좋지 않고 열정도 식은 것 같은 느낌이 들었다.
+미션이 다소 여유롭다고 느껴져서, 시간에 대한 부분도 잘 관리하지 못한 것 같다.
+미션에 잘 집중하지 못해서 페어에게 많이 미안했고, 나 자신에게 아쉬웠던 부분이 많았다.

지난번 회고를 다시 보는데 집중을 잘 못한 경우가 많은 것 같다.
+도전적이지 않거나 시간이 부족하지 않으면 집중을 잘 못하는 것 같다.
+머릿속에서 시간적 여유가 있다고 생각할 때가 가장 위험한 순간인 것 같다.

함께 자라기에서 나온 난이도 높이기가 필요해지는 순간이다.

새로 학습한 부분

중요도가 있는 어노테이션부터 클래스 이름에 가깝게 명시하기

@SuppressWarnings("NonAsciiCharacters")
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@Transactional
@AutoConfigureMockMvc
@SpringBootTest
public class RacingGameIntegrationTest {

페어에게 배울 부분

비버의 성격
+비버가 성격이 좋아서 편하게 페어를 할 수 있었다.
+미션을 진행하면서 성급하지 않고 여유로워서 좋았다.

미션에 집중하는 부분
+내가 미션에 잘 집중하지 못했는데도 같이 페어를 잘 진행한 것 같아서 좋았다.
+비버가 미션에 잘 집중해서 그렇지 않았나 생각했다.
+근육맨 비버라 그런지 체력이 좋아서 그런가?
+중간에 잘 안 쉬고도 집중해서 미션을 진행하는 걸 보고 대단하다고 생각했다.

학습에 대한 열정
+추가적으로 알고 싶은 부분을 따로 학습하는 열정이 좋다고 생각했다.
+비버와 스프링에 대해 알아가는 시간을 많이 가진 부분이 매우 좋았다.
+나도 5월부터 조금 더 화이팅 해야겠다.

+ + \ No newline at end of file diff --git a/page/24.html b/page/24.html index 28c5d6741..6c53c7c46 100644 --- a/page/24.html +++ b/page/24.html @@ -13,30 +13,31 @@ - - + +
-

· 약 8분

설정 환경

소프트웨어 이미지: Amazon Linux 2023 AMI
-아키텍쳐: ARM
-인스턴스 유형: t4g.small
-환경 구성이 완료된 Elastic Beanstalk
-단일 Spring Boot 프로젝트가 존재하는 Github Repository

[EC2 CLI] Swap 메모리 설정

t4g.small이 램이 2G인데 램이 부족하다고 느껴져서 swap 메모리를 설정했다.
-아래 명령어를 따라 swap 메모리를 설정하고 free -h 명령어를 통해 잘 설정되었는지 확인할 수 있다.

# fallocate 이용하여 스왑 파일 생성
sudo fallocate -l 2G /swapfile

# 권한 설정
sudo chmod 600 /swapfile

# 파일을 Swap 포맷으로 변경 후 시스템에 등록
sudo mkswap /swapfile
sudo swapon /swapfile

# Swap 메모리 부팅시 자동으로 마운트하도록 적용
# 최하단에 다음 구문 설정 -> /swapfile swap swap defaults 0 0
sudo vim /etc/fstab

[EC2 CLI] jenkins 설치

sudo wget -O /etc/yum.repos.d/jenkins.repo \
https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
sudo yum upgrade
sudo yum install java-17-amazon-corretto-devel
sudo yum install jenkins
sudo systemctl daemon-reload

Jenkins 공식 홈페이지 를 참고하여 설치하는 게 좋다.

[EC2 CLI] Jenkins 시작

sudo systemctl enable jenkins
sudo systemctl start jenkins

enable로 설정하여 부팅시 자동시작 되도록 설정한다.

[EC2 CLI] nginx & git 설치

sudo yum install nginx
sudo systemctl enable nginx
sudo systemctl start nginx

sudo yum install git

nginx와 코드를 불러올 때 사용할 git을 설치한다.

[EC2 CLI] nginx 리버스 프록시 설정

아래 설정 파일은 공식 홈페이지에서 안내한 기본적인 설정 파일이다.

upstream jenkins {
keepalive 32; # keepalive connections
server 127.0.0.1:8080; # jenkins ip and port
}

# Required for Jenkins websocket agents
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

server {
listen 80; # Listen on port 80 for IPv4 requests

server_name jenkins.example.com; # replace 'jenkins.example.com' with your server domain name

# this is the jenkins web root directory
# (mentioned in the output of "systemctl cat jenkins")
root /var/run/jenkins/war/;

access_log /var/log/nginx/jenkins.access.log;
error_log /var/log/nginx/jenkins.error.log;

# pass through headers from Jenkins that Nginx considers invalid
ignore_invalid_headers off;

location ~ "^/static/[0-9a-fA-F]{8}\/(.*)$" {
# rewrite all static files into requests to the root
# E.g /static/12345678/css/something.css will become /css/something.css
rewrite "^/static/[0-9a-fA-F]{8}\/(.*)" /$1 last;
}

location /userContent {
# have nginx handle all the static requests to userContent folder
# note : This is the $JENKINS_HOME dir
root /var/lib/jenkins/;
if (!-f $request_filename){
# this file does not exist, might be a directory or a /**view** url
rewrite (.*) /$1 last;
break;
}
sendfile on;
}

location / {
sendfile off;
proxy_pass http://jenkins;
proxy_redirect default;
proxy_http_version 1.1;

# Required for Jenkins websocket agents
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_max_temp_file_size 0;

#this is the maximum upload size
client_max_body_size 10m;
client_body_buffer_size 128k;

proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffering off;
proxy_request_buffering off; # Required for HTTP CLI commands
proxy_set_header Connection ""; # Clear for keepalive
}

}

Jenkins는 8080 포트로 동작하기 때문에 리버스 프록시를 설정해준다.
-/etc/nginx/conf.d 아래 default.conf 파일을 하나 생성하고 위와 같이 입력하고 저장한다.
-nginx의 기본 설정 파일에 존재하는 include /etc/nginx/conf.d/*.conf; 설정 때문에 .conf 로 끝난다면 설정이 적용된다.
-설정 후 sudo nginx -t로 설정파일이 정상인지 확인하고, sudo systemctl restart nginx 명령어로 nginx를 재시작한다.

[Jenkins] Jenkins 접속

Jenkins를 설치한 EC2 인스턴스 인바운드 설정에 80번 포트가 열려있는지 확인한다.
-EC2의 아이피 주소를 입력하고 들어가면 비밀번호를 입력하라는 창이 나온다.

jenkins-start

초기 비밀번호를 입력해야 하는데 sudo cat /var/lib/jenkins/secrets/initialAdminPasswor 를 입력해 초기 비밀번호를 얻을 수 있다.
-비밀번호를 입력하면 플러그인 설정 창이 나올텐데 install suggested plugins을 클릭하여 Jenkins가 추천하는 기본 플러그인들을 설치하면 된다.
-플러그인을 설치하면 계정 및 주소 설정을 해야하는데 이건 편하게 설정하면 된다.

[Jenkins] Jenkins Blue Ocean 설치

Jenkins 관리 → Plugin Manager에서 Blue Ocean을 검색해 설치한다.

[AWS IAM & EC2] IAM으로 EC2 인스턴스 권한 설정하기

S3와 Elastic Beanstalk에 접근할 수 있는 권한을 부여하려면 AmazonS3FullAccess, AdministratorAccess-AWSElasticBeanstalk 두 개의 정책을 가지고 있는 역할을 생성해야 한다.
-IAM에서 다음과 같이 역할을 하나 새로 생성한다.

  1. 엔터티 선택

aws-iam1

  1. 권한 추가

aws-iam2

  1. 이름 지정, 검토 및 생성

aws-iam3

  1. 생성한 IAM EC2 Jenkins 인스턴스를 선택하고, 작업 → 보안 → IAM 역할 수정을 눌러 Role 설정

aws-iam4

[AWS S3] Jar 파일을 업로드 할 S3 버킷 생성

버킷을 생성할 때 다음 설정을 제외하고 모두 차단 활성화를 해준다.

  • 새 ACL(액세스 제어 목록)을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단

aws-s3

[Github] Blue Ocean에서 파이프라인 생성에 필요한 Github Token 생성

repo, user:email 권한이 있는 토큰이 필요하다.

[Jenkins] 블루 오션 시작

jenkins-blue-ocean1

블루 오션 열기로 파이프라인을 생성한다.
-토큰 입력 → 조직 선택 → CI/CD 설정할 Repository 선택을 하면 파이프라인 창으로 넘어간다.
-Jenkinsfile을 직접 작성하여 설정하기 위해 간단하게 print 하나 출력하는 것으로 설정했다.

jenkins-blue-ocean2

파이프라인이 실행될 텐데 pipeline status에서 아래와 같이 초록불이 뜨면 된다.

jenkins-blue-ocean3

[Github Repsoitory] Jenkinsfile 설정

블루 오션 시작을 통해 설정하면 Jenkinsfile이 하나 만들어지고, 아래와 같이 원하는 파이프라인을 설정한다.

pipeline {
agent any
stages {
stage('build and test') {
steps {
sh '/gradlew clean build'
}
}
stage('zip') {
steps {
sh 'mv ./build/libs/woowachat.jar .'
sh 'zip -r woowachat.zip .platform delivery.jar Procfile'
}
}
stage('upload') {
steps {
sh 'aws s3 cp woowachat.zip s3://woowa-chat/woowachat.zip --region ap-northeast-2'
}
}
stage('deploy') {
steps {
sh 'aws elasticbeanstalk create-application-version --region ap-northeast-2 --application-name woowachat --version-label ${BUILD_TAG} --source-bundle S3Bucket="woowa-chat",S3Key="woowachat.zip"'
sh 'aws elasticbeanstalk update-environment --region ap-northeast-2 --environment-name Woowachat-env --version-label ${BUILD_TAG}'
}
}
}
}

[Github] Webhooks 설정

github-hook

push 이벤트가 발생할 때 http://Jenkins주소/github-webhook/ 로 post request를 하도록 웹훅을 설정한다.

참고 자료

Install Jenkins - CentOS, Jenkins
-Nginx Reverse Proxy Configuration, Jenkins
-Amazon Corretto 17 JDK Install, AWS
-Amazon Linux 2023 packages, AWS

- - +

· 약 5분

프론트엔트

닉네임을 입력하여 간단히 로그인하는 화면, 채팅 목록을 보여주는 화면도 만들었고 단일 채팅을 확인할 수 있는 화면도 만들었다.
+추가로 채팅을 이어나갈 수 있게 하는 기능도 추가했다.
+자잘하게 신경 쓸 부분이 많아서, 프론트엔드 하는 사람들이 대단하다고 생각되었다.
+여유가 된다면 자신의 채팅을 볼 수 있는 기능이나, 채팅을 이어서 할 수 있는 기능, 댓글 기능도 추가할 예정이다.

백엔드

최대한 빨리 서비스를 크루들에게 제공하기로 정해서, 백엔드는 말랑이 일단 다 만들고 있다.
+말랑이 한 부분이 너무 많아서 내가 못 따라가는 것 같다.
+나중에 백엔드 코드를 이해하는 시간을 가져야겠다.

Http Request Header

아직 인증에 대한 부분을 하지 않아서 요청 헤더에 이름을 보내기로 했다.
+말랑이 한글은 안된다고 말해줘서 Base64로 인코딩하고, 백엔드에서 디코딩 하여 사용하기로 했다.
+아래는 pinia에 있는 name 값을 인코딩 하는 코드다. deprecated 되었다는데, 다른 방법을 사용할 줄 몰라서 일단 이걸 사용했다.

const encodedName = () => {
const uriComponent = unescape(encodeURIComponent(name.value));
return btoa(uriComponent);
};

Elastic Beanstalk

가장 빠르게 백엔드를 배포할 수 있는 방법이 뭘지 고민하다가 Elastic Beanstalk를 사용하기로 했다.
+Elastic Beanstalk를 사용하면 인프라에 대해 잘 알지 못해도 애플리케이션을 빠르게 배포하고 관리할 수 있다.
+모니터링, 로깅, 로드 밸런싱 등 다양한 기능을 제공한다.

Elastic Beanstalk RDS 설정 후 분리

초기 설정 시 RDS를 연결하고 설정 완료 후 분리한다면, Beanstalk 인스턴스 -> RDS 요청 시 인바운드 설정을 안 해도 된다.
+RDS 분리 시 Beanstalk에 기본적으로 설정되어 있는 RDS_HOSTNAME, RDS_PORT, RDS_USERNAME, RDS_PASSWORD와 같은 환경 변수가 같이 제거된다.
+추가로 Elastic Beanstalk로 RDS를 설정하면 기본 데이터베이스 명은 ebdb다.

Elastic Beanstalk nginx 설정

업로드하는 zip 파일 내부에 .platform/nginx/conf.d/ 경로에 설정 파일을 추가하면 nginx 설정을 할 수 있다.

Jenkins

백엔드 코드를 일일히 배포하기 불편해서 Jenkins를 이용하여 Repository에 코드를 push 할 때 자동으로 배포가 되게 설정하기로 했다.
+작년에 확인했을 땐 2022년 12월 31일까지 EC2 ARM 기반 t4g.small이 무료였는데, 다시 들어가 보니 2023년까지 12월 31일까지 t4g.small을 무료로 사용할 수 있었다.
+t4g.small은 램이 2G인데, 예전에는 부족하지 않았다고 생각했는데 Java 17을 써서 그런가 빌드 할 때 램이 많이 부족한 것 같아서 Swap 메모리 2기가를 추가로 설정했다.
+추가로 build.gradle에서 아래와 같이 설정한다면 테스트 시 사용하는 램을 늘릴 수 있다. 기본값은 512MB라고 한다.

test {
maxHeapSize = "1024m"
}

Jenkins Blue Ocean

Blue Ocean은 Jenkins Pipeline을 구성하는 데에 있어 편리하게 해주는 도구다.
+시각화도 잘 되어있고, 설정도 편리한 것 같다.
+오늘 적용해 보니 램이 부족하여 중간에 잘 안되기도 하고 그래서 그냥 "Pipeline만 사용할 걸 그랬나?" 라는 생각이 든다.

참고 자료

Elastic Beanstalk, AWS
+EC2 AWS Graviton, AWS
+Default Memory Settings, AWS

+ + \ No newline at end of file diff --git a/page/25.html b/page/25.html index 491b4cd91..8df565ff1 100644 --- a/page/25.html +++ b/page/25.html @@ -13,35 +13,30 @@ - - + +
-

· 약 6분

4월 21일 금요일

레벨 2를 시작한 뒤 내가 학습에 대한 방향을 잃어버렸다는 생각이 들었다.
-레벨 3, 4에서 나만의 강점을 가지고 싶어 고민을 많이 했다.
-단순히 스프링을 깊게 공부하는 건 효율이 많이 떨어진다고 생각했다.
-글쓰기 수상으로 받은 쿠폰을 사용해 브라운에게 커피챗을 신청했고, 사이드 프로젝트를 해보라는 답을 받았다.

나는 아이디어를 못내는 편인데 브라운이 아이디어까지 던져주셨다.
-Chat-GPT 서비스를 크루들에게 제공하고, 해당 크루들이 질문한 내용을 공유할 수 있는 건 어떤지?

기술이 목적인 사이드 프로젝트를 진행하면 좋을 것 같다는 답변을 들었고, 혼자 아니면 페어할 수 있을 정도의 인원으로 진행하면 좋겠다고 하셨다.
-프론트랑 간단하게 배포까지 해본 경험이 있어서 혼자해도 크게 어렵지 않을 것 같아서 혼자 하기로 마음을 먹었다.

이건 못참지

도메인 구입 성공?

커피챗이 끝나고 집으로 돌아가는 길에 바로 도메인을 구매하려고 namecheap에서 적당한 도메인이 없을까 검색을 계속했다.
-마치 어릴 때 했던 게임 닉네임 정하는 것처럼 시간이 오래 걸렸다.
-dev, io, chat 도메인이 후보였고 집 가는 길에 결정만 하다가 구매하지 못했다.

말랑의 DM

집에 가서 밥을 먹고 말랑이랑 DM 하다 프로젝트를 같이 하자는 이야기가 나왔다.
-우테코 최고 고수 말랑의 요구라 수락하지 않으면 후폭풍을 감당할 수 없었다.

이런저런 대화를 나누다가 난 빠르게 프로토타입을 만들어 보고 싶어서 프론트를 구현한다고 했고, 말랑은 GPT api를 조사하기로 했다.
-추가로 도메인에 관한 이야기를 하다가 woowachat이 언급되었고, namecheap에서 chat 도메인을 사용한 woowa.chat으로 구매했다.
-이후에 teco.chat으로 변경했다!

도메인 설정 및 배포

토요일에 구매한 도메인을 CDN, 보안 등 다양한 기능을 제공하는 Cloudflare에 도메인 등록을 했다.
-나에게 익숙한 Nuxt3를 사용하기로 했고, Cloudflare Pages를 이용하여 배포했다.

GPT

무료 크레딧을 사용하니 api limit이 있어 분당 3번밖에 사용할 수 없었다.
-일단 백엔드를 구축하기 전에는 무료 크레딧을 사용할 생각이다.

Sonarcloud

정적 코드 분석 도구로 Sonarcloud를 적용했다.
-Sonarcloud는 SonarQube의 SaaS 버전이고 사용이 매우 편하다.
-예전에 Sonarcloud를 사용할 땐 버튼 몇 번 누르면 적용할 수 있었는데, 이번에는 바로 github action을 사용하라는 안내 페이지로 이동했다.
-Sonarcloud가 자체적으로 github repository에 push 하면 정적 분석을 해주는 기능을 원했고, Administration -> Analysis Method에 Automatic Analysis를 설정하니 되었다.
-너무 꽁꽁 숨겨져있네

Tiptap

코드 하이라이팅 기능을 넣고 싶어서 Tiptap을 사용했다.
-Tiptap은 Headless WYSIWYG 에디터로 사용자 정의 기능에 특화되어있는 에디터다.
-아직 Tiptap이 제공하는 모든 기능을 자연스럽게 사용하지는 못하지만 CodeBlockLowlight 플러그인을 사용하여 코드 블록을 예쁘게 출력할 수 있었다.
-api 반환값 그대로 tiptap의 content에 설정했더니 코드 블록이 설정되지 않아서 백 틱 3개를 <pre><code>로 변환했다.
-추가로 띄어쓰기도 적용되지 않아서 \n<br>태그로 변환했다.
-변환하는 로직은 GPT의 도움을 많이 받았다.

const replaceCodeFences = (input: String) => {
const codeFencesRegex = /```([\w-]*)\n([\s\S]*?)\n```/g;
return input
.replace(codeFencesRegex, (match, p1, p2) => {
const languageClass = p1 ? ` class="language-${p1}"` : "";
return `<pre><code${languageClass}>${p2}</code></pre>`;
})
.replace(/\n/g, "<br>");
};

Tiptap을 적용하니 다음과 같이 깔끔한 코드 블록을 볼 수 있었다.

tecochat

폰트 및 favicon 적용

타이틀은 배달의민족 도현체, 내용은 IBM Plex Sans를 사용했다.
-추가로 favicon도 간단하게 적용해서 만족스러웠다.

- - +

· 약 8분

설정 환경

소프트웨어 이미지: Amazon Linux 2023 AMI
+아키텍쳐: ARM
+인스턴스 유형: t4g.small
+환경 구성이 완료된 Elastic Beanstalk
+단일 Spring Boot 프로젝트가 존재하는 Github Repository

[EC2 CLI] Swap 메모리 설정

t4g.small이 램이 2G인데 램이 부족하다고 느껴져서 swap 메모리를 설정했다.
+아래 명령어를 따라 swap 메모리를 설정하고 free -h 명령어를 통해 잘 설정되었는지 확인할 수 있다.

# fallocate 이용하여 스왑 파일 생성
sudo fallocate -l 2G /swapfile

# 권한 설정
sudo chmod 600 /swapfile

# 파일을 Swap 포맷으로 변경 후 시스템에 등록
sudo mkswap /swapfile
sudo swapon /swapfile

# Swap 메모리 부팅시 자동으로 마운트하도록 적용
# 최하단에 다음 구문 설정 -> /swapfile swap swap defaults 0 0
sudo vim /etc/fstab

[EC2 CLI] jenkins 설치

sudo wget -O /etc/yum.repos.d/jenkins.repo \
https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
sudo yum upgrade
sudo yum install java-17-amazon-corretto-devel
sudo yum install jenkins
sudo systemctl daemon-reload

Jenkins 공식 홈페이지 를 참고하여 설치하는 게 좋다.

[EC2 CLI] Jenkins 시작

sudo systemctl enable jenkins
sudo systemctl start jenkins

enable로 설정하여 부팅시 자동시작 되도록 설정한다.

[EC2 CLI] nginx & git 설치

sudo yum install nginx
sudo systemctl enable nginx
sudo systemctl start nginx

sudo yum install git

nginx와 코드를 불러올 때 사용할 git을 설치한다.

[EC2 CLI] nginx 리버스 프록시 설정

아래 설정 파일은 공식 홈페이지에서 안내한 기본적인 설정 파일이다.

upstream jenkins {
keepalive 32; # keepalive connections
server 127.0.0.1:8080; # jenkins ip and port
}

# Required for Jenkins websocket agents
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

server {
listen 80; # Listen on port 80 for IPv4 requests

server_name jenkins.example.com; # replace 'jenkins.example.com' with your server domain name

# this is the jenkins web root directory
# (mentioned in the output of "systemctl cat jenkins")
root /var/run/jenkins/war/;

access_log /var/log/nginx/jenkins.access.log;
error_log /var/log/nginx/jenkins.error.log;

# pass through headers from Jenkins that Nginx considers invalid
ignore_invalid_headers off;

location ~ "^/static/[0-9a-fA-F]{8}\/(.*)$" {
# rewrite all static files into requests to the root
# E.g /static/12345678/css/something.css will become /css/something.css
rewrite "^/static/[0-9a-fA-F]{8}\/(.*)" /$1 last;
}

location /userContent {
# have nginx handle all the static requests to userContent folder
# note : This is the $JENKINS_HOME dir
root /var/lib/jenkins/;
if (!-f $request_filename){
# this file does not exist, might be a directory or a /**view** url
rewrite (.*) /$1 last;
break;
}
sendfile on;
}

location / {
sendfile off;
proxy_pass http://jenkins;
proxy_redirect default;
proxy_http_version 1.1;

# Required for Jenkins websocket agents
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_max_temp_file_size 0;

#this is the maximum upload size
client_max_body_size 10m;
client_body_buffer_size 128k;

proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffering off;
proxy_request_buffering off; # Required for HTTP CLI commands
proxy_set_header Connection ""; # Clear for keepalive
}

}

Jenkins는 8080 포트로 동작하기 때문에 리버스 프록시를 설정해준다.
+/etc/nginx/conf.d 아래 default.conf 파일을 하나 생성하고 위와 같이 입력하고 저장한다.
+nginx의 기본 설정 파일에 존재하는 include /etc/nginx/conf.d/*.conf; 설정 때문에 .conf 로 끝난다면 설정이 적용된다.
+설정 후 sudo nginx -t로 설정파일이 정상인지 확인하고, sudo systemctl restart nginx 명령어로 nginx를 재시작한다.

[Jenkins] Jenkins 접속

Jenkins를 설치한 EC2 인스턴스 인바운드 설정에 80번 포트가 열려있는지 확인한다.
+EC2의 아이피 주소를 입력하고 들어가면 비밀번호를 입력하라는 창이 나온다.

jenkins-start

초기 비밀번호를 입력해야 하는데 sudo cat /var/lib/jenkins/secrets/initialAdminPasswor 를 입력해 초기 비밀번호를 얻을 수 있다.
+비밀번호를 입력하면 플러그인 설정 창이 나올텐데 install suggested plugins을 클릭하여 Jenkins가 추천하는 기본 플러그인들을 설치하면 된다.
+플러그인을 설치하면 계정 및 주소 설정을 해야하는데 이건 편하게 설정하면 된다.

[Jenkins] Jenkins Blue Ocean 설치

Jenkins 관리 → Plugin Manager에서 Blue Ocean을 검색해 설치한다.

[AWS IAM & EC2] IAM으로 EC2 인스턴스 권한 설정하기

S3와 Elastic Beanstalk에 접근할 수 있는 권한을 부여하려면 AmazonS3FullAccess, AdministratorAccess-AWSElasticBeanstalk 두 개의 정책을 가지고 있는 역할을 생성해야 한다.
+IAM에서 다음과 같이 역할을 하나 새로 생성한다.

  1. 엔터티 선택

aws-iam1

  1. 권한 추가

aws-iam2

  1. 이름 지정, 검토 및 생성

aws-iam3

  1. 생성한 IAM EC2 Jenkins 인스턴스를 선택하고, 작업 → 보안 → IAM 역할 수정을 눌러 Role 설정

aws-iam4

[AWS S3] Jar 파일을 업로드 할 S3 버킷 생성

버킷을 생성할 때 다음 설정을 제외하고 모두 차단 활성화를 해준다.

  • 새 ACL(액세스 제어 목록)을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단

aws-s3

[Github] Blue Ocean에서 파이프라인 생성에 필요한 Github Token 생성

repo, user:email 권한이 있는 토큰이 필요하다.

[Jenkins] 블루 오션 시작

jenkins-blue-ocean1

블루 오션 열기로 파이프라인을 생성한다.
+토큰 입력 → 조직 선택 → CI/CD 설정할 Repository 선택을 하면 파이프라인 창으로 넘어간다.
+Jenkinsfile을 직접 작성하여 설정하기 위해 간단하게 print 하나 출력하는 것으로 설정했다.

jenkins-blue-ocean2

파이프라인이 실행될 텐데 pipeline status에서 아래와 같이 초록불이 뜨면 된다.

jenkins-blue-ocean3

[Github Repsoitory] Jenkinsfile 설정

블루 오션 시작을 통해 설정하면 Jenkinsfile이 하나 만들어지고, 아래와 같이 원하는 파이프라인을 설정한다.

pipeline {
agent any
stages {
stage('build and test') {
steps {
sh '/gradlew clean build'
}
}
stage('zip') {
steps {
sh 'mv ./build/libs/woowachat.jar .'
sh 'zip -r woowachat.zip .platform delivery.jar Procfile'
}
}
stage('upload') {
steps {
sh 'aws s3 cp woowachat.zip s3://woowa-chat/woowachat.zip --region ap-northeast-2'
}
}
stage('deploy') {
steps {
sh 'aws elasticbeanstalk create-application-version --region ap-northeast-2 --application-name woowachat --version-label ${BUILD_TAG} --source-bundle S3Bucket="woowa-chat",S3Key="woowachat.zip"'
sh 'aws elasticbeanstalk update-environment --region ap-northeast-2 --environment-name Woowachat-env --version-label ${BUILD_TAG}'
}
}
}
}

[Github] Webhooks 설정

github-hook

push 이벤트가 발생할 때 http://Jenkins주소/github-webhook/ 로 post request를 하도록 웹훅을 설정한다.

참고 자료

Install Jenkins - CentOS, Jenkins
+Nginx Reverse Proxy Configuration, Jenkins
+Amazon Corretto 17 JDK Install, AWS
+Amazon Linux 2023 packages, AWS

+ + \ No newline at end of file diff --git a/page/26.html b/page/26.html index 0b2e307f1..ae0bdcc8a 100644 --- a/page/26.html +++ b/page/26.html @@ -13,42 +13,35 @@ - - + +
-

· 약 6분

책 정보

상자 밖에 있는 사람
-아빈저연구소

자기기만과 자기배반

책에서는 자기기만과 자기배반에 대한 내용을 다룬다.

  • 자기기만: 자신의 문제를 인정하지 않는 것
  • 자기배반: 다른 사람을 위해 무언가 해야만 한다는 생각을 반하는 행위

자기배반을 한다면 자기기만 상태가 된다.
-자기기만 상태에 빠지는 것을 책에서는 상자 안에 들어간다고 표현한다.

읽고 나서

최근에 읽은 책 중 가장 마음이 불편했다.
-그렇기에 더더욱 나에게 필요한 내용이 담겨있었다.

살면서 많은 선택의 순간이 존재했고, 그 순간마다 자기배반을 택하는 경우가 많았다.
-작게는 집안일을 해야 하는데 몸이 조금 힘들다고 하지 않거나
-크게는 잘못을 인정해야 하는 상황에서 그러지 않은 경우가 있었다.
-이런 상황이 반복되어 결국 상자 안에 나 자신을 가두는 경우가 많았다.

더 나은 삶을 위해 내가 상자 안에 있는지 지속적으로 확인하고, 상자 밖으로 나가려는 연습을 해야겠다.
-넓은 시선을 가지고, 항상 내가 틀릴 수 있다는 것을 생각하고 살아가자.

밑줄 친 문장들

우리의 생각은 지식보다 작다.
-우리의 지식은 사랑보다 작다.
-우리의 사랑은 존재보다 작다.
-그리고 우리가 생각하는 나는 실제의 나보다 그만큼 작다.
-R. D. 랭
-p.19

우리가 외적으로 어떤 행동을 하든지 간에, 사람들은 우리 마음에서 그들을 어떻게 대하고 있는지에 따라 주로 반응합니다.
-우리가 사람들에 대해 어떻게 느끼게 되는지는 우리가 상자 안에 있는지 혹은 상자 밖에 있는지에 따라 달라지게 됩니다.
-p.66

비난은 감정에 속하고 낙관은 의지에 속한다.
-인간은 감정보다 더 큰 존재이다.
-알랭, 탁닛한
-p.103

우리가 자신에게만 집중하고 있는 한, 혼자서 일하는 것 이상의 창조적인 결과나 협력을 이끌어 낸다는 것은 불가능합니다.
-오늘날 경제 환경에서는 혼자서는 일의 결과를 탁월하게 만들어 내기가 어렵습니다.
-내가 중심이어야 된다는 폐쇄적인 사고는 함께 일하는 사람들의 열정을 불러오지 못합니다.
-p.175

솔직함은 우리의 문제를 해결하는 열쇠입니다.
-그것은 자신의 행동과 관련된 사람에 대해 기꺼이 사과를 하는 것입니다.
-그것만이 실타래처럼 엉킨 관계의 문제를 해결할 수 있기 때문이죠.
-p.188

누군가를 나와 같이 동일한 가치를 지닌 한 인간으로 생각해서 그 사람을 위해 내가 상자 밖에 계속 머무르고 싶은 열망이 생길 때, 나는 이미 그 사람에 대해 상자 밖에 있다.
-p.214

대부분의 사람들이 관계 기술을 가지고 그들이 겪고 있는 문제를 바로잡으려고 하는 노력이 결실을 얻지 못하는 것은 결코 그러한 기술 부족 때문에 생기는 것이 아닙니다.
-그것들은 자기배반 때문에 생겨납니다.
-p.224

우리는 함께 일하고 우리와 함께 살아가는 사람이 진정으로 누구인지 알지 못합니다.
-우리가 그들과 진정으로 함께 소통하기 전까지는 우리는 그들의 가치를 잘 모릅니다.
-우리의 위대함이란 다른 사람들의 위대한 점을 발견해 주는 것에 있습니다.
-p.280

- - +

· 약 6분

4월 21일 금요일

레벨 2를 시작한 뒤 내가 학습에 대한 방향을 잃어버렸다는 생각이 들었다.
+레벨 3, 4에서 나만의 강점을 가지고 싶어 고민을 많이 했다.
+단순히 스프링을 깊게 공부하는 건 효율이 많이 떨어진다고 생각했다.
+글쓰기 수상으로 받은 쿠폰을 사용해 브라운에게 커피챗을 신청했고, 사이드 프로젝트를 해보라는 답을 받았다.

나는 아이디어를 못내는 편인데 브라운이 아이디어까지 던져주셨다.
+Chat-GPT 서비스를 크루들에게 제공하고, 해당 크루들이 질문한 내용을 공유할 수 있는 건 어떤지?

기술이 목적인 사이드 프로젝트를 진행하면 좋을 것 같다는 답변을 들었고, 혼자 아니면 페어할 수 있을 정도의 인원으로 진행하면 좋겠다고 하셨다.
+프론트랑 간단하게 배포까지 해본 경험이 있어서 혼자해도 크게 어렵지 않을 것 같아서 혼자 하기로 마음을 먹었다.

이건 못참지

도메인 구입 성공?

커피챗이 끝나고 집으로 돌아가는 길에 바로 도메인을 구매하려고 namecheap에서 적당한 도메인이 없을까 검색을 계속했다.
+마치 어릴 때 했던 게임 닉네임 정하는 것처럼 시간이 오래 걸렸다.
+dev, io, chat 도메인이 후보였고 집 가는 길에 결정만 하다가 구매하지 못했다.

말랑의 DM

집에 가서 밥을 먹고 말랑이랑 DM 하다 프로젝트를 같이 하자는 이야기가 나왔다.
+우테코 최고 고수 말랑의 요구라 수락하지 않으면 후폭풍을 감당할 수 없었다.

이런저런 대화를 나누다가 난 빠르게 프로토타입을 만들어 보고 싶어서 프론트를 구현한다고 했고, 말랑은 GPT api를 조사하기로 했다.
+추가로 도메인에 관한 이야기를 하다가 woowachat이 언급되었고, namecheap에서 chat 도메인을 사용한 woowa.chat으로 구매했다.
+이후에 teco.chat으로 변경했다!

도메인 설정 및 배포

토요일에 구매한 도메인을 CDN, 보안 등 다양한 기능을 제공하는 Cloudflare에 도메인 등록을 했다.
+나에게 익숙한 Nuxt3를 사용하기로 했고, Cloudflare Pages를 이용하여 배포했다.

GPT

무료 크레딧을 사용하니 api limit이 있어 분당 3번밖에 사용할 수 없었다.
+일단 백엔드를 구축하기 전에는 무료 크레딧을 사용할 생각이다.

Sonarcloud

정적 코드 분석 도구로 Sonarcloud를 적용했다.
+Sonarcloud는 SonarQube의 SaaS 버전이고 사용이 매우 편하다.
+예전에 Sonarcloud를 사용할 땐 버튼 몇 번 누르면 적용할 수 있었는데, 이번에는 바로 github action을 사용하라는 안내 페이지로 이동했다.
+Sonarcloud가 자체적으로 github repository에 push 하면 정적 분석을 해주는 기능을 원했고, Administration -> Analysis Method에 Automatic Analysis를 설정하니 되었다.
+너무 꽁꽁 숨겨져있네

Tiptap

코드 하이라이팅 기능을 넣고 싶어서 Tiptap을 사용했다.
+Tiptap은 Headless WYSIWYG 에디터로 사용자 정의 기능에 특화되어있는 에디터다.
+아직 Tiptap이 제공하는 모든 기능을 자연스럽게 사용하지는 못하지만 CodeBlockLowlight 플러그인을 사용하여 코드 블록을 예쁘게 출력할 수 있었다.
+api 반환값 그대로 tiptap의 content에 설정했더니 코드 블록이 설정되지 않아서 백 틱 3개를 <pre><code>로 변환했다.
+추가로 띄어쓰기도 적용되지 않아서 \n<br>태그로 변환했다.
+변환하는 로직은 GPT의 도움을 많이 받았다.

const replaceCodeFences = (input: String) => {
const codeFencesRegex = /```([\w-]*)\n([\s\S]*?)\n```/g;
return input
.replace(codeFencesRegex, (match, p1, p2) => {
const languageClass = p1 ? ` class="language-${p1}"` : "";
return `<pre><code${languageClass}>${p2}</code></pre>`;
})
.replace(/\n/g, "<br>");
};

Tiptap을 적용하니 다음과 같이 깔끔한 코드 블록을 볼 수 있었다.

tecochat

폰트 및 favicon 적용

타이틀은 배달의민족 도현체, 내용은 IBM Plex Sans를 사용했다.
+추가로 favicon도 간단하게 적용해서 만족스러웠다.

+ + \ No newline at end of file diff --git a/page/27.html b/page/27.html index 45cae2b75..6e2341ad5 100644 --- a/page/27.html +++ b/page/27.html @@ -13,32 +13,42 @@ - - + +
-

· 약 6분

InnoDB 스토리지 엔진의 잠금

MySQL에서 제공하는 잠금과 별개로 스토리지 엔진 내부에서 로우 단위의 잠금을 지원한다.
-보통 명시적으로 잠금을 사용하는 경우는 드물고, 격리 수준에 따라 묵시적으로 잠금이 사용된다.

동시성 제어 방식에는 낙관적인 방식과 비관적인 방식이 있다.
-InnoDB는 기본적으로 MVCC(다중 버전 동시성 제어)를 통해 낙관적인 방식을 사용하고 락을 통해 특정 상황에서 비관적인 방식을 사용한다.

낙관적 동시성 제어(OCC, Optimistic concurrency control)

트랜잭션이 서로 충돌하지 않는다고 가정하는 방식

비관적 동시성 제어(PCC, Pessimistic Concurrency Control)

트랜잭션이 충돌하는 가정하에 잠금을 거는 방식
-일반적으로 Shared Lock, Exclusive Lock을 통해 이를 구현한다.

Shared & Exclusive Locks

InnoDB는 로우 단위의 잠금을 수행할 때 공유 잠금과 배타적 잠금을 사용한다.

공유 잠금(S, shared lock)

데이터 조회를 위한 락, 읽기 잠금(read lock)으로도 불린다.
-다른 트랜잭션에서 읽기가 가능하지만, 쓰기는 불가능하다.
-예) SELECT * FROM table_name WHERE id = 1 LOCK IN SHARE MODE;

배타적 잠금(X, exclusive lock)

데이터 변경을 위한 락, 쓰기 잠금(write lock)으로도 불린다.
-락을 건 트랜잭션만이 해당 데이터에 접근 가능하다. 다른 트랜잭션의 경우 읽기, 쓰기가 불가능하다.
-예) SELECT * FROM table_name WHERE id = 1 FOR UPDATE;

Intention Locks

InnoDB는 로우 단위 잠금과 테이블 잠금의 공존을 위해 인텍션 잠금을 지원한다.
-테이블에 있는 로우에 대해서 나중에 요청되는 것이 어떤 형태의 잠금인지 가리키기 위해 사용된다.
-기본적으로 로우 단위 잠금을 수행하기 전에 인텐션 잠금을 먼저 획득한다.
-인텐션 락은 기본적으로 충돌을 방지하고 데드락을 방지하는 역할을 한다.

인텐션 공유 잠금(IS, intention shared lock)

트랜잭션이 테이블의 개별 로우에 대한 공유 잠금을 수행하는 것을 의미한다.

인텐션 배타적 잠금(IX, intention exclusive lock)

트랜잭션이 테이블의 개별 로우에 대한 배타적 잠금을 수행하는 것을 의미한다.

잠금간의 호환성

XIXSIS
XConflictConflictConflictConflict
IXConflictCompatibleConflictCompatible
SConflictConflictCompatibleCompatible
ISConflictCompatibleCompatibleCompatible

Record Locks

레코드 자체만을 잠그는 락이다.
-InnoDB 스토리지 엔진은 레코드 자체가 아니라 인덱스의 레코드를 잠근다.

Gap Locks

레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 락이다.
-레코드와 레코드 사이의 간격에 새로운 레코드가 생성되는 것을 제어하고, 넥스트 키 락의 일부로 사용된다.

Next-Key Locks

레코드 락과 갭 락을 합쳐놓은 형태의 잠금으로 레코드와 그 레코드 앞의 갭 락을 포함한다.
-REPEATABLE READ 격리 수준에서 팬텀 리드를 방지하기 위한 잠금이다.

AUTO-INC Locks

AUTO_INCREMENT 칼림이 사용된 테이블에 동시에 여러 레코드가 INSERT되는 경우, 각 레코드는 중복되지 않고 저장된 순서대로 증가하는 일련번호 값을 가져야 한다.
-InnoDB 는 내부적으로 AUTO-INC 락이라고 하는 테이블 수준의 잠금을 사용한다.
-트랜잭션과 관계 없이 INSERTREPLACE 문장에서 AUTO_INCREMENT 값을 가져오는 순간만 락이 걸렸다가 해제된다.

잠금 예시

-- 레코드는 id 기준 10, 20, 30, 40, 50이 있다고 가정
-- Record Locks: 10에 대해 락이 걸린다.
SELECT * FROM table_name where id = 10 for update;

-- Gap Locks: 51부터 PositiveInfinity까지 락이 걸린다.
SELECT * FROM table_name where id > 100 for update;

-- Next-Key Locks: 21부터 30, 31부터 40에 락이 걸린다.
SELECT * FROM table_name where id BETWEEN 25 AND 35 for update;

참고 자료

Real My SQL 8.0 - 5장 트랜잭션과 잠금, 백은빈, 이성욱
-Optimistic and Pessimistic record locking, IBM
-MySQL Innodb Locks, cecil1018
-MySQL 8.0 InnoDB Locks, MySQL
-Locks Set by Different SQL Statements in InnoDB, MySQL

- - +

· 약 6분

책 정보

상자 밖에 있는 사람
+아빈저연구소

자기기만과 자기배반

책에서는 자기기만과 자기배반에 대한 내용을 다룬다.

  • 자기기만: 자신의 문제를 인정하지 않는 것
  • 자기배반: 다른 사람을 위해 무언가 해야만 한다는 생각을 반하는 행위

자기배반을 한다면 자기기만 상태가 된다.
+자기기만 상태에 빠지는 것을 책에서는 상자 안에 들어간다고 표현한다.

읽고 나서

최근에 읽은 책 중 가장 마음이 불편했다.
+그렇기에 더더욱 나에게 필요한 내용이 담겨있었다.

살면서 많은 선택의 순간이 존재했고, 그 순간마다 자기배반을 택하는 경우가 많았다.
+작게는 집안일을 해야 하는데 몸이 조금 힘들다고 하지 않거나
+크게는 잘못을 인정해야 하는 상황에서 그러지 않은 경우가 있었다.
+이런 상황이 반복되어 결국 상자 안에 나 자신을 가두는 경우가 많았다.

더 나은 삶을 위해 내가 상자 안에 있는지 지속적으로 확인하고, 상자 밖으로 나가려는 연습을 해야겠다.
+넓은 시선을 가지고, 항상 내가 틀릴 수 있다는 것을 생각하고 살아가자.

밑줄 친 문장들

우리의 생각은 지식보다 작다.
+우리의 지식은 사랑보다 작다.
+우리의 사랑은 존재보다 작다.
+그리고 우리가 생각하는 나는 실제의 나보다 그만큼 작다.
+R. D. 랭
+p.19

우리가 외적으로 어떤 행동을 하든지 간에, 사람들은 우리 마음에서 그들을 어떻게 대하고 있는지에 따라 주로 반응합니다.
+우리가 사람들에 대해 어떻게 느끼게 되는지는 우리가 상자 안에 있는지 혹은 상자 밖에 있는지에 따라 달라지게 됩니다.
+p.66

비난은 감정에 속하고 낙관은 의지에 속한다.
+인간은 감정보다 더 큰 존재이다.
+알랭, 탁닛한
+p.103

우리가 자신에게만 집중하고 있는 한, 혼자서 일하는 것 이상의 창조적인 결과나 협력을 이끌어 낸다는 것은 불가능합니다.
+오늘날 경제 환경에서는 혼자서는 일의 결과를 탁월하게 만들어 내기가 어렵습니다.
+내가 중심이어야 된다는 폐쇄적인 사고는 함께 일하는 사람들의 열정을 불러오지 못합니다.
+p.175

솔직함은 우리의 문제를 해결하는 열쇠입니다.
+그것은 자신의 행동과 관련된 사람에 대해 기꺼이 사과를 하는 것입니다.
+그것만이 실타래처럼 엉킨 관계의 문제를 해결할 수 있기 때문이죠.
+p.188

누군가를 나와 같이 동일한 가치를 지닌 한 인간으로 생각해서 그 사람을 위해 내가 상자 밖에 계속 머무르고 싶은 열망이 생길 때, 나는 이미 그 사람에 대해 상자 밖에 있다.
+p.214

대부분의 사람들이 관계 기술을 가지고 그들이 겪고 있는 문제를 바로잡으려고 하는 노력이 결실을 얻지 못하는 것은 결코 그러한 기술 부족 때문에 생기는 것이 아닙니다.
+그것들은 자기배반 때문에 생겨납니다.
+p.224

우리는 함께 일하고 우리와 함께 살아가는 사람이 진정으로 누구인지 알지 못합니다.
+우리가 그들과 진정으로 함께 소통하기 전까지는 우리는 그들의 가치를 잘 모릅니다.
+우리의 위대함이란 다른 사람들의 위대한 점을 발견해 주는 것에 있습니다.
+p.280

+ + \ No newline at end of file diff --git a/page/28.html b/page/28.html index 2d87da999..0da9317e7 100644 --- a/page/28.html +++ b/page/28.html @@ -13,26 +13,32 @@ - - + +
-

· 약 5분

MySQL 엔진의 잠금

MySQL에서의 락은 스토리지 엔진 레벨과, MySQL 엔진 레벨로 나눌 수 있다.
-MySQL 엔진 레벨의 잠금은 모든 스토리지 엔진에 영향을 미친다.

글로벌 락(Global lock)

MySQL에서 제공하는 잠금 중 가장 넓은 범위를 가지고 있는 잠금이다.

  • 영향을 미치는 범위는 해당 서버 전체이다.
  • 작업 대상 테이블, 데이터베이스 상관 없이 동일하게 영향을 받는다.

한 세션에서 글로벌 락을 획득하면 해제 될 때 까지 조회를 제외한 대부분의 명령이 대기 상태가 된다.
-데이터베이스에 존재하는 MyISAM이나 MEMORY 테이블에 대해 일관된 백업을 받아야할 때 사용한다.
-InnoDB 스토리지 엔진에서는 백업 시 조금 더 가벼운 백업 락을 사용할 수 있다.

-- GLOBAL LOCK
FLUSH TABLES WITH READ LOCK;
-- UNLOCK
UNLOCK TABLES;

-- BACKUP LOCK
LOCK INSTANCE FOR BACKUP;
-- UNLOCK
UNLOCK INSTANCE;
MyISAM

MySQL 5.5 버전 이전의 기본 스토리지 엔진이다.
-트랜잭션을 지원하지 않고, SELECT 작업 속도가 빠르다.

테이블 락(Table lock)

개별 테이블 단위로 설정되는 잠금이다.
-명시적 또는 묵시적으로 특정 테이블의 락을 획득할 수 있다.
-묵시적 락은 MyISAM이나 MEMORY 테이블에 데이터를 변경하는 쿼리를 실행하면 발생한다.
-InnoDB 테이블에는 DML 쿼리는 무시되고 DDL 일 경우에만 묵시적으로 락을 획득한다.

-- TABLE LOCK
LOCK TABLES table_name [ READ | WRITE ]

-- UNLOCK
UNLOCK TABLES;

네임드 락(Named lock)

임의의 문자열에 대한 잠금을 설정할 수 있는 잠금으로 유저 레벨 락으로도 불린다.
-여러 스레드나 프로세스가 동일한 데이터를 수정하려는 경우, 동시에 수정하지 못하도록 보호할 수 있다.

-- aGVyYg== 라는 문자열에 대한 잠금 획득, 이미 잠금을 사용중인 경우 1초 동안만 대기
SELECT GET_LOCK('aGVyYg==', 1);

-- 문자열에 대한 잠금이 설정되어 있는지 확인한다.
SELECT IS_FREE_LOCK('aGVyYg==');

-- 문자열에 대한 잠금을 해제한다.
SELECT RELEASE_LOCK('aGVyYg==');

-- 위 3개 함수 모두 정상적으로 락을 획득하거나 해제한 경우에 1을, 아니면 0을 반환한다.

-- 모든 문자열에 대한 잠금을 해제한다. 해제된 잠금의 개수를 반환한다.
SELECT RELEASE_ALL_LOCKS();

메타데이터 락(Metadata lock)

데이터베이스 객체의 이름이나 구조를 변경하는 경우 획득하는 잠금이다.
-명시적으로 획득 또는 해제 할 수 없지만 테이블의 이름을 변경하는 경우 자동으로 획득한다.
-보통 배치 프로그램에서 실시간으로 테이블을 바꿔야하는 경우에 사용된다.

-- 배치 프로그램에서 별도의 임시 테이블에 서비스용 랭킹 데이터 생성 후 기존 테이블을 백업하는 경우
-- 아래 구문 실행 시 메타데이터 락을 자동으로 획득한다.
RENAME TABLE rank TO rank_backup, rank_new TO rank;

참고 자료

Real My SQL 8.0 - 5장 트랜잭션과 잠금, 백은빈, 이성욱
-MySQL의 User Level Lock를 활용한다면?, gywndi
-Locking Functions, MySQL 5.7 Reference
-Locking Functions, MySQL 8.0 Reference

- - +

· 약 6분

InnoDB 스토리지 엔진의 잠금

MySQL에서 제공하는 잠금과 별개로 스토리지 엔진 내부에서 로우 단위의 잠금을 지원한다.
+보통 명시적으로 잠금을 사용하는 경우는 드물고, 격리 수준에 따라 묵시적으로 잠금이 사용된다.

동시성 제어 방식에는 낙관적인 방식과 비관적인 방식이 있다.
+InnoDB는 기본적으로 MVCC(다중 버전 동시성 제어)를 통해 낙관적인 방식을 사용하고 락을 통해 특정 상황에서 비관적인 방식을 사용한다.

낙관적 동시성 제어(OCC, Optimistic concurrency control)

트랜잭션이 서로 충돌하지 않는다고 가정하는 방식

비관적 동시성 제어(PCC, Pessimistic Concurrency Control)

트랜잭션이 충돌하는 가정하에 잠금을 거는 방식
+일반적으로 Shared Lock, Exclusive Lock을 통해 이를 구현한다.

Shared & Exclusive Locks

InnoDB는 로우 단위의 잠금을 수행할 때 공유 잠금과 배타적 잠금을 사용한다.

공유 잠금(S, shared lock)

데이터 조회를 위한 락, 읽기 잠금(read lock)으로도 불린다.
+다른 트랜잭션에서 읽기가 가능하지만, 쓰기는 불가능하다.
+예) SELECT * FROM table_name WHERE id = 1 LOCK IN SHARE MODE;

배타적 잠금(X, exclusive lock)

데이터 변경을 위한 락, 쓰기 잠금(write lock)으로도 불린다.
+락을 건 트랜잭션만이 해당 데이터에 접근 가능하다. 다른 트랜잭션의 경우 읽기, 쓰기가 불가능하다.
+예) SELECT * FROM table_name WHERE id = 1 FOR UPDATE;

Intention Locks

InnoDB는 로우 단위 잠금과 테이블 잠금의 공존을 위해 인텍션 잠금을 지원한다.
+테이블에 있는 로우에 대해서 나중에 요청되는 것이 어떤 형태의 잠금인지 가리키기 위해 사용된다.
+기본적으로 로우 단위 잠금을 수행하기 전에 인텐션 잠금을 먼저 획득한다.
+인텐션 락은 기본적으로 충돌을 방지하고 데드락을 방지하는 역할을 한다.

인텐션 공유 잠금(IS, intention shared lock)

트랜잭션이 테이블의 개별 로우에 대한 공유 잠금을 수행하는 것을 의미한다.

인텐션 배타적 잠금(IX, intention exclusive lock)

트랜잭션이 테이블의 개별 로우에 대한 배타적 잠금을 수행하는 것을 의미한다.

잠금간의 호환성

XIXSIS
XConflictConflictConflictConflict
IXConflictCompatibleConflictCompatible
SConflictConflictCompatibleCompatible
ISConflictCompatibleCompatibleCompatible

Record Locks

레코드 자체만을 잠그는 락이다.
+InnoDB 스토리지 엔진은 레코드 자체가 아니라 인덱스의 레코드를 잠근다.

Gap Locks

레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 락이다.
+레코드와 레코드 사이의 간격에 새로운 레코드가 생성되는 것을 제어하고, 넥스트 키 락의 일부로 사용된다.

Next-Key Locks

레코드 락과 갭 락을 합쳐놓은 형태의 잠금으로 레코드와 그 레코드 앞의 갭 락을 포함한다.
+REPEATABLE READ 격리 수준에서 팬텀 리드를 방지하기 위한 잠금이다.

AUTO-INC Locks

AUTO_INCREMENT 칼림이 사용된 테이블에 동시에 여러 레코드가 INSERT되는 경우, 각 레코드는 중복되지 않고 저장된 순서대로 증가하는 일련번호 값을 가져야 한다.
+InnoDB 는 내부적으로 AUTO-INC 락이라고 하는 테이블 수준의 잠금을 사용한다.
+트랜잭션과 관계 없이 INSERTREPLACE 문장에서 AUTO_INCREMENT 값을 가져오는 순간만 락이 걸렸다가 해제된다.

잠금 예시

-- 레코드는 id 기준 10, 20, 30, 40, 50이 있다고 가정
-- Record Locks: 10에 대해 락이 걸린다.
SELECT * FROM table_name where id = 10 for update;

-- Gap Locks: 51부터 PositiveInfinity까지 락이 걸린다.
SELECT * FROM table_name where id > 100 for update;

-- Next-Key Locks: 21부터 30, 31부터 40에 락이 걸린다.
SELECT * FROM table_name where id BETWEEN 25 AND 35 for update;

참고 자료

Real My SQL 8.0 - 5장 트랜잭션과 잠금, 백은빈, 이성욱
+Optimistic and Pessimistic record locking, IBM
+MySQL Innodb Locks, cecil1018
+MySQL 8.0 InnoDB Locks, MySQL
+Locks Set by Different SQL Statements in InnoDB, MySQL

+ + \ No newline at end of file diff --git a/page/29.html b/page/29.html index 1d025761f..85d8334ad 100644 --- a/page/29.html +++ b/page/29.html @@ -13,36 +13,26 @@ - - + +
-

· 약 10분

트랜잭션(Transaction)

데이터베이스에서 논리적 기능을 수행하기 위한 작업의 단위를 말한다.
-트랜잭션은 작업의 완전성과 데이터의 정합성을 보장해 준다.
-논리적인 작업 셋을 완벽하게 처리하거나, 오류 시 작업의 일부만 적용되는 현상을 막아준다.

트랜잭션의 속성(ACID)

원자성(Atomicity): 트랜잭션 내에서 실행된 작업들은 모두 성공하거나, 실패해야 한다.
-일관성(Consistency): 트랜잭션이 수행되기 전과 후에 데이터베이스가 일관된 상태를 유지해야 한다.
-격리성(Isolation): 각각의 트랜잭션은 독립적이라 서로에게 영향을 주지 않아야 한다.
-지속성(Durability): 트랜잭션이 성공적으로 완료된다면 영구적으로 결과에 반영되어야 한다.

트랜잭션 주의사항

트랜잭션은 꼭 필요한 최소의 코드에만 적용하는 것이 좋다.(트랜잭션의 범위를 최소화하라)
-구현해야 하는 업무에 따라 트랜잭션을 묶거나 트랜잭션에서 제외하고, 네트워크 작업이 있는 경우 반드시 트랜잭션에서 배제해야 한다.

왜 네트워크 작업이 있을 때 트랜잭션에서 배제해야 할까? 🤔

데이터의 일관성과 안전성을 보장하기 위해 배제해야 한다.
-네트워크 작업을 트랜잭션 내부에 포함한다면 다음과 같은 문제가 발생할 수 있다.

  • 네트워크 작업이 중간에 실패할 가능성(안전성 X)
  • 통신으로 인해 데이터가 변경될 수 있는 부분(일관성 X)

격리 수준(Isolation level)

여러 트랜잭션이 동시에 처리될 때 특정 트랜잭션이 다른 트랜잭션에서 데이터의 조회 및 변경을 허용할지 결정하는 것을 말한다.
-격리 수준이 높아질 수록 동시 처리 성능이 떨어지는 것이 일반적이지만, SERIALIZABLE이 아니라면 크게 성능의 저하가 발생하지 않는다.

READ UNCOMMITTED

각 트랜잭션에서의 변경 내용이 COMMIT이나 ROLLBACK 여부에 상관없이 다른 트랜잭션에서 보인다.
-더티 리드 현상이 발생하기 때문에 정합성의 문제가 많은 격리 수준이다.
-MySQL 사용시 최소 READ COMMITTED 이상의 격리 수준 사용을 권장한다.

READ COMMITTED

트랜잭션에서 데이터를 변경하더라도 COMMIT이 완료된 데이터만 다른 트랜잭션에서 조회할 수 있다.
-오라클 DBMS에서 기본으로 사용되는 격리 수준이며, 온라인 서비스에서 가장 많이 선택되는 격리 수준이다.
-REPEATABLE READ가 보장되지 않기 때문에 NON-REPEATABLE READ 문제가 발생한다.

REPEATABLE READ

트랜잭션이 시작되기 전에 COMMIT이 완료된 내용에 대해서만 조회할 수 있다.
-MySQL의 InnoDB 스토리지 엔진에서 기본으로 사용되는 격리 수준이다.
-MVCC를 이용해 언두(Undo) 영역에 백업된 이전 데이터를 이용해 동일 트랜잭션 내에서는 동일한 결과를 보여줄 수 있게 보장한다.
-동일한 결과를 보장하는 방법은 다음과 같다.

  • 모든 InnoDB 트랜잭션은 순차적으로 증가하는 고유한 트랜잭션 번호를 가진다.
  • Undo 영역에 백업된 레코드에는 변경을 발생시킨 트랜잭션의 번호가 포함되어있다.
  • Undo 영역의 백업된 데이터는 스토리지 엔진이 불필요하다고 판단하는 경우 삭제된다.
  • REPEATABLE READ 격리 수준에서는 MVCC를 보장하기 위해 가장 오래된 트랜잭션 번호보다 앞선 Undo 영역의 데이터는 삭제하지 않는다.

InnoDB에서는 갭 락과 넥스트 키 락을 이용하여 팬텀 리드 현상을 방지한다.

갭 랍(Gap lock)과 넥스트 키 락(Next-key lock)

갭 락: 레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 락이다.
-넥스트 키 락: 레코드 락과 갭 락을 합쳐놓은 형태의 잠금으로 레코드와 그 레코드 앞의 갭 락을 포함한다.

MVCC(Multi Version Concurrency Control)

동시성을 제어하는 방법 중 하나로 하나의 레코드에 대해 여러 개의 버전이 동시에 관리되는 것이다.

  • PostgreSQL은 다중 버전의 데이터를 저장하는 것으로 MVCC를 구현한다.
  • Oracle, InnoDB는 Undo log를 이용해 이 기능을 구현한다.(최신 버전의 데이터만 DB에 저장)

잠금을 사용하지 않는 읽관된 읽기를 제공하는 것이 목적이다.

SERIALIZABLE

트랜잭션을 순차적으로 진행시키는 격리 수준이고 따라서 동시 처리 성능도 다른 격리 수준보다 떨어진다.
-트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서는 접근할 수 없고 단순한 읽기 작업도 공유 잠금(읽기 잠금)을 획득해야만 한다.
-InnoDB에서는 팬텀 리드 현상이 REPEATABLE READ 격리 수준에서 발생하지 않기 때문에 굳이 사용할 필요는 없다.

격리 수준에 따른 부정합 문제

격리 수준에 따라 더티 리드, 반복 가능하지 않은 조회, 팬텀 리드 문제가 발생한다.

격리 수준 / 부정합 문제더티 리드반복 가능하지 않은 조회팬텀 리드
READ UNCOMMITTEDOOO
READ COMMITTEDXOO
REPEATABLE READXXO(InnoDB는 X)
SERIALIZABLEXXX

더티 리드(Dirty read)

어떤 트랜잭션에서 처리한 작업이 완료되지 않았어도 다른 트랜잭션에서 볼 수 있는 현상
-트랜잭션 격리 수준이 READ UNCOMMITTED일 때 발생한다.
-예) B가 레코드를 추가하고 커밋을 하지 않았지만, A가 해당 레코드를 조회할 수 있는 경우

반복 가능하지 않은 조회(Non-repeatable read)

한 트랜잭션 내의 같은 행에 두 번 이상 조회가 발생했는데, 그 값이 다른 현상
-예) A가 레코드를 여러 번 조회하던 중 B가 레코드를 변경하여 A가 조회한 값이 달라지는 경우

팬텀 리드(Phantom read, Phantom row)

한 트랜잭션 내에서 동일한 쿼리 수행시, 수행 결과가 다른 현상
-예) A가 레코드를 조회하고 B가 레코드를 추가하여 A가 다시 조회할 때 존재하지 않은 레코드가 조회되는 경우

참고 자료

Real My SQL 8.0 - 5장 트랜잭션과 잠금, 백은빈, 이성욱
-Isolation Level, MySQL

- - +

· 약 5분

MySQL 엔진의 잠금

MySQL에서의 락은 스토리지 엔진 레벨과, MySQL 엔진 레벨로 나눌 수 있다.
+MySQL 엔진 레벨의 잠금은 모든 스토리지 엔진에 영향을 미친다.

글로벌 락(Global lock)

MySQL에서 제공하는 잠금 중 가장 넓은 범위를 가지고 있는 잠금이다.

  • 영향을 미치는 범위는 해당 서버 전체이다.
  • 작업 대상 테이블, 데이터베이스 상관 없이 동일하게 영향을 받는다.

한 세션에서 글로벌 락을 획득하면 해제 될 때 까지 조회를 제외한 대부분의 명령이 대기 상태가 된다.
+데이터베이스에 존재하는 MyISAM이나 MEMORY 테이블에 대해 일관된 백업을 받아야할 때 사용한다.
+InnoDB 스토리지 엔진에서는 백업 시 조금 더 가벼운 백업 락을 사용할 수 있다.

-- GLOBAL LOCK
FLUSH TABLES WITH READ LOCK;
-- UNLOCK
UNLOCK TABLES;

-- BACKUP LOCK
LOCK INSTANCE FOR BACKUP;
-- UNLOCK
UNLOCK INSTANCE;
MyISAM

MySQL 5.5 버전 이전의 기본 스토리지 엔진이다.
+트랜잭션을 지원하지 않고, SELECT 작업 속도가 빠르다.

테이블 락(Table lock)

개별 테이블 단위로 설정되는 잠금이다.
+명시적 또는 묵시적으로 특정 테이블의 락을 획득할 수 있다.
+묵시적 락은 MyISAM이나 MEMORY 테이블에 데이터를 변경하는 쿼리를 실행하면 발생한다.
+InnoDB 테이블에는 DML 쿼리는 무시되고 DDL 일 경우에만 묵시적으로 락을 획득한다.

-- TABLE LOCK
LOCK TABLES table_name [ READ | WRITE ]

-- UNLOCK
UNLOCK TABLES;

네임드 락(Named lock)

임의의 문자열에 대한 잠금을 설정할 수 있는 잠금으로 유저 레벨 락으로도 불린다.
+여러 스레드나 프로세스가 동일한 데이터를 수정하려는 경우, 동시에 수정하지 못하도록 보호할 수 있다.

-- aGVyYg== 라는 문자열에 대한 잠금 획득, 이미 잠금을 사용중인 경우 1초 동안만 대기
SELECT GET_LOCK('aGVyYg==', 1);

-- 문자열에 대한 잠금이 설정되어 있는지 확인한다.
SELECT IS_FREE_LOCK('aGVyYg==');

-- 문자열에 대한 잠금을 해제한다.
SELECT RELEASE_LOCK('aGVyYg==');

-- 위 3개 함수 모두 정상적으로 락을 획득하거나 해제한 경우에 1을, 아니면 0을 반환한다.

-- 모든 문자열에 대한 잠금을 해제한다. 해제된 잠금의 개수를 반환한다.
SELECT RELEASE_ALL_LOCKS();

메타데이터 락(Metadata lock)

데이터베이스 객체의 이름이나 구조를 변경하는 경우 획득하는 잠금이다.
+명시적으로 획득 또는 해제 할 수 없지만 테이블의 이름을 변경하는 경우 자동으로 획득한다.
+보통 배치 프로그램에서 실시간으로 테이블을 바꿔야하는 경우에 사용된다.

-- 배치 프로그램에서 별도의 임시 테이블에 서비스용 랭킹 데이터 생성 후 기존 테이블을 백업하는 경우
-- 아래 구문 실행 시 메타데이터 락을 자동으로 획득한다.
RENAME TABLE rank TO rank_backup, rank_new TO rank;

참고 자료

Real My SQL 8.0 - 5장 트랜잭션과 잠금, 백은빈, 이성욱
+MySQL의 User Level Lock를 활용한다면?, gywndi
+Locking Functions, MySQL 5.7 Reference
+Locking Functions, MySQL 8.0 Reference

+ + \ No newline at end of file diff --git a/page/3.html b/page/3.html index ab9de6347..41bb78393 100644 --- a/page/3.html +++ b/page/3.html @@ -13,65 +13,27 @@ - - + +
-

· 약 21분

복제(Replication)

한 서버에서 다른 서버로 데이터를 동기화하는 것을 의미한다.
-원본 데이터를 가지는 서버를 Primary 또는 Source 라고 부르고, 복제된 데이터를 가지는 서버를 Secondary 또는 Replica 라고 부른다.

복제를 하는 이유

1. 스케일 아웃

사용자의 트래픽이 증가하는 경우, 데이터베이스에 가해지는 부하도 자연스럽게 증가한다.
-이를 처리하기 위해 복제를 통한 스케일 아웃을 적용하여 애플리케이션에서 사용하는 쿼리들을 각각의 데이터베이스로 분산 시킬 수 있다.

2. 데이터 백업

실제 운영되는 서비스가 사용하고 있는 DB에서 백업을 진행하는 경우, 서비스에 영향을 미칠 수 있다.
-따라서 실제 서비스에 영향이 가지 않도록 복제를 통해 Replica 서버를 구축하여, Replica 서버에서 복제를 진행하는 방법으로 영향을 최소화 할 수 있다.

3. 데이터 분석

백업과 마찬가지로 복잡하고 무거운 분석용 쿼리의 서비스에 영향을 미칠 수 있다.
-마찬가지로 복제를 사용해 분석용 쿼리를 사용할 수 있는 환경을 만들 수 있다.

4. 데이터의 지리적 분산

빠른 응답을 위해 애플리케이션 서버에 가깝게 서버를 구성하거나, 고가용성(High Availability)을 위해서도 사용된다.

바이너리 로그 파일 위치 기반 복제

MySQL 서버에서 발생하는 변경사항에 대한 로그 파일을 바이너리 로그라고 한다.
-바이너리 로그를 통해 데이터 변경, 테이블 구조 변경, 계정이나 권한 변경에 대한 정보가 저장된다.
-MySQL의 복제는 바이너리 로그 기반으로 구현되어 있다. 이를 Replica 서버로 전달하고 바이너리 로그 기반으로 데이터를 변경 사항을 반영한다.

스레드별 역할

Binary Log Dump Thread: 바이너리 로그의 내용을 Replica 서버로 전달
-Replication I/O Thread: Binary 로그 이벤트를 가져와 로컬 서버의 파일(Relay Log)로 저장
-Replication SQL Thread: 릴레이 로그 파일의 이벤트를 읽고 실행

바이너리 로그 방식의 문제점

바이너리 로그 방식은 서버에 장애가 발생했을 때 복제 토폴로지 변경이 까다롭다.
-토폴로지란 네트워크의 요소들을 물리적으로 연결해 놓은 것, 또는 그 연결 방식을 말한다.

위와 같이 Source 서버, Replica 2대가 존재하고, C 서버에 복제 지연이 되었을 때 문제가 발생한다.

A 서버에서 장애가 발생한다면 B 서버를 Source 서버로 승격하고, C에게 조회 쿼리를 분산시킨다.
-하지만 여기서 C 서버에는 A 서버와 동기화가 안되었으니 조회 시 문제가 발생한다.
-뒤늦게 B 서버와 동기화를 하려고 해도, 어떤 바이너리 로그, 어떤 위치와 동기화해야하는지 알기 어렵다.

글로벌 트랜잭션 아이디(GTID) 기반 복제

GTID 방식을 사용하여 참여한 모든 데이터베이스가 발생한 이벤트에 고유한 식별값을 부여한다면, 동기화에 대한 문제를 간단하게 해결할 수 있다.
-위의 예시와 같이 복제 지연과 함께 장애가 발생한다해도 특정 GTID 부터 복제를 재개하면 된다.

GTID

복제 토폴로지에 참여한 모든 서버에서 고유하도록 각 이벤트에 부여된 식별값
-[source_id]:[transaction_id]로 구성되며, source_id는 서버를 식별하기 위한 값이고 transaction_id는 커밋된 트랜잭션을 식별하기 위한 값으로 1씩 증가하는 형태로 발급된다.

복제 토폴로지

싱글 레플리카 복제 구성

가장 간단한 구성으로 제일 많이 사용하는 형태다.
-replica 서버를 읽기 전용, 예비 서버, 백업 용도로 많이 사용한다.

멀티 레플리카 복제 구성

2개의 replica 서버를 사용하는 형태다.
-하나의 replica는 예비 용도로 남겨두는 형태다.
-추후에 트래픽이 증가하는 경우 예비 용도의 replica를 사용함으로 읽기 요청의 부하 분산을 할 수 있다.

체인 복제 구성

replica 서버가 많은 경우 바이너리 로그를 전달하는 작업 자체가 부하가 될 수 있다.
-따라서 1:M:M 구조로 체인 복제 구성을 고려할 수 있다.

듀얼 소스 복제 구성

2개의 MySQL 서버 모두 읽기와 쓰기가 가능하도록 하는 구성이다.
-각 서버에서 변경된 데이터는 다른 서버에 반영된다.
-목적에 따라 ACTIVE-ACTIVE 형태 또는 ACTIVE-PASSIVE 형태로 사용할 수 있다.
-ACTIVE-PASSIVE 형태인 경우 싱글 레플리카 복제 구성과 동일해보이지만, ACTIVE 서버에서 문제가 발생하면 설정의 변경없이 PASSIVE 서버로 쓰기 작업을 전환할 수 있다는 것이 장점이다.

ACTIVE-ACTIVE, ACTIVE-PASSIVE

ACTIVE-ACTIVE: 2개의 서버 모두 쓰기 작업을 수행하는 형태
-ACTIVE-PASSIVE: 하나의 서버에서만 쓰기 작업을 수행하는 형태

멀티 소스 복제 구성

여러개의 source 서버와 하나의 replica 서버를 사용하는 구성이다.
-이는 source 서버의 데이터를 한 곳에 백업하는 용도로 사용, 여러 서버에 존재하는 데이터를 통합, 샤딩되어있는 테이블 데이터를 통합할 때 사용한다.

바이너리 로그 방식 Replication 구성하기

mysql 2대를 이용하여 replication을 구성하고, spring boot application으로 source, replica 데이터베이스에 접근해보는 예제이다.
-https://github.com/bbiac/db-replication

MySQL 환경 구성

MySQL 버전은 8.1을 사용했다.
-13306, 13307 포트를 사용해서 MySQL 서버 2대를 띄웠다.
-또한 사실 IP 대역으로 통신할 수 있도록 커스텀 네트워크를 추가했다.

version: '3.8'

services:
source:
platform: linux/x86_64
image: mysql:latest
restart: always
container_name: mysql-source
environment:
TZ: 'Asia/Seoul'
MYSQL_DATABASE: 'db'
MYSQL_USER: 'user'
MYSQL_PASSWORD: 'password'
MYSQL_ROOT_PASSWORD: 'password'
ports:
- "13306:3306"
volumes:
- db-source:/var/lib/mysql
- db-source:/var/lib/mysql-files
- ./docker/source.cnf:/etc/mysql/my.cnf
networks:
- mysql_network

replica:
platform: linux/x86_64
image: mysql:latest
restart: always
container_name: mysql-replica
environment:
TZ: 'Asia/Seoul'
MYSQL_DATABASE: 'db'
MYSQL_USER: 'user'
MYSQL_PASSWORD: 'password'
MYSQL_ROOT_PASSWORD: 'password'
ports:
- "13307:3306"
volumes:
- db-replica:/var/lib/mysql
- db-replica:/var/lib/mysql-files
- ./docker/replica.cnf:/etc/mysql/my.cnf
networks:
- mysql_network

volumes:
db-source:
db-replica:

networks:
mysql_network:
driver: bridge

또한 source, replica 각각 다음과 같이 db 설정을 했다.

설정설명
server_id각각의 mysql 마다 고유한 값을 가져야 한다.
log_bin바이너리 로그 파일 경로 설정으로 절대경로를 사용하지 않는다면 /var/lib/mysql 아래 해당 log_bin에 설정된 값으로 로그가 생성된다.
sync_binlogN개의 트랜잭션 당 바이너리 로그를 디스크와 동기화 작업을 하도록 한다. 1은 기본값으로 안정적이지만, 가장 느리다.
relay_log릴레이 로그 파일 경로 설정
relay_log_purge필요 없는 릴레이 로그 파일을 자동으로 삭제하는 옵션
read_only읽기 전용 설정
log_replica_updatesReplication SQL Thread로 인해 실행되는 정보를 바이너리 로그에 기록 추후에 소스 서버로 승격되는 경우를 고려하면 설정하는 것이 좋다.
/docker/source.cnf
[mysqld]
server_id=1
log_bin=mysql-bin
sync_binlog=1

도커 실행

docker-compose up 명령어로 docker-compose 설정으로 docker를 띄운다.
--d 옵션을 붙이면 백그라운드 모드로 실행된다.

docker-compose up -d

replication slave 권한 설정

REPLICATION SLAVE 권한이 설정되어 있어야 replica 서버에서 source 서버에 접근하여 로그를 읽어올 수 있다.
-source 서버에 접근하여 user 계정에 해당 권한을 설정해준다.

SOURCE 접속

docker exec -it mysql-source mysql -u root -p

user 계정에 REPLICATION SLAVE 권한 추가

GRANT REPLICATION SLAVE ON *.* TO 'user'@'%';
FLUSH PRIVILEGES;

SOURCE DB 정보 확인

replica 설정에 필요한 source db의 바이너리 로그 파일명과 Position을 확인한다.
-Position 값은 실제 파일의 바이트 수를 의미한다.
-확인한 File(SOURCE_LOG_FILE)과 Position(SOURCE_LOG_POS) 값은 replica 설정에서 사용한다.

SHOW MASTER STATUS;

+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 | 1082 | | | |
+------------------+----------+--------------+------------------+-------------------+

SOURCE ip 주소 확인

docker inspect -f 옵션을 사용하면 해당 컨테이너의 세부 정보를 확인할 수 있다.
-다음 명령어를 이용해 docker-compose 파일에 설정해둔 mysql_network에서 사용되는 사설 아이피 주소를 확인한다.

docker inspect -f "{{with index .NetworkSettings.Networks \"db-replication_mysql_network\"}}{{.IPAddress}}{{end}}" mysql-source

ip 주소가 나오지 않는 경우 docker inspect mysql-source로 확인한다.
-확인한 IP주소(SOURCE_HOST) 값은 replica 설정에서 사용한다.

replica mysql 접속

source db에 접속했던 방법과 동일하게 replica db에 접속한다.

docker exec -it mysql-replica mysql -u root -p

replica 설정

이전에 source db에서 얻었던 정보들을 사용하여 replica 설정을 진행한다.
-실제 DB 서버에서 복제하는 경우 추가적으로 source DB의 파일을 복제해야하지만 현재 복제할 데이터가 없기 때문에 해당 부분은 생략했다.
-SOURCE_HOST, SOURCE_LOG_FILE, SOURCE_LOG_POS 를 적절히 변경한다.

STOP REPLICA;

CHANGE REPLICATION SOURCE TO
SOURCE_HOST='172.29.0.2',
SOURCE_USER='user',
SOURCE_PASSWORD='password',
SOURCE_LOG_FILE='mysql-bin.000001',
SOURCE_LOG_POS=0,
GET_SOURCE_PUBLIC_KEY=1;

START REPLICA;

설정 확인

SHOW REPLICA STATUS;

+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+
| Replica_IO_State | Source_Host | Source_User | Source_Port | Connect_Retry | Source_Log_File | Read_Source_Log_Pos | Relay_Log_File | Relay_Log_Pos | Relay_Source_Log_File | Replica_IO_Running | Replica_SQL_Running | Replicate_Do_DB | Replicate_Ignore_DB | Replicate_Do_Table | Replicate_Ignore_Table | Replicate_Wild_Do_Table | Replicate_Wild_Ignore_Table | Last_Errno | Last_Error | Skip_Counter | Exec_Source_Log_Pos | Relay_Log_Space | Until_Condition | Until_Log_File | Until_Log_Pos | Source_SSL_Allowed | Source_SSL_CA_File | Source_SSL_CA_Path | Source_SSL_Cert | Source_SSL_Cipher | Source_SSL_Key | Seconds_Behind_Source | Source_SSL_Verify_Server_Cert | Last_IO_Errno | Last_IO_Error | Last_SQL_Errno | Last_SQL_Error | Replicate_Ignore_Server_Ids | Source_Server_Id | Source_UUID | Source_Info_File | SQL_Delay | SQL_Remaining_Delay | Replica_SQL_Running_State | Source_Retry_Count | Source_Bind | Last_IO_Error_Timestamp | Last_SQL_Error_Timestamp | Source_SSL_Crl | Source_SSL_Crlpath | Retrieved_Gtid_Set | Executed_Gtid_Set | Auto_Position | Replicate_Rewrite_DB | Channel_Name | Source_TLS_Version | Source_public_key_path | Get_Source_public_key | Network_Namespace |
+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+
| Waiting for source to send event | 172.25.0.3 | user | 3306 | 60 | mysql-bin.000003 | 1082 | mysql-relay-bin.000002 | 868 | mysql-bin.000003 | Yes | Yes | | | | | | | 0 | | 0 | 1082 | 1078 | None | | 0 | No | | | | | | 0 | No | 0 | | 0 | | | 1 | 5a396b02-41c6-11ee-a56d-0242ac190003 | mysql.slave_master_info | 0 | NULL | Replica has read all relay log; waiting for more updates | 86400 | | | | | | | | 0 | | | | | 1 | |
+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+

Replica_IO_Running, Replica_SQL_Running 값이 YES라면 정상적으로 replication 구성이 완료된 것이다.

설정을 마친 후 source db에 다음과 같이 create table 명령어를 입력한다.
-replica db에 동일한 member table이 생성된 것을 확인할 수 있다.

CREATE TABLE member
(
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255)
);

스프링 부트로 DB 접근하기

일반적인 트랜잭션의 경우 source, 읽기 전용 트랜잭션인 경우 replica로 요청이 가도록 구성해보자.

Environment 설정

다음과 같이 source, replica로 구분하여 설정한다.

application.yml
spring:
datasource:
source:
username: user
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:13306/db
replica:
username: user
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:13307/db

DataSourceType 설정

단순 문자열로도 구분할 수 있지만, enum을 이용해서 트랜잭션을 구분하도록 생성한다.
-Key는 추후에 빈 설정에 사용한다.

DataSourceType
public enum DataSourceType {
SOURCE(SOURCE_NAME),
REPLICA(REPLICA_NAME),
;

private final String key;

DataSourceType(String key) {
this.key = key;
}

public static class Key {
public static final String ROUTING_NAME = "ROUTING";
public static final String SOURCE_NAME = "SOURCE";
public static final String REPLICA_NAME = "REPLICA";
}
}

AbstractRoutingDataSource 설정

스프링이 지원해주는 AbstractRoutingDataSource를 상속받아 트랜잭션의 읽기 여부에 따라 다른 DataSource를 향하도록 설정한다.

정적 팩터리 메서드는 Map<DataSourceKey, DataSource>에 해당하는 값을 받아 데이터 소스를 설정한다.

  • setDefaultTargetDataSource: 기본 데이터 소스를 설정한다.
  • setTargetDataSources: 맵 형태로 받은 데이터 소스 값들을 설정한다.

determineCurrentLookupKey를 오버라이딩하여 트랜잭션의 읽기 여부에 따라 다른 DataSourceType을 반환하도록 설정한다.

  • isCurrentTransactionReadOnly() 메서드를 통해 트랜잭션이 읽기 전용인지 확인할 수 있다.
  • DataSourceType을 반환하도록 설정하고, 반환한 값에 해당하는 데이터 소스가 사용된다.
RoutingDataSource
public class RoutingDataSource extends AbstractRoutingDataSource {

private final Logger log = LoggerFactory.getLogger(getClass());

public static RoutingDataSource from(Map<Object, Object> dataSources) {
RoutingDataSource routingDataSource = new RoutingDataSource();
routingDataSource.setDefaultTargetDataSource(dataSources.get(DataSourceType.SOURCE));
routingDataSource.setTargetDataSources(dataSources);
return routingDataSource;
}

@Override
protected Object determineCurrentLookupKey() {
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();

if (readOnly) {
log.info("readOnly = true, request to replica");
return DataSourceType.REPLICA;
}
log.info("readOnly = false, request to source");
return DataSourceType.SOURCE;
}
}

DataSource 설정

위에서부터 순서대로 Source, Replica, RoutingDataSource, LazyConnectionDataSourceProxy 설정이다.
-스프링은 트랜잭션 시작시에 커넥션의 사용여부와 상관없이 커넥션을 확보한다.
-따라서 readOnly 트랜잭션이 설정된 메서드를 사용하더라도 미리 확보된 커넥션을 사용하기 때문에 replica db로 요청을 하지 않고 setDefaultTargetDataSource로 설정한 source db로 요청을 한다.
-LazyConnectionDataSourceProxy를 설정하는 경우 실제 DataSource를 사용하는 시점에 커넥션을 획득해서 사용하기 때문에 설정한대로 replica db로 조회 요청을 한다.

DataSourceConfiguration
@Configuration
public class DataSourceConfiguration {

@Bean
@Qualifier(SOURCE_NAME)
@ConfigurationProperties(prefix = "spring.datasource.source")
public DataSource sourceDataSource() {
return DataSourceBuilder.create().build();
}

@Bean
@Qualifier(REPLICA_NAME)
@ConfigurationProperties(prefix = "spring.datasource.replica")
public DataSource replicaDataSource() {
return DataSourceBuilder.create().build();
}

@Bean
@Qualifier(ROUTING_NAME)
public DataSource routingDataSource(
@Qualifier(SOURCE_NAME) DataSource sourceDataSource,
@Qualifier(REPLICA_NAME) DataSource replicaDataSource
) {
return RoutingDataSource.from(Map.of(
DataSourceType.SOURCE, sourceDataSource,
DataSourceType.REPLICA, replicaDataSource
));
}

@Bean
@Primary
public DataSource dataSource(
@Qualifier(ROUTING_NAME) DataSource routingDataSource
) {
return new LazyConnectionDataSourceProxy(routingDataSource);
}
}

최종적으로 DataSource 빈은 다음과 같은 형태가 된다.

동작 확인

간단하게 테스트를 작성해서 설정한대로 동작이 되는지 확인해보았다.
-save 메서드의 경우 @Transactional, findById 메서드의 경우 @Transactional(readOnly = true)가 설정되어있다.
-로그를 통해 save의 경우 source db로 findById의 경우 replica db로 요청을 하는 것을 알 수 있다.

MemberServiceTest
@SpringBootTest
class MemberServiceTest {

@Autowired
private MemberService memberService;

@Test
void 사용자를_저장한다() {
// RoutingDataSource log: readOnly = false
memberService.save("bbiac");
}

@Test
void 사용자를_조회한다() {
// RoutingDataSource log: readOnly = true
assertThatThrownBy(() -> memberService.findById(MAX_VALUE))
.isInstanceOf(NoSuchElementException.class);
}
}

DB에서는 확인하려면 root 계정으로 접속한 후 general log를 활성화 시킨다.

SET GLOBAL log_output = 'table';
SET GLOBAL general_log = 1;

general log를 활성화 한 후 읽기 전용 메서드를 실행한다.
-server_id, 실행한 쿼리문을 확인할 수 있다.

SELECT user_host, thread_id, server_id, convert(argument using utf8) FROM mysql.general_log where argument like '%select%';

+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+
| user_host | thread_id | server_id | convert(argument using utf8) |
+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+
| user[user] @ [172.25.0.1] | 277 | 2 | select m1_0.id,m1_0.name from member m1_0 where m1_0.id=9223372036854775807 |
+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+

확인 후 general log를 비활성화 한 후 비활성화 되었는지 확인한다.

SET GLOBAL general_log = 0;
SHOW VARIABLES LIKE '%general%';

+------------------+---------------------------------+
| Variable_name | Value |
+------------------+---------------------------------+
| general_log | OFF |
| general_log_file | /var/lib/mysql/4b6b9db98290.log |
+------------------+---------------------------------+

참고 자료

16장 복제, Real MySQL 8.0 - 백은빈, 이성욱
-Replication, MySQL Docs
-MySql - Master Slave Replication 구조 만들어보기
-Spring 레플리케이션 트랜잭션 처리 방식
-replication-datasource
-Simplified Guide to MySQL Replication with Docker Compose
-Dockerfile에서 자주 쓰이는 명령어
-CHANGE REPLICATION SOURCE TO Statement
-LazyConnectionDataSourceProxy
-데이터베이스 레플리케이션을 통한 쿼리 성능 개선 (feat. Mysql, SpringBoot)
-부하 분산을 위한 MySQL Replication 구성 및 쿼리 요청 분기
-Use Docker Compose, Docker

- - +

· 약 6분

성능 테스트

API의 요청이 많은 상황에서 서버가 어떻게 동작하는지 확인하는 테스트

시스템에 부하가 걸리면 문제 상황이 발생할 수 있다.
+다양한 상황에 대비해서 성능 테스트를 해야한다.

./test.png

스모크 테스트(Smoke Test)

최소한의 부하를 주어 시스템이 정상적으로 동작하는지 확인하는 테스트

VU를 최소한으로 두고, 짧은 시간을 가지고 테스트한다.
+다른 테스트를 시작하기 전에 스모크 테스트를 함으로써 테스트 스크립트에 오류가 없는지 확인할 수 있고, 성능 지표가 정상적으로 수집, 모니터링 되고 있는지 확인할 수 있다.

가상 사용자(VU)

가상 사용자는 서버 애플리케이션에 대해 특정 테스트를 실행한다.
+이는 다른 가상 사용자와 독립적으로 실행되며, 여러 가상 사용자를 사용하여 동시 연결을 할 수 있다.
+스레드라고 생각하면 된다.

스파이크 테스트(Spike Test)

사용량이 급증하는 상황에서 시스템이 견디고 성능에 문제가 없는지 확인하는 테스트

티켓 발급, 할인 쿠폰 발급과 같은 이벤트를 하는 경우 대규모 트래픽이 들어온다.
+스파이크 테스트를 통해 급증하는 부하 상황에서 시스템이 어떻게 동작하고, 부하를 잘 버티는지 확인할 수 있다.

부하 테스트(Load Test)

목푯값에 해당되는 부하를 견딜 수 있을지 확인하는 테스트

일반적인 부하 상황에서 시스템이 어떻게 동작하는지 확인하는 테스트다.
+램프업 또는 묙푯값에 해당하는 부하 기간동안 성능이 문제가 있는지 확인하고, 시스템 변경 후에도 부하 테스트를 돌려 동일하게 목푯값을 처리하는지 확인할 수 있다.

램프 업(Ramp-up)

부하 테스트를 위해 설정한 가상 사용자 수에 도달하는 데 걸리는 시간

스트레스 테스트(Stress Test)

시스템의 최대치에 해당되는 부하를 받았을 때 시스템이 어떻게 동작하는지 확인하는 테스트

그래프를 봤을 때 부하 테스트와 유사한 형태로 보이지만, 부하량이 다르다.
+일반적으로 평균적인 목푯값 대비 작게는 50% 이상, 필요의 경우 그 이상으로 부하를 준다.
+스트레스 테스트는 부하 테스트를 실행한 후에만 실행해야 한다. 부하 테스트가 이루어지지 않은 상황에서 스트레스 테스트를 실행하는 경우에는 병목 지점이나 문제 상황을 찾기 어려워진다.
+또한 부하 테스트에서 사용한 스크립트를 VU값(스레드 수)만 수정하여 재사용하는 것이 좋다.

내구 테스트(Endurance Test)

평균 사용률로 일정 부하를 지속적으로 주며 시스템이 문제되는 지점을 확인하는 테스트

흡수 테스트(Soak Test)라고도 하며, 기본적인 부하 테스트의 변형이라고 볼 수 있다.
+다른 테스트와 달리 긴 시간동안 테스트를 하는 것이 특징이며, 메모리 누수 문제와 같이 장시간 애플리케이션을 실행할 때 시스템의 문제가 발생하는 부분을 확인하는 것이 목적이다.

중단점 테스트(Breakpoint Test)

임계 지점을 찾기 위해 부하를 점진적으로 증가시키며 진행하는 테스트

문제되는 부분을 더 빨리 찾기 위해 다른 테스트를 통과한 다음에 중단점 테스트를 진행하고, 이 때 점진적으로 부하를 늘려나가는 것이 좋다.
+스트레스 테스트를 성능 튜닝과 반복해서 진행한다면, 시스템을 더욱 발전시킬 수 있다.
+다만 Auto Scaling이 적용된 클라우드 환경에서는 진행하지 않아야 한다.

참고 자료

Load test types, k6
+자바 최적화 - 벤저민 J. 에번스, 제임스 고프, 크리스 뉴랜드
+아마존 웹 서비스 부하 테스트 입문 - 나카가와 타루하치, 모리시타 켄

+ + \ No newline at end of file diff --git a/page/30.html b/page/30.html index 23e9290b0..f5b1f9915 100644 --- a/page/30.html +++ b/page/30.html @@ -13,30 +13,36 @@ - - + +
-

· 약 5분

테스트 대역이란?

모든 유형의 테스트를 위한 가짜 의존성을 의미하고, 테스트가 실행될 때 다른 객체를 대신한다.
-Gerard Meszaros의 xUnit Test Patterns라는 책에서는 테스트 대역을 다섯 가지(더미, 스텁, 스파이, 목, 페이크)로 구분한다.

테스트 대역의 기본 메커니즘은 다형성을 이용하는 방법이다.
-외부 서비스를 사용하는 코드를 테스트 하는 경우, 인터페이스를 정의하고 외부 서비스 대신 테스트 용도의 구현체를 생성하는 것이다.

테스트 대역의 타입 계층 구조

더미(Dummy)

가장 단순하고, 원시적인 유형의 테스트 대역이다.
-기본적으로 아무 일도 하지 않는 구현체로 인스턴스화가 필요한 경우 사용한다.
-만약 메서드가 무언가 반환을 해야하는 경우 0, null과 같은 값을 반환한다.

스텁(Stub)

시나리오마다 다른 값(미리 준비 된 결과)을 반환한다.
-이를 통해 특정 조건에서 메서드가 예상한대로 동작하는지 확인할 수 있다.

스파이(Spy)

스텁과 유사하지만 호출 여부를 기록하거나 호출할 때 전달한 인자값을 기록할 수 있다.
-예) 메일 전송 기능을 가진 객체를 테스트 대역으로 구현했을 때 메일 전송 횟수를 기록한다.

목, 모의 객체(Mock)

목은 더미, 스텁, 스파이를 포함한다.
-호출 시 사전에 정의된 결과를 반환하고, 예상치 못한 호출이 있을 경우 예외를 던질 수 있다.
-또한 호출에 대한 검증을 할 수 있다.

가짜(Fake)

DOC와 동일한 기능을 제공하지만, 더욱 간단한 방법으로 구현된 것이다.
-예) 실제 데이터베이스와 유사하게 동작하는 가짜 객체를 만들어 테스트할 수 있다.

DOC(depended-on component)

의존 구성 요소, DOC를 테스트 더블로 대체할 수 있다.
-테스트 더블은 DOC와 동일한 API를 제공해야 한다.

상호작용에 따른 목과 스텁 구분

단위 테스트 p.149 에서는 테스트 대역을 크게 목과 스텁으로 구분한다.
-목은 SUT와 관련된 상호작용을 모방하고 검사하는 반면, 스텁은 단순 모방만 한다.

TestDoubleMockStub
포함 유형목, 스파이스텁, 더미, 페이크
용도외부로 나가는 상호작용을 모방하고 검사하는 데 사용내부로 들어오는 상호작용을 모방하는 데 사용
설명SUT가 상태를 변경하기 위한 의존성을 호출하는 것에 해당SUT가 입력 데이터를 얻기 위한 의존성을 호출하는 것에 해당
예시이메일 발송데이터 검색
SUT(system under test)

테스트 대상 시스템
-테스트를 하려는 대상

참고 자료

소프트웨어 장인 정신 이야기 - 3장 고급 테스트 주도 개발, 로버트 C. 마틴
-단위 테스트 - 5장 목과 테스트 취약성, 블라디미르 코리코프
-테스트 주도 개발 시작하기 - 7장 대역, 최범균
-테스트 더블, Martin Fowler
-테스트 관련 용어 정리, Johngrib
-Test Double, Gerard Meszaros

- - +

· 약 10분

트랜잭션(Transaction)

데이터베이스에서 논리적 기능을 수행하기 위한 작업의 단위를 말한다.
+트랜잭션은 작업의 완전성과 데이터의 정합성을 보장해 준다.
+논리적인 작업 셋을 완벽하게 처리하거나, 오류 시 작업의 일부만 적용되는 현상을 막아준다.

트랜잭션의 속성(ACID)

원자성(Atomicity): 트랜잭션 내에서 실행된 작업들은 모두 성공하거나, 실패해야 한다.
+일관성(Consistency): 트랜잭션이 수행되기 전과 후에 데이터베이스가 일관된 상태를 유지해야 한다.
+격리성(Isolation): 각각의 트랜잭션은 독립적이라 서로에게 영향을 주지 않아야 한다.
+지속성(Durability): 트랜잭션이 성공적으로 완료된다면 영구적으로 결과에 반영되어야 한다.

트랜잭션 주의사항

트랜잭션은 꼭 필요한 최소의 코드에만 적용하는 것이 좋다.(트랜잭션의 범위를 최소화하라)
+구현해야 하는 업무에 따라 트랜잭션을 묶거나 트랜잭션에서 제외하고, 네트워크 작업이 있는 경우 반드시 트랜잭션에서 배제해야 한다.

왜 네트워크 작업이 있을 때 트랜잭션에서 배제해야 할까? 🤔

데이터의 일관성과 안전성을 보장하기 위해 배제해야 한다.
+네트워크 작업을 트랜잭션 내부에 포함한다면 다음과 같은 문제가 발생할 수 있다.

  • 네트워크 작업이 중간에 실패할 가능성(안전성 X)
  • 통신으로 인해 데이터가 변경될 수 있는 부분(일관성 X)

격리 수준(Isolation level)

여러 트랜잭션이 동시에 처리될 때 특정 트랜잭션이 다른 트랜잭션에서 데이터의 조회 및 변경을 허용할지 결정하는 것을 말한다.
+격리 수준이 높아질 수록 동시 처리 성능이 떨어지는 것이 일반적이지만, SERIALIZABLE이 아니라면 크게 성능의 저하가 발생하지 않는다.

READ UNCOMMITTED

각 트랜잭션에서의 변경 내용이 COMMIT이나 ROLLBACK 여부에 상관없이 다른 트랜잭션에서 보인다.
+더티 리드 현상이 발생하기 때문에 정합성의 문제가 많은 격리 수준이다.
+MySQL 사용시 최소 READ COMMITTED 이상의 격리 수준 사용을 권장한다.

READ COMMITTED

트랜잭션에서 데이터를 변경하더라도 COMMIT이 완료된 데이터만 다른 트랜잭션에서 조회할 수 있다.
+오라클 DBMS에서 기본으로 사용되는 격리 수준이며, 온라인 서비스에서 가장 많이 선택되는 격리 수준이다.
+REPEATABLE READ가 보장되지 않기 때문에 NON-REPEATABLE READ 문제가 발생한다.

REPEATABLE READ

트랜잭션이 시작되기 전에 COMMIT이 완료된 내용에 대해서만 조회할 수 있다.
+MySQL의 InnoDB 스토리지 엔진에서 기본으로 사용되는 격리 수준이다.
+MVCC를 이용해 언두(Undo) 영역에 백업된 이전 데이터를 이용해 동일 트랜잭션 내에서는 동일한 결과를 보여줄 수 있게 보장한다.
+동일한 결과를 보장하는 방법은 다음과 같다.

  • 모든 InnoDB 트랜잭션은 순차적으로 증가하는 고유한 트랜잭션 번호를 가진다.
  • Undo 영역에 백업된 레코드에는 변경을 발생시킨 트랜잭션의 번호가 포함되어있다.
  • Undo 영역의 백업된 데이터는 스토리지 엔진이 불필요하다고 판단하는 경우 삭제된다.
  • REPEATABLE READ 격리 수준에서는 MVCC를 보장하기 위해 가장 오래된 트랜잭션 번호보다 앞선 Undo 영역의 데이터는 삭제하지 않는다.

InnoDB에서는 갭 락과 넥스트 키 락을 이용하여 팬텀 리드 현상을 방지한다.

갭 랍(Gap lock)과 넥스트 키 락(Next-key lock)

갭 락: 레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 락이다.
+넥스트 키 락: 레코드 락과 갭 락을 합쳐놓은 형태의 잠금으로 레코드와 그 레코드 앞의 갭 락을 포함한다.

MVCC(Multi Version Concurrency Control)

동시성을 제어하는 방법 중 하나로 하나의 레코드에 대해 여러 개의 버전이 동시에 관리되는 것이다.

  • PostgreSQL은 다중 버전의 데이터를 저장하는 것으로 MVCC를 구현한다.
  • Oracle, InnoDB는 Undo log를 이용해 이 기능을 구현한다.(최신 버전의 데이터만 DB에 저장)

잠금을 사용하지 않는 읽관된 읽기를 제공하는 것이 목적이다.

SERIALIZABLE

트랜잭션을 순차적으로 진행시키는 격리 수준이고 따라서 동시 처리 성능도 다른 격리 수준보다 떨어진다.
+트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서는 접근할 수 없고 단순한 읽기 작업도 공유 잠금(읽기 잠금)을 획득해야만 한다.
+InnoDB에서는 팬텀 리드 현상이 REPEATABLE READ 격리 수준에서 발생하지 않기 때문에 굳이 사용할 필요는 없다.

격리 수준에 따른 부정합 문제

격리 수준에 따라 더티 리드, 반복 가능하지 않은 조회, 팬텀 리드 문제가 발생한다.

격리 수준 / 부정합 문제더티 리드반복 가능하지 않은 조회팬텀 리드
READ UNCOMMITTEDOOO
READ COMMITTEDXOO
REPEATABLE READXXO(InnoDB는 X)
SERIALIZABLEXXX

더티 리드(Dirty read)

어떤 트랜잭션에서 처리한 작업이 완료되지 않았어도 다른 트랜잭션에서 볼 수 있는 현상
+트랜잭션 격리 수준이 READ UNCOMMITTED일 때 발생한다.
+예) B가 레코드를 추가하고 커밋을 하지 않았지만, A가 해당 레코드를 조회할 수 있는 경우

반복 가능하지 않은 조회(Non-repeatable read)

한 트랜잭션 내의 같은 행에 두 번 이상 조회가 발생했는데, 그 값이 다른 현상
+예) A가 레코드를 여러 번 조회하던 중 B가 레코드를 변경하여 A가 조회한 값이 달라지는 경우

팬텀 리드(Phantom read, Phantom row)

한 트랜잭션 내에서 동일한 쿼리 수행시, 수행 결과가 다른 현상
+예) A가 레코드를 조회하고 B가 레코드를 추가하여 A가 다시 조회할 때 존재하지 않은 레코드가 조회되는 경우

참고 자료

Real My SQL 8.0 - 5장 트랜잭션과 잠금, 백은빈, 이성욱
+Isolation Level, MySQL

+ + \ No newline at end of file diff --git a/page/31.html b/page/31.html index c803651ae..669f4811f 100644 --- a/page/31.html +++ b/page/31.html @@ -13,30 +13,30 @@ - - + +
-

· 약 6분

클래스 파일

자바 소스코드가 실행이 되려면 자바 컴파일러(javac)를 통해 소스코드를 클래스파일로 변환해야 한다.
-컴파일된 클래스파일은 어떤 구조로 되어있을까?

클래스 파일의 데이터 형식

8비트 바이트의 스트림으로 구성된다.
-16비트 및 32비트의 데이터는 각각 2개, 4개의 연속된 8비트를 읽어서 구성된다.
-멀티바이트의 경우 항상 big endian 순서로 저장된다.

u1 → unsigned 1byte
-u2 → unsigned 2byte
-u4 → unsigned 4byte

클래스 파일 구조

ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}

매직넘버

모든 클래스 파일은 0xCAFEBABE라는 매직넘버로 시작한다.
-보통 매직넘버는 파일 종류를 식별하는 용도로 사용된다.

클래스 파일 포맷 버전

클래스 파일 버전 값은 클래스로더의 호환성 보장을 위해 꼭 필요한 값이다.

  • Java 17 버전으로 빌드한다면 class version 61 ex) 00 00 00 3D

호환되지 않는 버전의 클래스 파일을 로딩하려고 하는 경우 런타임에 UnsupportedClassVersionError 예외가 발생한다.

class file format major versions

Java SEReleasedMajorSupported majors
8March 20145245 .. 52
9September 20175345 .. 53
10March 20185445 .. 54
11September 20185545 .. 55
12March 20195645 .. 56
13September 20195745 .. 57
14March 20205845 .. 58
15September 20205945 .. 59
16March 20216045 .. 60
17September 20216145 .. 61

상수 풀

2바이트의 상수의 개수값이 먼저오고 그 뒤로 코드에 등장하는 상수값이 모여있다.
-클래스명, 상수명, 상수 값, 필드명, 메서드명과 같은 값들이 존재한다.
-JVM은 코드 실행 시 런타임에 배치된 메모리가 아니라, 해당 상수 풀 테이블을 찾아보고 필요한 값을 참조한다.

액세스 플래그

클래스, 인터페이스와 같은 파일의 속성을 표시한다.
-예를 들어 public interface로 정의된 인터페이스의 플래그는 0x0601이다.

  • 계산은 다음과 같이 이루어진다. ACC_PUBLIC xor ACC_INTERFACE xor ACC_ABSTRACT

공식문서에 들어가면 각 플래그에 대한 설명 + 플래그 설정시 동시에 설정되면 안되는 플래그와 같은 설명이 자세하게 나와있다.

Class access and property modifiers

Flag NameValueInterpretation
ACC_PUBLIC0x0001Declared public; may be accessed from outside its package.
ACC_FINAL0x0010Declared final; no subclasses allowed.
ACC_SUPER0x0020Treat superclass methods specially when invoked by the invokespecial instruction.
ACC_INTERFACE0x0200Is an interface, not a class.
ACC_ABSTRACT0x0400Declared abstract; must not be instantiated.
ACC_SYNTHETIC0x1000Declared synthetic; not present in the source code.
ACC_ANNOTATION0x2000Declared as an annotation type.
ACC_ENUM0x4000Declared as an enum type.
ACC_MODULE0x8000Is a module, not a class or interface.

this_class

클래스명과 같은 이름을 표현하는 값으로, 상수 풀에서 클래스명과 일치하는 항목의 인덱스를 참조한다.
-해당 인덱스의 항목은 CONSTANT_Class_infoclass 형식의 값이어야 한다.

super_class

상수 풀에서 슈퍼 클래스의 이름과 일치하는 항목의 인덱스를 참조한다.
-아무것도 상속하지 않는 클래스의 경우 java.lang.Object의 인덱스 값이 들어있다.

interface, field, method

각각의 개수와, 정보에 대한 값이 들어있다.
-interface, field, method를 표시하는 방법이 각각 다르고, 접근자에 대한 플래그도 각각 다르다.

attributes

해당 클래스 파일에서 사용하는 추가 정보의 모음이다. 예) 소스파일명
-정해진 클래스 파일의 구조를 확장하는 역할을 한다.

클래스 파일 확인하면서 사용한 툴

IntelliJ plugin - BinEd
-IntelliJ plugin - jclasslib Bytecode Viewer

참고 자료

2장 JVM 이야기, 자바 최적화
-Class file in Java, File Format
-java se11 Class 파일 형식, Oracle
-java se17 Class 파일 형식, Oracle

- - +

· 약 5분

테스트 대역이란?

모든 유형의 테스트를 위한 가짜 의존성을 의미하고, 테스트가 실행될 때 다른 객체를 대신한다.
+Gerard Meszaros의 xUnit Test Patterns라는 책에서는 테스트 대역을 다섯 가지(더미, 스텁, 스파이, 목, 페이크)로 구분한다.

테스트 대역의 기본 메커니즘은 다형성을 이용하는 방법이다.
+외부 서비스를 사용하는 코드를 테스트 하는 경우, 인터페이스를 정의하고 외부 서비스 대신 테스트 용도의 구현체를 생성하는 것이다.

테스트 대역의 타입 계층 구조

더미(Dummy)

가장 단순하고, 원시적인 유형의 테스트 대역이다.
+기본적으로 아무 일도 하지 않는 구현체로 인스턴스화가 필요한 경우 사용한다.
+만약 메서드가 무언가 반환을 해야하는 경우 0, null과 같은 값을 반환한다.

스텁(Stub)

시나리오마다 다른 값(미리 준비 된 결과)을 반환한다.
+이를 통해 특정 조건에서 메서드가 예상한대로 동작하는지 확인할 수 있다.

스파이(Spy)

스텁과 유사하지만 호출 여부를 기록하거나 호출할 때 전달한 인자값을 기록할 수 있다.
+예) 메일 전송 기능을 가진 객체를 테스트 대역으로 구현했을 때 메일 전송 횟수를 기록한다.

목, 모의 객체(Mock)

목은 더미, 스텁, 스파이를 포함한다.
+호출 시 사전에 정의된 결과를 반환하고, 예상치 못한 호출이 있을 경우 예외를 던질 수 있다.
+또한 호출에 대한 검증을 할 수 있다.

가짜(Fake)

DOC와 동일한 기능을 제공하지만, 더욱 간단한 방법으로 구현된 것이다.
+예) 실제 데이터베이스와 유사하게 동작하는 가짜 객체를 만들어 테스트할 수 있다.

DOC(depended-on component)

의존 구성 요소, DOC를 테스트 더블로 대체할 수 있다.
+테스트 더블은 DOC와 동일한 API를 제공해야 한다.

상호작용에 따른 목과 스텁 구분

단위 테스트 p.149 에서는 테스트 대역을 크게 목과 스텁으로 구분한다.
+목은 SUT와 관련된 상호작용을 모방하고 검사하는 반면, 스텁은 단순 모방만 한다.

TestDoubleMockStub
포함 유형목, 스파이스텁, 더미, 페이크
용도외부로 나가는 상호작용을 모방하고 검사하는 데 사용내부로 들어오는 상호작용을 모방하는 데 사용
설명SUT가 상태를 변경하기 위한 의존성을 호출하는 것에 해당SUT가 입력 데이터를 얻기 위한 의존성을 호출하는 것에 해당
예시이메일 발송데이터 검색
SUT(system under test)

테스트 대상 시스템
+테스트를 하려는 대상

참고 자료

소프트웨어 장인 정신 이야기 - 3장 고급 테스트 주도 개발, 로버트 C. 마틴
+단위 테스트 - 5장 목과 테스트 취약성, 블라디미르 코리코프
+테스트 주도 개발 시작하기 - 7장 대역, 최범균
+테스트 더블, Martin Fowler
+테스트 관련 용어 정리, Johngrib
+Test Double, Gerard Meszaros

+ + \ No newline at end of file diff --git a/page/32.html b/page/32.html index 39c148fac..2c914707d 100644 --- a/page/32.html +++ b/page/32.html @@ -13,24 +13,30 @@ - - + +
-

· 약 10분

체스 미션에서는 데이터베이스에서 값을 가져오기 위해 DAO를 사용했다.
-이 때 JDBC를 사용할 때 데이터베이스의 커넥션을 얻고, try-with-resource를 사용하는 부분이 반복되었다.
-템플릿 콜백 패턴을 이용하여 나만의 JdbcTemplate을 만들어보았다.

기존 코드

public class User {
private final int id;
private final String name;

public User(final int id, final String name) {
this.id = id;
this.name = name;
}

public int getId() {
return id;
}

public String getName() {
return name;
}
}

SELECT, DELETE 중복 제거

변하지 않는 부분: try-with-resource, preparedStatement를 사용하는 부분, executeUpdate로 실행 등등
-변하는 부분: SQL Query, 매개변수

다음과 같이 쿼리를 실행하는 부분을 분리하고 가변인수를 사용한다면 SELECT와 DELETE의 중복을 제거할 수 있다.

public void insert(final String name) {
final String query = "INSERT INTO User (name) VALUES (?)";
executeUpdate(query, name);
}

public void delete(final int userId) {
final String query = "DELETE FROM user WHERE user_id = ?";
executeUpdate(query, userId);
}

private void executeUpdate(final String query, final Object... parameters) {
final Connection connection = connectionPool.getConnection();
try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
for (int i = 1; i <= parameters.length; i++) {
preparedStatement.setObject(i, parameters[i - 1]);
}
preparedStatement.executeUpdate();
} catch (final SQLException e) {
throw new IllegalArgumentException(e.getMessage());
}
}

조회 분리하기 - 1. 콜백을 위한 인터페이스 정의

조회는 INSERT, DELETE와 달리 값을 반환받아야 하기 때문에 다른 방법을 사용해야 한다.
-이 때 콜백이라는 것을 사용하여 중복을 제거할 수 있다.

콜백(Callback)

프로그래밍에서 콜백은 다른 코드의 인수로 넘겨주는 실행 가능한 코드를 뜻한다.
-자바에서는 람다나 익명 클래스를 넘겨서 사용할 수 있다.

데이터베이스에서 값을 조회하고, 해당 값을 객체로 매핑하여 값을 반환해야 한다.
-executeQuery로 조회한 값은 ResultSet 안에 들어가있다.
-이를 원하는 타입의 값으로 변환해야하니 일단 콜백을 위한 인터페이스를 만들어야 한다.

@FunctionalInterface
public interface RowMapper {
User mapRow(final ResultSet resultSet) throws SQLException;
}

조회 분리하기 - 2. 단건 조회

위에서 정의한 RowMapper를 메서드에서 어떻게 사용해야 할까?
-아래와 같이 SQL 쿼리, RowMapper, 파라미터를 분리한 메서드에 넘겨주고 쿼리 실행 후 매핑한 값을 반환하도록 한다.

public User findById(final int userId) {
final String query = "SELECT * FROM user WHERE id = ?";
return queryForSingleResult(query, resultSet -> {
final int id = resultSet.getInt("id");
final String name = resultSet.getString("name");
return new User(id, name);
}, userId);
}

private User queryForSingleResult(
final String query,
final RowMapper rowMapper,
final Object... parameters
) {
final Connection connection = connectionPool.getConnection();
try (final PreparedStatement preparedStatement = connection.prepareStatement(query);
final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {
if (resultSet.next()) {
return rowMapper.mapRow(resultSet);
}
return null;
} catch (SQLException e) {
throw new IllegalArgumentException(e.getMessage());
}
}

private ResultSet executeQuery(
final PreparedStatement preparedStatement,
final Object[] parameters) throws SQLException {
for (int i = 1; i <= parameters.length; i++) {
preparedStatement.setObject(i, parameters[i - 1]);
}
return preparedStatement.executeQuery();
}

조회 분리하기 - 3. 다건 조회

단건 조회와 유사하다.

public List<User> findAll() {
final String query = "SELECT * FROM user";
return query(query, resultSet -> {
final int id = resultSet.getInt("id");
final String name = resultSet.getString("name");
return new User(id, name);
});
}

private List<User> query(final String query, final RowMapper rowMapper, final Object... parameters) {
final Connection connection = connectionPool.getConnection();
try (final PreparedStatement preparedStatement = connection.prepareStatement(query);
final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {
final List<User> result = new ArrayList<>();
while (resultSet.next()) {
result.add(rowMapper.mapRow(resultSet));
}
return result;
} catch (SQLException e) {
throw new IllegalArgumentException(e.getMessage());
}
}

private ResultSet executeQuery(
final PreparedStatement preparedStatement,
final Object[] parameters) throws SQLException {
for (int i = 1; i <= parameters.length; i++) {
preparedStatement.setObject(i, parameters[i - 1]);
}
return preparedStatement.executeQuery();
}

제네릭 사용하기

위의 코드는 User를 조회할 때만 사용할 수 있다.
-아래와 같이 제네릭을 적용하여 다른 Dao에서도 사용 가능하도록 변경할 수 있다.

@FunctionalInterface
public interface RowMapper<T> {
T mapRow(final ResultSet resultSet) throws SQLException;
}

private <T> List<T> query(final String query, final RowMapper<T> rowMapper, final Object... parameters) {...}
private <T> T queryForSingleResult(final String query, final RowMapper<T> rowMapper, final Object... parameters) {...}

메서드 분리한 부분 클래스로 분리하기 + Optional 사용하기

메서드로 분리한 부분을 JdbcTemplate이라는 클래스를 만들어 옮긴다.
-또한 null을 반환하기 보단 Optional로 감싸서 반환하도록 변경한다.
-최종적으로 아래와 같은 코드가 완성된다.

public class UserDao {
private final RowMapper<User> rowMapper = resultSet -> {
final int id = resultSet.getInt("id");
final String name = resultSet.getString("name");
return new User(id, name);
};
private final JdbcTemplate jdbcTemplate;

public UserDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void insert(final String name) {
final String query = "INSERT INTO User (name) VALUES (?)";
jdbcTemplate.executeUpdate(query, name);
}

public void delete(final int userId) {
final String query = "DELETE FROM user WHERE user_id = ?";
jdbcTemplate.executeUpdate(query, userId);
}

public Optional<User> findById(final int userId) {
final String query = "SELECT * FROM user WHERE id = ?";
return jdbcTemplate.queryForSingleResult(query, rowMapper, userId);
}

public List<User> findAll() {
final String query = "SELECT * FROM user";
return jdbcTemplate.query(query, rowMapper);
}
}
- - +

· 약 6분

클래스 파일

자바 소스코드가 실행이 되려면 자바 컴파일러(javac)를 통해 소스코드를 클래스파일로 변환해야 한다.
+컴파일된 클래스파일은 어떤 구조로 되어있을까?

클래스 파일의 데이터 형식

8비트 바이트의 스트림으로 구성된다.
+16비트 및 32비트의 데이터는 각각 2개, 4개의 연속된 8비트를 읽어서 구성된다.
+멀티바이트의 경우 항상 big endian 순서로 저장된다.

u1 → unsigned 1byte
+u2 → unsigned 2byte
+u4 → unsigned 4byte

클래스 파일 구조

ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}

매직넘버

모든 클래스 파일은 0xCAFEBABE라는 매직넘버로 시작한다.
+보통 매직넘버는 파일 종류를 식별하는 용도로 사용된다.

클래스 파일 포맷 버전

클래스 파일 버전 값은 클래스로더의 호환성 보장을 위해 꼭 필요한 값이다.

  • Java 17 버전으로 빌드한다면 class version 61 ex) 00 00 00 3D

호환되지 않는 버전의 클래스 파일을 로딩하려고 하는 경우 런타임에 UnsupportedClassVersionError 예외가 발생한다.

class file format major versions

Java SEReleasedMajorSupported majors
8March 20145245 .. 52
9September 20175345 .. 53
10March 20185445 .. 54
11September 20185545 .. 55
12March 20195645 .. 56
13September 20195745 .. 57
14March 20205845 .. 58
15September 20205945 .. 59
16March 20216045 .. 60
17September 20216145 .. 61

상수 풀

2바이트의 상수의 개수값이 먼저오고 그 뒤로 코드에 등장하는 상수값이 모여있다.
+클래스명, 상수명, 상수 값, 필드명, 메서드명과 같은 값들이 존재한다.
+JVM은 코드 실행 시 런타임에 배치된 메모리가 아니라, 해당 상수 풀 테이블을 찾아보고 필요한 값을 참조한다.

액세스 플래그

클래스, 인터페이스와 같은 파일의 속성을 표시한다.
+예를 들어 public interface로 정의된 인터페이스의 플래그는 0x0601이다.

  • 계산은 다음과 같이 이루어진다. ACC_PUBLIC xor ACC_INTERFACE xor ACC_ABSTRACT

공식문서에 들어가면 각 플래그에 대한 설명 + 플래그 설정시 동시에 설정되면 안되는 플래그와 같은 설명이 자세하게 나와있다.

Class access and property modifiers

Flag NameValueInterpretation
ACC_PUBLIC0x0001Declared public; may be accessed from outside its package.
ACC_FINAL0x0010Declared final; no subclasses allowed.
ACC_SUPER0x0020Treat superclass methods specially when invoked by the invokespecial instruction.
ACC_INTERFACE0x0200Is an interface, not a class.
ACC_ABSTRACT0x0400Declared abstract; must not be instantiated.
ACC_SYNTHETIC0x1000Declared synthetic; not present in the source code.
ACC_ANNOTATION0x2000Declared as an annotation type.
ACC_ENUM0x4000Declared as an enum type.
ACC_MODULE0x8000Is a module, not a class or interface.

this_class

클래스명과 같은 이름을 표현하는 값으로, 상수 풀에서 클래스명과 일치하는 항목의 인덱스를 참조한다.
+해당 인덱스의 항목은 CONSTANT_Class_infoclass 형식의 값이어야 한다.

super_class

상수 풀에서 슈퍼 클래스의 이름과 일치하는 항목의 인덱스를 참조한다.
+아무것도 상속하지 않는 클래스의 경우 java.lang.Object의 인덱스 값이 들어있다.

interface, field, method

각각의 개수와, 정보에 대한 값이 들어있다.
+interface, field, method를 표시하는 방법이 각각 다르고, 접근자에 대한 플래그도 각각 다르다.

attributes

해당 클래스 파일에서 사용하는 추가 정보의 모음이다. 예) 소스파일명
+정해진 클래스 파일의 구조를 확장하는 역할을 한다.

클래스 파일 확인하면서 사용한 툴

IntelliJ plugin - BinEd
+IntelliJ plugin - jclasslib Bytecode Viewer

참고 자료

2장 JVM 이야기, 자바 최적화
+Class file in Java, File Format
+java se11 Class 파일 형식, Oracle
+java se17 Class 파일 형식, Oracle

+ + \ No newline at end of file diff --git a/page/33.html b/page/33.html index 464a38711..59a9c4914 100644 --- a/page/33.html +++ b/page/33.html @@ -13,44 +13,24 @@ - - + +
-

· 약 8분

레벨 1이 끝났다.
-우테코를 시작하기 전 내가 정해두었던 목표 이상으로 달성했기 때문에 매우 만족스럽다.
-혼자 독학을 할 땐 이 방향으로 공부하는 게 맞는지 계속 반추하다 결국 무기력함에 빠져들었다.
-하지만 이제는 같이 공부할 사람도 있고, 이야기할 사람도 있기 때문에 즐기는 일만 남은 것 같다.

Keep

나만의 루틴 만들기

스스로가 외부의 영향을 많이 받는다고 생각한다.
-최대한 꾸준히 할 수 있는 시간을 만드는 것이 중요하다고 생각한다.

매일 8시에 도착하여 아침에 해야 할 일을 정리하거나, 우선순위에 따라 처리하고
-소화능력이 부족하기 때문에 점심은 도시락(그래봤자 계란2개)을 준비하고
-항상 똑같은 컨디션을 유지하기 위해 항상 6시에 집에 간다.
-이제 바빠질 테니 일찍 집에 가는 일은 어쩔 수 없이 줄어들겠지만😢

선택도 비용이다. 앞으로 의사결정이 필요 없는 부분을 최대한 많이 만들어야겠다.

크루들과 친하게 지내기

10명 정도의 크루의 닉네임을 외우고 친하게 지낸다면 성공적이라고 생각했었다.
-하다 보니 더 많은 크루들의 닉네임을 외운 것 같다.
-앞으로도 크루들과 친하게 지내고 아무 때나 말을 걸 수 있는 크루가 늘어나길 :)

글쓰기

글을 잘 쓰는 편은 아니지만 꾸준히 작성하려고 노력했다.
-매 미션마다 회고를 작성하니 생각도 정리되고 개선점도 찾을 수 있어서 좋았다.

우아한테크코스에는 레벨마다 글쓰기를 진행하는데, 운이 좋게 글쓰기 상을 받았다.
-사실 겉으로 드러내지 않았지만 꼭 받아보고 싶었다.
-글쓰기 조원, 투표해 준 크루들에게 너무 감사하다.

코드 리뷰 스터디

누누, 주노, 다즐, 말랑, 박스터, 오잉, 깃짱와 코드 리뷰 스터디를 진행했다.
-과연 도움이 될까 생각했지만 결과적으로는 코드 리뷰를 하면서 성장을 많이 한 것 같다.
-투자한 시간 대비 가성비가 좋은 활동이었다.
-누누가 스터디장인데 과연 꾸준히 이어나가려나?

레벨 인터뷰

인터뷰할 때 많이 떨지 않아서 좋았다.
-남들 앞에서 이야기를 하거나, 면접을 보면 항상 엄청 떨어서 걱정했는데
-기술적인 질문을 받았을 때 떨지 않고 잘 대답할 수 있었다.
-우아한테크코스 생활을 하면서 다른 크루가 질문했을 때, 최대한 이해하기 쉽게 설명하려고 했던 경험이 도움이 된 것 같다.
-이후 레벨 인터뷰를 진행할 때 다음과 같은 부분을 고려하면 더 좋을 것 같다.

  • 대답하면서 질문을 계속 생각하며 잊어버리지 말기
  • 두괄식 표현
  • 설명하다가 잘못 설명한 것 같으면 다 끊고 다시 이야기해도 될지 물어보기
  • 설명할 수 있을만큼 시간 충분히 가지기
  • 인터뷰어의 질문 의도를 명확히 이해하지 못했다면 의도 다시 물어보기
  • 끝맺는 부분 연습하기(자신감 있게)
  • 기술적인 집착가지기
  • 기술적인 부분을 꼼꼼히 준비했으면 협업 관련 질문도 준비하기

Problem

페어프로그래밍

우아한테크코스를 진행하면서 가장 어려운 활동 중 하나라고 생각한다.
-페어는 매번 바뀌고, 미션의 복잡도도 증가하기 때문인 것 같다.
-소통 능력, 시간관리가 부족했고, 만족스럽지 않았다.
-하지만 페어를 진행하고, 회고를 하다 보니 나만의 노하우가 쌓이는 느낌이다.
-레벨 2에서는 부족했던 부분을 개선하여 함께하고 싶은 페어가 되고 싶다.

집중하는 시간⏱️ 부족

레벨 1을 진행하면서 집중하는 시간이 많이 부족했다.
-이른 아침과 오후에 개인적으로 집중할 수 있는 공간을 예약해서 온전히 나만의 시간을 가져야겠다.

Try

허브🌿와의 티타임?

소프트 스킬을 늘릴 방법을 생각하다가 대화를 나누지 못한 다른 크루들과 깜짝 커피챗을 하면 어떨까 생각했다.
-예를 들어 잡담방에 저와 커피챗 하실 분 :) 하면서 올릴 수 있을 것 같다.
-참여하는 사람이 있을지, 안 좋게 보는 게 아닐지 걱정되지만 그래도 재밌을 것 같다.
-저랑 허브티 한잔 하실래요?

기술적인 부분

우아한테크코스 생활을 하면서 소프트 스킬에 조금 더 무게를 두다 보니 이론적인 부분이 부족할 수 있다고 생각했다.
-시간의 여유가 될 때 책을 조금씩 읽어야겠다.
-블로그에 기술적인 부분을 많이 정리하지 않았는데, 조금 더 깊게 공부하고 정리하는 시간도 가져야겠다.

레벨 1을 마무리하며

시간이 빠르게 흘러갔다.
-타인에게 좋은 영향을 주기위해, 방학동안 나를 챙기는 시간을 가져야겠다.
-또한 함께 일하고 싶은 사람을 목표로 앞으로도 꾸준히 의식적 노력을 해야겠다.

- - +

· 약 10분

체스 미션에서는 데이터베이스에서 값을 가져오기 위해 DAO를 사용했다.
+이 때 JDBC를 사용할 때 데이터베이스의 커넥션을 얻고, try-with-resource를 사용하는 부분이 반복되었다.
+템플릿 콜백 패턴을 이용하여 나만의 JdbcTemplate을 만들어보았다.

기존 코드

public class User {
private final int id;
private final String name;

public User(final int id, final String name) {
this.id = id;
this.name = name;
}

public int getId() {
return id;
}

public String getName() {
return name;
}
}

SELECT, DELETE 중복 제거

변하지 않는 부분: try-with-resource, preparedStatement를 사용하는 부분, executeUpdate로 실행 등등
+변하는 부분: SQL Query, 매개변수

다음과 같이 쿼리를 실행하는 부분을 분리하고 가변인수를 사용한다면 SELECT와 DELETE의 중복을 제거할 수 있다.

public void insert(final String name) {
final String query = "INSERT INTO User (name) VALUES (?)";
executeUpdate(query, name);
}

public void delete(final int userId) {
final String query = "DELETE FROM user WHERE user_id = ?";
executeUpdate(query, userId);
}

private void executeUpdate(final String query, final Object... parameters) {
final Connection connection = connectionPool.getConnection();
try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
for (int i = 1; i <= parameters.length; i++) {
preparedStatement.setObject(i, parameters[i - 1]);
}
preparedStatement.executeUpdate();
} catch (final SQLException e) {
throw new IllegalArgumentException(e.getMessage());
}
}

조회 분리하기 - 1. 콜백을 위한 인터페이스 정의

조회는 INSERT, DELETE와 달리 값을 반환받아야 하기 때문에 다른 방법을 사용해야 한다.
+이 때 콜백이라는 것을 사용하여 중복을 제거할 수 있다.

콜백(Callback)

프로그래밍에서 콜백은 다른 코드의 인수로 넘겨주는 실행 가능한 코드를 뜻한다.
+자바에서는 람다나 익명 클래스를 넘겨서 사용할 수 있다.

데이터베이스에서 값을 조회하고, 해당 값을 객체로 매핑하여 값을 반환해야 한다.
+executeQuery로 조회한 값은 ResultSet 안에 들어가있다.
+이를 원하는 타입의 값으로 변환해야하니 일단 콜백을 위한 인터페이스를 만들어야 한다.

@FunctionalInterface
public interface RowMapper {
User mapRow(final ResultSet resultSet) throws SQLException;
}

조회 분리하기 - 2. 단건 조회

위에서 정의한 RowMapper를 메서드에서 어떻게 사용해야 할까?
+아래와 같이 SQL 쿼리, RowMapper, 파라미터를 분리한 메서드에 넘겨주고 쿼리 실행 후 매핑한 값을 반환하도록 한다.

public User findById(final int userId) {
final String query = "SELECT * FROM user WHERE id = ?";
return queryForSingleResult(query, resultSet -> {
final int id = resultSet.getInt("id");
final String name = resultSet.getString("name");
return new User(id, name);
}, userId);
}

private User queryForSingleResult(
final String query,
final RowMapper rowMapper,
final Object... parameters
) {
final Connection connection = connectionPool.getConnection();
try (final PreparedStatement preparedStatement = connection.prepareStatement(query);
final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {
if (resultSet.next()) {
return rowMapper.mapRow(resultSet);
}
return null;
} catch (SQLException e) {
throw new IllegalArgumentException(e.getMessage());
}
}

private ResultSet executeQuery(
final PreparedStatement preparedStatement,
final Object[] parameters) throws SQLException {
for (int i = 1; i <= parameters.length; i++) {
preparedStatement.setObject(i, parameters[i - 1]);
}
return preparedStatement.executeQuery();
}

조회 분리하기 - 3. 다건 조회

단건 조회와 유사하다.

public List<User> findAll() {
final String query = "SELECT * FROM user";
return query(query, resultSet -> {
final int id = resultSet.getInt("id");
final String name = resultSet.getString("name");
return new User(id, name);
});
}

private List<User> query(final String query, final RowMapper rowMapper, final Object... parameters) {
final Connection connection = connectionPool.getConnection();
try (final PreparedStatement preparedStatement = connection.prepareStatement(query);
final ResultSet resultSet = executeQuery(preparedStatement, parameters)) {
final List<User> result = new ArrayList<>();
while (resultSet.next()) {
result.add(rowMapper.mapRow(resultSet));
}
return result;
} catch (SQLException e) {
throw new IllegalArgumentException(e.getMessage());
}
}

private ResultSet executeQuery(
final PreparedStatement preparedStatement,
final Object[] parameters) throws SQLException {
for (int i = 1; i <= parameters.length; i++) {
preparedStatement.setObject(i, parameters[i - 1]);
}
return preparedStatement.executeQuery();
}

제네릭 사용하기

위의 코드는 User를 조회할 때만 사용할 수 있다.
+아래와 같이 제네릭을 적용하여 다른 Dao에서도 사용 가능하도록 변경할 수 있다.

@FunctionalInterface
public interface RowMapper<T> {
T mapRow(final ResultSet resultSet) throws SQLException;
}

private <T> List<T> query(final String query, final RowMapper<T> rowMapper, final Object... parameters) {...}
private <T> T queryForSingleResult(final String query, final RowMapper<T> rowMapper, final Object... parameters) {...}

메서드 분리한 부분 클래스로 분리하기 + Optional 사용하기

메서드로 분리한 부분을 JdbcTemplate이라는 클래스를 만들어 옮긴다.
+또한 null을 반환하기 보단 Optional로 감싸서 반환하도록 변경한다.
+최종적으로 아래와 같은 코드가 완성된다.

public class UserDao {
private final RowMapper<User> rowMapper = resultSet -> {
final int id = resultSet.getInt("id");
final String name = resultSet.getString("name");
return new User(id, name);
};
private final JdbcTemplate jdbcTemplate;

public UserDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void insert(final String name) {
final String query = "INSERT INTO User (name) VALUES (?)";
jdbcTemplate.executeUpdate(query, name);
}

public void delete(final int userId) {
final String query = "DELETE FROM user WHERE user_id = ?";
jdbcTemplate.executeUpdate(query, userId);
}

public Optional<User> findById(final int userId) {
final String query = "SELECT * FROM user WHERE id = ?";
return jdbcTemplate.queryForSingleResult(query, rowMapper, userId);
}

public List<User> findAll() {
final String query = "SELECT * FROM user";
return jdbcTemplate.query(query, rowMapper);
}
}
+ + \ No newline at end of file diff --git a/page/34.html b/page/34.html index f345da0b2..392371fcb 100644 --- a/page/34.html +++ b/page/34.html @@ -13,47 +13,44 @@ - - + +
-

· 약 8분

체스

체스 미션에는 가비와 페어가 매칭되었다!
-체스는 이전 미션들보다 훨씬 복잡한 도메인이었다.
-하지만 가비와 나는 체스 도메인이 익숙해서 더 편한 마음으로 시작할 수 있었다.
-미션을 진행하면서 어려웠던 부분은 기물의 이동, 이동시 경로에 기물이 존재하는지 확인하는 부분이었다.

가비가 집에가서도 기물의 이동 관련해 생각 정리한 글을 보내줘서 더욱 빨리 진행할 수 있었다.
-최종적으로 결정한 부분은 다음과 같다.

각 기물의 이동 가능여부
-Rank와 File은 각각 위치값을 가지고 있고, 값의 차이를 이용해서 각 기물의 이동 가능 여부를 계산했다.
-직선 → Rank와 File 차이 중 하나가 0이어야 한다.
-대각선 → Rank와 File 차이의 절대값이 같아야 한다. ex) abs(-2) == abs(2)
-나이트 → 차이의 절대값이 하나는 2 나머지 하나는 1이어야 한다.

도착 칸의 기물 여부
-아군 → 이동이 불가능하다.
-적군 → 이동이 가능하다. 적군을 잡는다.

중간에 기물 존재 여부
-이동 경로에 기물이 존재하면 안된다.

데이터베이스 사용
-체스 미션은 특별하게 데이터베이스와 연결하는 부분이 있었다.
-체스 게임의 상태를 다음의 두가지 방법으로 정할 수 있다고 생각했다.

  • 기물 전체를 저장하는 방법
  • 기보를 저장하고 게임을 불러와 기보대로 이동시키는 방법

기물이 이동할 때마다 값을 저장하고 싶었고, 기보를 저장하는 방법을 선택했다.
-기물 전체를 저장하지 않은 이유는 다음과 같다.

  • 턴과 같은 부가적인 요소를 저장해야 한다.
  • 이동을 할 때 기물이 잡히는 경우 update 쿼리(이동 기물)와 delete(잡힌 기물) 2개의 쿼리를 날려야 한다.
  • 현재 구조에서 도메인의 변경이 크게(초기 상태를 구성하는 부분) 일어나야 한다.

정리하자면 기물 전체 저장과 기보 저장은 다음과 차이가 있다.
-보드저장: 초기상태에서 32개의 Insert 쿼리(기물의 위치) + 기물 이동 시 움직임 변경(잡히는 경우 2개의 쿼리)
-기보저장: 초기상태 애플리케이션에서 구성 + 저장된 기보를 select 쿼리로 조회해서 사용(1회) + insert 쿼리(이동 당 1회)

추가로 기보저장이 구현도 더욱 간단하다. 👍

부가적인 부분

리뷰어인 찰리🍫가 동시에 여러 게임이 진행된다면 어떨지? 에 대한 코멘트를 남겨주셔서 다양한 시도를 해봤다.

  • 누누의 도움으로 ConnectionPool 구현
  • ThreadLocal 사용해서 쓰레드 별 세션 관리
  • 실제로 애플리케이션 내에서 체스 게임이 진행되는 Board를 ConcurrentHashMap으로 저장(사실 이 부분은 현재 구조에서 필요없지만 2명이 서로 게임하는 경우를 생각해서 넣어보았다.)

두 명이 서로 같은 방에 입장하여 게임을 진행한다면 출력하는 부분이 까다로워질 것 같다고 예상되어(Board에 옵저버 패턴을 사용해야되나?) 해볼 엄두가 나지 않았다.

부족했던 부분

꼼꼼하게 코드를 작성하지 못한 부분
-DB 관련 부분을 꼼꼼하게 코딩을 하지 못했다.
-도메인 로직에만 집중하다보니 정적 중요한 DB의 코드의 예외처리, 빈 값을 반환 하는 부분을 꼼꼼하게 처리하지 못했다.
-하지만 찰리의 꼼꼼한 리뷰로 DB부분과 나만의 JdbcTemplate을 깔끔하게 구현할 수 있었다.

시간에 대한 부담감
-초반에는 여유롭지만 제출 마감에 가까워질 수록 사람이 급해지는 것 같다.
-다음 페어프로그래밍할 땐 속도를 조절하고, 마음에 여유를 가져야겠다.

새로 학습한 부분

DAO 중복 제거

프롤로그에 을 작성했다.
-DAO를 작성하는데 try-catch-resources와 여러 코드가 중복되서 제거하고싶었다.
-템플릿 콜백 패턴으로 깔끔하게 중복을 제거할 수 있었다.👍

페어에게 배울 부분

페어 생각하기
-가비는 누구보다 페어를 생각하고, 배려해주는 페어였다.
-중간 중간 당 떨어질까봐 걱정도 해주고, 나의 컨디션도 확인해줬다!

미션 몰입하기
-최근에 미션에 잘 몰입하지 못했다.
-가비는 페어를 진행할 때 미션에 대한 몰입도가 매우 좋았다.
-집에가서도 체스 이동에 대한 로직을 어떻게 구현할 지 생각한 뒤 꼼꼼해서 정리해서 나에게 보내주었다.
-덕분에 나도 가비의 생각을 알 수 있어서 미션을 진행하는데 가속도가 붙은 것 같다.
-또한 미션을 잘 마무리하고 싶은 마음이 전달되서 그런지 나도 덩달아 열심히 미션을 할 수 있었다.😄

솔직함
-먼저 회고하자고 말 걸어줘서 정말 고마웠다고 표현해주는 부분
-모르는게 있으면 솔직하게 말해주는 부분
-나의 의견을 정리하지 못한 상태로 전달할 때 이해가 안되었다고 정확히 전달해주는 부분
-솔직함은 페어할 때 중요한 부분인 것 같다.

마지막으로 찰리🍫 체스 미션때 꼼꼼하게 리뷰 남겨주셔서 감사합니다!

- - +

· 약 8분

레벨 1이 끝났다.
+우테코를 시작하기 전 내가 정해두었던 목표 이상으로 달성했기 때문에 매우 만족스럽다.
+혼자 독학을 할 땐 이 방향으로 공부하는 게 맞는지 계속 반추하다 결국 무기력함에 빠져들었다.
+하지만 이제는 같이 공부할 사람도 있고, 이야기할 사람도 있기 때문에 즐기는 일만 남은 것 같다.

Keep

나만의 루틴 만들기

스스로가 외부의 영향을 많이 받는다고 생각한다.
+최대한 꾸준히 할 수 있는 시간을 만드는 것이 중요하다고 생각한다.

매일 8시에 도착하여 아침에 해야 할 일을 정리하거나, 우선순위에 따라 처리하고
+소화능력이 부족하기 때문에 점심은 도시락(그래봤자 계란2개)을 준비하고
+항상 똑같은 컨디션을 유지하기 위해 항상 6시에 집에 간다.
+이제 바빠질 테니 일찍 집에 가는 일은 어쩔 수 없이 줄어들겠지만😢

선택도 비용이다. 앞으로 의사결정이 필요 없는 부분을 최대한 많이 만들어야겠다.

크루들과 친하게 지내기

10명 정도의 크루의 닉네임을 외우고 친하게 지낸다면 성공적이라고 생각했었다.
+하다 보니 더 많은 크루들의 닉네임을 외운 것 같다.
+앞으로도 크루들과 친하게 지내고 아무 때나 말을 걸 수 있는 크루가 늘어나길 :)

글쓰기

글을 잘 쓰는 편은 아니지만 꾸준히 작성하려고 노력했다.
+매 미션마다 회고를 작성하니 생각도 정리되고 개선점도 찾을 수 있어서 좋았다.

우아한테크코스에는 레벨마다 글쓰기를 진행하는데, 운이 좋게 글쓰기 상을 받았다.
+사실 겉으로 드러내지 않았지만 꼭 받아보고 싶었다.
+글쓰기 조원, 투표해 준 크루들에게 너무 감사하다.

코드 리뷰 스터디

누누, 주노, 다즐, 말랑, 박스터, 오잉, 깃짱와 코드 리뷰 스터디를 진행했다.
+과연 도움이 될까 생각했지만 결과적으로는 코드 리뷰를 하면서 성장을 많이 한 것 같다.
+투자한 시간 대비 가성비가 좋은 활동이었다.
+누누가 스터디장인데 과연 꾸준히 이어나가려나?

레벨 인터뷰

인터뷰할 때 많이 떨지 않아서 좋았다.
+남들 앞에서 이야기를 하거나, 면접을 보면 항상 엄청 떨어서 걱정했는데
+기술적인 질문을 받았을 때 떨지 않고 잘 대답할 수 있었다.
+우아한테크코스 생활을 하면서 다른 크루가 질문했을 때, 최대한 이해하기 쉽게 설명하려고 했던 경험이 도움이 된 것 같다.
+이후 레벨 인터뷰를 진행할 때 다음과 같은 부분을 고려하면 더 좋을 것 같다.

  • 대답하면서 질문을 계속 생각하며 잊어버리지 말기
  • 두괄식 표현
  • 설명하다가 잘못 설명한 것 같으면 다 끊고 다시 이야기해도 될지 물어보기
  • 설명할 수 있을만큼 시간 충분히 가지기
  • 인터뷰어의 질문 의도를 명확히 이해하지 못했다면 의도 다시 물어보기
  • 끝맺는 부분 연습하기(자신감 있게)
  • 기술적인 집착가지기
  • 기술적인 부분을 꼼꼼히 준비했으면 협업 관련 질문도 준비하기

Problem

페어프로그래밍

우아한테크코스를 진행하면서 가장 어려운 활동 중 하나라고 생각한다.
+페어는 매번 바뀌고, 미션의 복잡도도 증가하기 때문인 것 같다.
+소통 능력, 시간관리가 부족했고, 만족스럽지 않았다.
+하지만 페어를 진행하고, 회고를 하다 보니 나만의 노하우가 쌓이는 느낌이다.
+레벨 2에서는 부족했던 부분을 개선하여 함께하고 싶은 페어가 되고 싶다.

집중하는 시간⏱️ 부족

레벨 1을 진행하면서 집중하는 시간이 많이 부족했다.
+이른 아침과 오후에 개인적으로 집중할 수 있는 공간을 예약해서 온전히 나만의 시간을 가져야겠다.

Try

허브🌿와의 티타임?

소프트 스킬을 늘릴 방법을 생각하다가 대화를 나누지 못한 다른 크루들과 깜짝 커피챗을 하면 어떨까 생각했다.
+예를 들어 잡담방에 저와 커피챗 하실 분 :) 하면서 올릴 수 있을 것 같다.
+참여하는 사람이 있을지, 안 좋게 보는 게 아닐지 걱정되지만 그래도 재밌을 것 같다.
+저랑 허브티 한잔 하실래요?

기술적인 부분

우아한테크코스 생활을 하면서 소프트 스킬에 조금 더 무게를 두다 보니 이론적인 부분이 부족할 수 있다고 생각했다.
+시간의 여유가 될 때 책을 조금씩 읽어야겠다.
+블로그에 기술적인 부분을 많이 정리하지 않았는데, 조금 더 깊게 공부하고 정리하는 시간도 가져야겠다.

레벨 1을 마무리하며

시간이 빠르게 흘러갔다.
+타인에게 좋은 영향을 주기위해, 방학동안 나를 챙기는 시간을 가져야겠다.
+또한 함께 일하고 싶은 사람을 목표로 앞으로도 꾸준히 의식적 노력을 해야겠다.

+ + \ No newline at end of file diff --git a/page/35.html b/page/35.html index 9a0bcacad..33c3b7bc3 100644 --- a/page/35.html +++ b/page/35.html @@ -13,15 +13,47 @@ - - + +
-

· 약 9분

GRASP(General Responsibility Assignment Software Pattern)

크레이그 라만의 Applying UML and Patterns이라는 책에서 나온 책임 할당을 위한 패턴

각 패턴마다 Solution과 Problem로 구성되어 있다.

정보 전문가 패턴(Information Expert)

Q: 객체에 책임을 할당하는 기본 원칙은 무엇인가?

A: 책임을 수행하는 데 필요한 정보를 가진 클래스(정보 전문가)에게 책임을 할당한다.

정보와 행동을 가까운 곳에 위치시키기 때문에 캡슐화를 유지할 수 있다.

필요한 정보를 가진 객체들로 책임이 분산된다.

창조자 패턴(Creator)

Q: 누가 객체 A를 생성하는가?

A: 다음의 조건을 최대한 많이 만족하는 객체에게 객체 생성 책임을 할당해야 한다.

  • B가 A 객체를 포함 또는 참조한다.
  • B가 A 객체를 기록한다.
  • B가 A 객체를 긴밀하게 사용한다.
  • B가 A 객체의 초기값을 가지고 있다.

생성 예정인 객체와 연관되어 있는 객체가 생성 책임을 가지고 있게 된다면, 이미 해당 객체와 결합되어있다고 생각할 수 있다. 따라서 전체적인 결합도를 낮게 유지할 수 있다.

낮은 결합도 패턴(Low Coupling)

Q: 의존성을 낮추고 변화의 영향을 줄이며 재사용성을 증가시키는 방법은?

A: 전체적인 결합이 낮게 유지되도록 책임을 할당해야 한다.

결합도(Coupling) -객체 사이의 의존성이 과한 경우 결합도가 높다고 말한다.

  • 오브젝트 p.17

결합도를 낮춘다면 다음과 같은 이점이 있다.

  • 다른 구성 요소의 변화에 영향을 받지 않는다.
  • 재사용이 편리해진다.
  • 해당 클래스에 대한 이해가 쉬워진다. (의존하는 클래스가 적기 때문에)

높은 응집도 패턴(High Cohesion)

Q. 객체를 관리하기 쉽게 하려면 어떻게 해야 할까?

A. 높은 응집도를 유지할 수 있게 책임을 할당해야 한다.

응집도(Cohesion) -연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에게 위임하는 객체를 가리켜 응집도가 높다고 말한다.

  • 오브젝트 p.26

변경의 이유에 따라 클래스를 분리한다면 응집도를 높일 수 있고, 응집도가 높아진다면 다음과 같은 이점이 있다.

  • 해당 클래스에 대한 이해가 쉬워진다. (할당된 책임만을 수행하고 있기 때문에)
  • 유지보수가 쉬워진다.
  • 낮은 결합도 또한 지원한다.
  • 응집도가 높은 클래스는 특정한 목적에 사용할 수 있기 때문에 재사용하기 좋다.

컨트롤러 패턴(Controller)

Q. 사용자의 요청을 처리하는 것은 누가 담당해야 하는가?

A. 사용자의 요청을 처리하는 Controller 객체를 만들어서 사용해야 한다.

어떤 서브시스템이 존재한다고 가정할 때

  • 직접적으로 객체에 접근하여 프로그램을 사용한다면 결합도가 상승한다.
  • 서브 시스템에 들어오는 요청을 처리해주는 컨트롤러가 있다면 사용하는 입장에서는 해당 컨트롤러만 알면 된다.
  • 만약 서브 시스템의 변경이 생겼을 때 외부에 미치는 영향도 줄어든다.

다형성 패턴(Polymorphism)

Q. 객체의 타입에 따라 행동이 바뀐다면 책임을 어떻게 할당해야 할까?

A. OOP가 지원하는 다형성을 적극적으로 활용한다. (인터페이스를 두고 행동에 대한 부분을 구현)

객체의 종류에 따라 분기하는 조건문이 아닌 다형성을 사용하는 것이 좋은 방법이다.

새로운 타입이 추가되었을 때 조건문을 사용한다면 기존의 조건문을 수정해야 하지만 다형성을 활용하면 쉽게 확장할 수 있다.

변경 보호 패턴(Protected Variations)

Q. 어떻게 하면 변경이 다른 요소에 영향을 미치지 않도록 방지할 수 있을까?

A. 변화가 예상되는 지점을 식별하고, 주위에 안정된 인터페이스를 형성하도록 책임을 할당해야 한다.

간접 참조 패턴(Indirection)

Q. 두 객체 사이의 직접적인 연결을 피하고 싶다면 어떻게 해야 할까?

A. 두 객체 사이에 또 다른 객체를 두어 직접적인 연결을 피할 수 있다.

중재자 패턴을 사용하여 두 객체 사이에 또 하나의 객체를 추가하여 복잡한 관계를 단순화할 수 있다.

중간에 인터페이스를 둔다면 변경 보호 패턴(Protected Variations)에 해당된다.

순수한 가공물 패턴(Pure Fabrication)

Q. 책임을 할당한 도메인 객체가 Low Coupling, High Cohesion, 재사용성 등의 목적을 위반한다면 어떻게 해야 할까?

A. 도메인 개념을 포함하지 않는 클래스를 하나 만들고 매우 응집된 책임을 할당할 수 있다.

행동을 추가할 때, 해당 책임을 수행할 도메인 개념이 존재하지 않는다면 도메인과 무관한 인공적인 객체를 만든다음 해당 객체에게 책임을 할당한다.

객체가 데이터베이스에 저장해야 할 값을 가지고 있다고, 정보 전문가 패턴을 적용하여 데이터베이스에 저장하라는 책임을 가지라고 하지 않는다.

예) 상점과 고객 클래스가 있고 서로 다른 통화를 사용하고 있다고 가정

  • 서로 다른 통화를 사용하고 있기 때문에 거래를 하려면 환전을 해야한다.
  • 두 클래스 다 환전에 대한 책임을 부여하기 애매하다면 환전을 책임하는 클래스를 추가하고 사용할 수 있다.

참고 자료

오브젝트 5장. 책임 할당하기, 조영호

Applying UML and Patterns Chapter 16, Chapter 21 GRASP, Craig Larman

GRASP, 한빛 네트워크

- - +

· 약 8분

체스

체스 미션에는 가비와 페어가 매칭되었다!
+체스는 이전 미션들보다 훨씬 복잡한 도메인이었다.
+하지만 가비와 나는 체스 도메인이 익숙해서 더 편한 마음으로 시작할 수 있었다.
+미션을 진행하면서 어려웠던 부분은 기물의 이동, 이동시 경로에 기물이 존재하는지 확인하는 부분이었다.

가비가 집에가서도 기물의 이동 관련해 생각 정리한 글을 보내줘서 더욱 빨리 진행할 수 있었다.
+최종적으로 결정한 부분은 다음과 같다.

각 기물의 이동 가능여부
+Rank와 File은 각각 위치값을 가지고 있고, 값의 차이를 이용해서 각 기물의 이동 가능 여부를 계산했다.
+직선 → Rank와 File 차이 중 하나가 0이어야 한다.
+대각선 → Rank와 File 차이의 절대값이 같아야 한다. ex) abs(-2) == abs(2)
+나이트 → 차이의 절대값이 하나는 2 나머지 하나는 1이어야 한다.

도착 칸의 기물 여부
+아군 → 이동이 불가능하다.
+적군 → 이동이 가능하다. 적군을 잡는다.

중간에 기물 존재 여부
+이동 경로에 기물이 존재하면 안된다.

데이터베이스 사용
+체스 미션은 특별하게 데이터베이스와 연결하는 부분이 있었다.
+체스 게임의 상태를 다음의 두가지 방법으로 정할 수 있다고 생각했다.

  • 기물 전체를 저장하는 방법
  • 기보를 저장하고 게임을 불러와 기보대로 이동시키는 방법

기물이 이동할 때마다 값을 저장하고 싶었고, 기보를 저장하는 방법을 선택했다.
+기물 전체를 저장하지 않은 이유는 다음과 같다.

  • 턴과 같은 부가적인 요소를 저장해야 한다.
  • 이동을 할 때 기물이 잡히는 경우 update 쿼리(이동 기물)와 delete(잡힌 기물) 2개의 쿼리를 날려야 한다.
  • 현재 구조에서 도메인의 변경이 크게(초기 상태를 구성하는 부분) 일어나야 한다.

정리하자면 기물 전체 저장과 기보 저장은 다음과 차이가 있다.
+보드저장: 초기상태에서 32개의 Insert 쿼리(기물의 위치) + 기물 이동 시 움직임 변경(잡히는 경우 2개의 쿼리)
+기보저장: 초기상태 애플리케이션에서 구성 + 저장된 기보를 select 쿼리로 조회해서 사용(1회) + insert 쿼리(이동 당 1회)

추가로 기보저장이 구현도 더욱 간단하다. 👍

부가적인 부분

리뷰어인 찰리🍫가 동시에 여러 게임이 진행된다면 어떨지? 에 대한 코멘트를 남겨주셔서 다양한 시도를 해봤다.

  • 누누의 도움으로 ConnectionPool 구현
  • ThreadLocal 사용해서 쓰레드 별 세션 관리
  • 실제로 애플리케이션 내에서 체스 게임이 진행되는 Board를 ConcurrentHashMap으로 저장(사실 이 부분은 현재 구조에서 필요없지만 2명이 서로 게임하는 경우를 생각해서 넣어보았다.)

두 명이 서로 같은 방에 입장하여 게임을 진행한다면 출력하는 부분이 까다로워질 것 같다고 예상되어(Board에 옵저버 패턴을 사용해야되나?) 해볼 엄두가 나지 않았다.

부족했던 부분

꼼꼼하게 코드를 작성하지 못한 부분
+DB 관련 부분을 꼼꼼하게 코딩을 하지 못했다.
+도메인 로직에만 집중하다보니 정적 중요한 DB의 코드의 예외처리, 빈 값을 반환 하는 부분을 꼼꼼하게 처리하지 못했다.
+하지만 찰리의 꼼꼼한 리뷰로 DB부분과 나만의 JdbcTemplate을 깔끔하게 구현할 수 있었다.

시간에 대한 부담감
+초반에는 여유롭지만 제출 마감에 가까워질 수록 사람이 급해지는 것 같다.
+다음 페어프로그래밍할 땐 속도를 조절하고, 마음에 여유를 가져야겠다.

새로 학습한 부분

DAO 중복 제거

프롤로그에 을 작성했다.
+DAO를 작성하는데 try-catch-resources와 여러 코드가 중복되서 제거하고싶었다.
+템플릿 콜백 패턴으로 깔끔하게 중복을 제거할 수 있었다.👍

페어에게 배울 부분

페어 생각하기
+가비는 누구보다 페어를 생각하고, 배려해주는 페어였다.
+중간 중간 당 떨어질까봐 걱정도 해주고, 나의 컨디션도 확인해줬다!

미션 몰입하기
+최근에 미션에 잘 몰입하지 못했다.
+가비는 페어를 진행할 때 미션에 대한 몰입도가 매우 좋았다.
+집에가서도 체스 이동에 대한 로직을 어떻게 구현할 지 생각한 뒤 꼼꼼해서 정리해서 나에게 보내주었다.
+덕분에 나도 가비의 생각을 알 수 있어서 미션을 진행하는데 가속도가 붙은 것 같다.
+또한 미션을 잘 마무리하고 싶은 마음이 전달되서 그런지 나도 덩달아 열심히 미션을 할 수 있었다.😄

솔직함
+먼저 회고하자고 말 걸어줘서 정말 고마웠다고 표현해주는 부분
+모르는게 있으면 솔직하게 말해주는 부분
+나의 의견을 정리하지 못한 상태로 전달할 때 이해가 안되었다고 정확히 전달해주는 부분
+솔직함은 페어할 때 중요한 부분인 것 같다.

마지막으로 찰리🍫 체스 미션때 꼼꼼하게 리뷰 남겨주셔서 감사합니다!

+ + \ No newline at end of file diff --git a/page/36.html b/page/36.html index fec9e97cd..8c39f9f57 100644 --- a/page/36.html +++ b/page/36.html @@ -13,42 +13,15 @@ - - + +
-

· 약 6분

블랙잭

블랙잭 미션에서는 후추와 페어(조미료 듀오?)가 매칭되었다.
-이번에는 실수하지 않고, 바로 미션을 진행하지 않고 친해지기 부터 시작했다.

블랙잭은 구현해야 될 내용이 많아 시간이 많이 부족할 것 같았지만
-후추와 함께 전략적(삼일절에 미션 이야기 나누기)으로 미션을 진행해 시간 내에 제출할 수 있었다.

미션을 끝나고 회고를 했을 때 후추가 고민거리를 하나 내줬다.
-"페어를 진행할 때 압박감을 느끼는 페어가 있다면 허브가 해줄 수 있는게 뭐가 있을까?"

곰곰히 생각해봤지만 쉽게 답을 내릴 수 없었다.
-중간 중간 회고를 하고, 나의 소프트스킬을 높히는게 답일까?
-부담감을 느끼지 않고 같이 일하고 싶은 사람이 될 수 있도록 계속 생각해봐야겠다.

이 부분에 대해 생각이 많아져서 전 리뷰어인 터틀🐢과도 대화를 나누었다.
-터틀은 제어할 수 없는 부분보다 제어할 수 있는 부분(궁극적인 목표인 좋은 코드를 작성하는 것)에 집중해보라고 하셨다.

좋은 코드, 좋은 페어에 대한 부분을 일단 지속적으로 생각해봐야겠다.

부족했던 부분

페어 신경쓰기
-이번 페어할 때 적극적으로 의견을 내보도록 했다. 그렇기에 너무 의견을 강하게 밀어붙인 느낌이 들어서 미안했다.
-후추가 압박을 느꼈을 수도 있을 것 같다는 생각이 든다.
-중간 중간 작은 회고를 진행해보는 것이 좋을까?

체력 관리
-요즘 잘 못먹는 것 같다.
-앞으로 살 날이 많은데 잘 챙겨먹고, 힘내야겠다.

중간 중간 돌아보기
-이번 미션과 관련된 내용은 아니지만 우테코를 잘 활용 하고 있는지 생각을 해봐야겠다.
-내가 우테코에 지원한 이유를 항상 잊지 않아야겠다.

새로 학습한 부분

상태 패턴
-객체의 내부 상태에 따라 스스로 행동을 변경하도록 하는 패턴으로 if/else/switch와 같은 조건문을 효과적으로 제거할 수 있다.
-블랙잭 미션을 진행하면서 상태 패턴에 대한 부분을 처음 적용해보았다.
-처음 적용하기 전에는 별로라고 생각했는데, 생각보다 괜찮은 것 같다.

일관성, 가독성, 추상화
-이번 리뷰어는 검프🍫 였다!
-검프의 리뷰는 간결함에 관련된 내용이 많았다.
-일관성이 있는 코드, 가독성이 좋은 코드, 추상화가 잘 되어있는 코드
-읽기 좋고, 간결한 방향으로 코드를 작성하는 방법을 배운 것 같다.
-코드를 바라보는 시점이 하나 늘어난 기분이다!(앞으로 적용하는 것은 나의 몫이지만)

페어에게 배울 부분

생각 정리
-중간 중간 현재 상황에 대해 그림을 그리거나, 글을 적으면서 정리한다.
-페어와 동일한 부분을 이해하고 있는지 확인한다.
-진행하는데 매우 도움이 되었던 것 같다.
-나도 다음 페어때부터 펜이랑 종이를 준비해야겠다.

가감없이 의견을 말해주는 부분
-진행 상황에 대한 부분, 진행 속도, 지금 자신이 이해하고 있는 부분을 말해줘서 편했다.
-회고때도 서로 솔직하게 의견을 주고 받아서 좋았다.

도메인 언어에 신경쓰는 부분
-클래스명, 변수명과 같은 언어를 세심하게 신경쓴다.
-요구사항 정리도 깔끔하게 잘하는 것 같다.

후추 최고 👍

- - +

· 약 9분

GRASP(General Responsibility Assignment Software Pattern)

크레이그 라만의 Applying UML and Patterns이라는 책에서 나온 책임 할당을 위한 패턴

각 패턴마다 Solution과 Problem로 구성되어 있다.

정보 전문가 패턴(Information Expert)

Q: 객체에 책임을 할당하는 기본 원칙은 무엇인가?

A: 책임을 수행하는 데 필요한 정보를 가진 클래스(정보 전문가)에게 책임을 할당한다.

정보와 행동을 가까운 곳에 위치시키기 때문에 캡슐화를 유지할 수 있다.

필요한 정보를 가진 객체들로 책임이 분산된다.

창조자 패턴(Creator)

Q: 누가 객체 A를 생성하는가?

A: 다음의 조건을 최대한 많이 만족하는 객체에게 객체 생성 책임을 할당해야 한다.

  • B가 A 객체를 포함 또는 참조한다.
  • B가 A 객체를 기록한다.
  • B가 A 객체를 긴밀하게 사용한다.
  • B가 A 객체의 초기값을 가지고 있다.

생성 예정인 객체와 연관되어 있는 객체가 생성 책임을 가지고 있게 된다면, 이미 해당 객체와 결합되어있다고 생각할 수 있다. 따라서 전체적인 결합도를 낮게 유지할 수 있다.

낮은 결합도 패턴(Low Coupling)

Q: 의존성을 낮추고 변화의 영향을 줄이며 재사용성을 증가시키는 방법은?

A: 전체적인 결합이 낮게 유지되도록 책임을 할당해야 한다.

결합도(Coupling) +객체 사이의 의존성이 과한 경우 결합도가 높다고 말한다.

  • 오브젝트 p.17

결합도를 낮춘다면 다음과 같은 이점이 있다.

  • 다른 구성 요소의 변화에 영향을 받지 않는다.
  • 재사용이 편리해진다.
  • 해당 클래스에 대한 이해가 쉬워진다. (의존하는 클래스가 적기 때문에)

높은 응집도 패턴(High Cohesion)

Q. 객체를 관리하기 쉽게 하려면 어떻게 해야 할까?

A. 높은 응집도를 유지할 수 있게 책임을 할당해야 한다.

응집도(Cohesion) +연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에게 위임하는 객체를 가리켜 응집도가 높다고 말한다.

  • 오브젝트 p.26

변경의 이유에 따라 클래스를 분리한다면 응집도를 높일 수 있고, 응집도가 높아진다면 다음과 같은 이점이 있다.

  • 해당 클래스에 대한 이해가 쉬워진다. (할당된 책임만을 수행하고 있기 때문에)
  • 유지보수가 쉬워진다.
  • 낮은 결합도 또한 지원한다.
  • 응집도가 높은 클래스는 특정한 목적에 사용할 수 있기 때문에 재사용하기 좋다.

컨트롤러 패턴(Controller)

Q. 사용자의 요청을 처리하는 것은 누가 담당해야 하는가?

A. 사용자의 요청을 처리하는 Controller 객체를 만들어서 사용해야 한다.

어떤 서브시스템이 존재한다고 가정할 때

  • 직접적으로 객체에 접근하여 프로그램을 사용한다면 결합도가 상승한다.
  • 서브 시스템에 들어오는 요청을 처리해주는 컨트롤러가 있다면 사용하는 입장에서는 해당 컨트롤러만 알면 된다.
  • 만약 서브 시스템의 변경이 생겼을 때 외부에 미치는 영향도 줄어든다.

다형성 패턴(Polymorphism)

Q. 객체의 타입에 따라 행동이 바뀐다면 책임을 어떻게 할당해야 할까?

A. OOP가 지원하는 다형성을 적극적으로 활용한다. (인터페이스를 두고 행동에 대한 부분을 구현)

객체의 종류에 따라 분기하는 조건문이 아닌 다형성을 사용하는 것이 좋은 방법이다.

새로운 타입이 추가되었을 때 조건문을 사용한다면 기존의 조건문을 수정해야 하지만 다형성을 활용하면 쉽게 확장할 수 있다.

변경 보호 패턴(Protected Variations)

Q. 어떻게 하면 변경이 다른 요소에 영향을 미치지 않도록 방지할 수 있을까?

A. 변화가 예상되는 지점을 식별하고, 주위에 안정된 인터페이스를 형성하도록 책임을 할당해야 한다.

간접 참조 패턴(Indirection)

Q. 두 객체 사이의 직접적인 연결을 피하고 싶다면 어떻게 해야 할까?

A. 두 객체 사이에 또 다른 객체를 두어 직접적인 연결을 피할 수 있다.

중재자 패턴을 사용하여 두 객체 사이에 또 하나의 객체를 추가하여 복잡한 관계를 단순화할 수 있다.

중간에 인터페이스를 둔다면 변경 보호 패턴(Protected Variations)에 해당된다.

순수한 가공물 패턴(Pure Fabrication)

Q. 책임을 할당한 도메인 객체가 Low Coupling, High Cohesion, 재사용성 등의 목적을 위반한다면 어떻게 해야 할까?

A. 도메인 개념을 포함하지 않는 클래스를 하나 만들고 매우 응집된 책임을 할당할 수 있다.

행동을 추가할 때, 해당 책임을 수행할 도메인 개념이 존재하지 않는다면 도메인과 무관한 인공적인 객체를 만든다음 해당 객체에게 책임을 할당한다.

객체가 데이터베이스에 저장해야 할 값을 가지고 있다고, 정보 전문가 패턴을 적용하여 데이터베이스에 저장하라는 책임을 가지라고 하지 않는다.

예) 상점과 고객 클래스가 있고 서로 다른 통화를 사용하고 있다고 가정

  • 서로 다른 통화를 사용하고 있기 때문에 거래를 하려면 환전을 해야한다.
  • 두 클래스 다 환전에 대한 책임을 부여하기 애매하다면 환전을 책임하는 클래스를 추가하고 사용할 수 있다.

참고 자료

오브젝트 5장. 책임 할당하기, 조영호

Applying UML and Patterns Chapter 16, Chapter 21 GRASP, Craig Larman

GRASP, 한빛 네트워크

+ + \ No newline at end of file diff --git a/page/37.html b/page/37.html index d05e7d11b..fd19028b1 100644 --- a/page/37.html +++ b/page/37.html @@ -13,44 +13,42 @@ - - + +
-

· 약 11분

사다리 타기

사다리 타기 미션에서는 우가와 페어가 매칭되었다.
-이전 미션과 달리 TDD로 진행하는 것이 필수였기 때문에 익숙하지 않았지만, 우가와 미션에 관한 소통이 잘 되어서 큰 문제 없이 미션을 마무리할 수 있었다.

우가와 이야기가 잘 통해서 그런지 1단계는 크게 어렵지 않게 진행할 수 있었는데, 2단계에서 많이 고전한 것 같다.

2단계에서는 2가지 방법으로 구현해봤다.

  1. LadderGame에서 Position 기준으로 사다리 게임을 진행하는 방법
  2. Player에게 Ladder를 넘겨서 Ladder에게 Position을 넘겨주며 메시지를 보내는 방법

Position 기준으로 사다리 게임을 진행하는 방법

사실상 index를 Ladder에게 넘겨주고, 해당 index에 대한 결과를 받는 방법과 유사했다.
-구현하고 나니 다른 클래스들이 Position에 대한 의존도가 너무 높은 것 같았다.
-또한 Players가 별다른 책임을 가지고 있지 않다고 느꼈다.

public LadderGameResult play() {
final Map<Player, Item> result = new LinkedHashMap<>();
// 사용자 수만큼 Position을 가져와서 사다리 게임을 진행한다.
for (Position position : Position.range(players.count())) {
final Position resultPosition = ladder.play(position);
result.put(players.get(position), items.get(resultPosition));
}
return new LadderGameResult(result);
}

Player에게 Ladder를 전달하여 게임을 진행하는 방법

Position에 대한 값을 가지고 있는 Player에게 Ladder를 넘겨서, Player가 Ladder에게 메시지를 보내도록 구현하였다.
-이 방법이 사다리 게임을 위해서 객체들이 긴밀하게 협력하고, 조금 더 책임의 분배가 잘 되어있다고 생각이 되었다.

public LadderGameResult play() {
// 참가자들에게 사다리를 전달해서 사다리에게 메시지를 보내도록 한다.
final Map<Player, Position> playResult = players.play(ladder);

final Map<Player, Item> result = new LinkedHashMap<>();
for (Player player : playResult.keySet()) {
result.put(player, toItem(playResult.get(player)));
}
return new LadderGameResult(result);
}

부족했던 부분

유비쿼터스 언어에 시간을 들이기
-유비쿼터스 언어를 정하는데 시간을 조금 더 들여야겠다고 생각했다.
-사다리 타기의 실행 결과를 Item으로 짓다니.. 뭔가 만족스럽지 않다.
-이전 미션과 마찬가지로, 명명하는 부분에서 부족함을 많이 느꼈다.

페어와 조금 더 친해지기
-첫날은 페어와 친해지는 시간을 조금 더 가져야겠다고 생각했다.
-우가랑 회고할 때 내가 시작하자마자 컨벤션 정하자고 해서 많이 당황스러웠다고 한다. 우가 미안.. 🥲

README를 조금 더 꼼꼼하게
-이상하게 코딩에 집중하면 README를 업데이트하면서 같이 커밋 하는 걸 항상 까먹는다.
-다음 미션에는 조금 더 신경 써야겠다.

좋은 질문을 생각하기
-첫 PR때 리뷰어에게 질문을 남기지 못했다.
-리뷰어와의 시간이 소중한 시간이라는 것을 까먹지 말고, 나의 성장에 도움이 될 수 있는 질문을 생각해야겠다.

PR 후에도 꼼꼼하게 확인하기
-분명 알고 있는 부분이지만, 놓친 부분이 많은 것 같았다.
-PR 하기 전에도 계속 확인을 했지만, 아무래도 IntelliJ에서 보니 코드에 익숙해져서 그런지 변경해야 할 부분이 잘 안보였다.
-github pr에서는 전체 변경사항을 확인할 수 있으니 PR 후에도 꼭 확인해야겠다.

적극적으로 나의 의견을 말하기
-의견을 적극적으로 내는 부분에 대해서 페어의 의견이 괜찮다고 생각하면 수용 후 개선을 하는 방향으로 진행을 했었는데, 조금 더 개선할 수 있는 방향이 있다면 나도 적극적으로 의견을 말해야겠다고 생각이 든다.
-나도 설득하는 힘을 기르고, 페어도 좋은 방향을 알 수 있고, 결과물도 좋은 방향으로 나오지 않을까? (고민 들어주신 리뷰어 터틀🐢 감사합니다.)

새로 학습한 부분

객체의 생성 책임
-Players가 Position을 생성하고 Player의 생성자에 넣어주었다. 하지만 이 부분에 대해서 생성 책임에 관련된 코멘트가 달렸다. -시간을 가지고 생각해 보니 Position을 가지고 있는 건 Player기 때문에 생성 책임을 Player가 담당하는 것이 좋다고 생각되었다.

생성 책임에 관한 패턴으로 GRASP의 Creator 패턴이 있는데 다음의 요소를 최대한 만족하는 클래스에 생성 책임을 할당하는 것이 좋다.

  • B가 A 객체를 포함 또는 참조한다.
  • B가 A 객체를 기록한다.
  • B가 A를 긴밀하게 사용한다.
  • B가 A의 초깃값을 가지고 있다.

실제로 객체의 생성 책임에 관해서 깊이 생각하면서 코딩을 하지 않았는데, 이번 미션을 통해 시야가 넓어진 것 같다.

패키지 분리 기준
-패키지 분리에 대한 나만의 기준이 아직 명확하지 않아 질문이 들어와도 명확하게 답변을 하지 못했다.
-마지막 제출 전에 도메인 패키지 내부를 분리해 봤는데, 기준이 명확하지 않았기 때문에 좋지 않은 선택이었던 것 같다. -현재 진행하는 미션의 애플리케이션 크기가 그렇게 크지 않으니, domain 패키지에서 세부 패키지로 분리하지 않아도 될 것 같다.

사용하는 쪽에서 생각하기 & 예측가능한 코드 작성하기
-Position에서 다음 위치나 이전 위치를 반환하는 메서드를 허용 범위(0~19)가 벗어난다면, 의미 없는 값이 들어간 Position을 반환하도록 했다.
-이건 Position을 사용하는 입장을 고려하지 못한 코딩이었는데, 사용하는 입장에서는 0~19의 값이 보장되어 있다고 생각할 것이기 때문이다.
-따라서 hasNext, hasPrevious라는 이전 값, 이후 값이 범위 내에 있는지 확인하는 메서드를 추가하고, 기존의 값을 가져오는 메서드는 범위가 벗어나면 예외를 던지는 방향으로 해결하였다.

페어에게 배울 부분

밝은 기운을 가지고 있고 다른 사람들과 친화력이 좋은 것 같았다.
-이번에 페어 할 때 컨디션 관리를 제대로 못해서 많이 미안했다. 다음에는 최상의 컨디션으로 페어를 준비해 봐야겠다.
-그리고 우가랑 페어를 하고 나서, 나도 다른 사람들과 더 잘 지내봐야겠다는 생각이 들어 조금 더 용기를 내 잡담 중이다!

의견을 적극적으로 내줘서 페어프로그래밍 진행이 잘 되었다.
-또한 페어 진행이 느린 것 같다고 말해줘서 안정적으로 시간 안에 미션을 완료할 수 있었다.
-페어프로그래밍 진행 속도에 대해 조금 더 생각을 해봐야겠다!

항상 지나갈 때마다 웃어주는데, 나도 자주 웃어야겠다고 생각했다.
-웃는 것만으로도 사람이 밝아 보여서 너무 좋은 것 같다!

- - +

· 약 6분

블랙잭

블랙잭 미션에서는 후추와 페어(조미료 듀오?)가 매칭되었다.
+이번에는 실수하지 않고, 바로 미션을 진행하지 않고 친해지기 부터 시작했다.

블랙잭은 구현해야 될 내용이 많아 시간이 많이 부족할 것 같았지만
+후추와 함께 전략적(삼일절에 미션 이야기 나누기)으로 미션을 진행해 시간 내에 제출할 수 있었다.

미션을 끝나고 회고를 했을 때 후추가 고민거리를 하나 내줬다.
+"페어를 진행할 때 압박감을 느끼는 페어가 있다면 허브가 해줄 수 있는게 뭐가 있을까?"

곰곰히 생각해봤지만 쉽게 답을 내릴 수 없었다.
+중간 중간 회고를 하고, 나의 소프트스킬을 높히는게 답일까?
+부담감을 느끼지 않고 같이 일하고 싶은 사람이 될 수 있도록 계속 생각해봐야겠다.

이 부분에 대해 생각이 많아져서 전 리뷰어인 터틀🐢과도 대화를 나누었다.
+터틀은 제어할 수 없는 부분보다 제어할 수 있는 부분(궁극적인 목표인 좋은 코드를 작성하는 것)에 집중해보라고 하셨다.

좋은 코드, 좋은 페어에 대한 부분을 일단 지속적으로 생각해봐야겠다.

부족했던 부분

페어 신경쓰기
+이번 페어할 때 적극적으로 의견을 내보도록 했다. 그렇기에 너무 의견을 강하게 밀어붙인 느낌이 들어서 미안했다.
+후추가 압박을 느꼈을 수도 있을 것 같다는 생각이 든다.
+중간 중간 작은 회고를 진행해보는 것이 좋을까?

체력 관리
+요즘 잘 못먹는 것 같다.
+앞으로 살 날이 많은데 잘 챙겨먹고, 힘내야겠다.

중간 중간 돌아보기
+이번 미션과 관련된 내용은 아니지만 우테코를 잘 활용 하고 있는지 생각을 해봐야겠다.
+내가 우테코에 지원한 이유를 항상 잊지 않아야겠다.

새로 학습한 부분

상태 패턴
+객체의 내부 상태에 따라 스스로 행동을 변경하도록 하는 패턴으로 if/else/switch와 같은 조건문을 효과적으로 제거할 수 있다.
+블랙잭 미션을 진행하면서 상태 패턴에 대한 부분을 처음 적용해보았다.
+처음 적용하기 전에는 별로라고 생각했는데, 생각보다 괜찮은 것 같다.

일관성, 가독성, 추상화
+이번 리뷰어는 검프🍫 였다!
+검프의 리뷰는 간결함에 관련된 내용이 많았다.
+일관성이 있는 코드, 가독성이 좋은 코드, 추상화가 잘 되어있는 코드
+읽기 좋고, 간결한 방향으로 코드를 작성하는 방법을 배운 것 같다.
+코드를 바라보는 시점이 하나 늘어난 기분이다!(앞으로 적용하는 것은 나의 몫이지만)

페어에게 배울 부분

생각 정리
+중간 중간 현재 상황에 대해 그림을 그리거나, 글을 적으면서 정리한다.
+페어와 동일한 부분을 이해하고 있는지 확인한다.
+진행하는데 매우 도움이 되었던 것 같다.
+나도 다음 페어때부터 펜이랑 종이를 준비해야겠다.

가감없이 의견을 말해주는 부분
+진행 상황에 대한 부분, 진행 속도, 지금 자신이 이해하고 있는 부분을 말해줘서 편했다.
+회고때도 서로 솔직하게 의견을 주고 받아서 좋았다.

도메인 언어에 신경쓰는 부분
+클래스명, 변수명과 같은 언어를 세심하게 신경쓴다.
+요구사항 정리도 깔끔하게 잘하는 것 같다.

후추 최고 👍

+ + \ No newline at end of file diff --git a/page/38.html b/page/38.html index ec473f896..4b7068d61 100644 --- a/page/38.html +++ b/page/38.html @@ -13,38 +13,44 @@ - - + +
-

· 약 8분

자동차 경주

자동차 경주 미션에서는 다즐과 페어가 매칭되었다.
-우테코 들어와서 첫 페어프로그래밍이라 많이 떨렸지만, 다즐이 대화를 잘 이끌어줘 너무 즐거웠다.

첫날은 간단히 컨벤션과 환경을 설정하는 시간을 가졌고 다음 날부터 자동차 경주를 시작했다.
-시작은 간단하게 요구사항을 정리하고, 어떻게 코드를 작성할지 같이 고민했다.

시작하기 전 아래와 같이 mermaid를 이용하여 의존성 방향에 대해서 간단한 다이어그램을 만들고 시작했다.
-mermaid는 코드로 다이어그램을 생성 해주는 도구로 다음과 같은 장점이 있다고 생각한다.

  • 코드 기반이라 빠른 시간 안에 생각한 것을 시각화할 수 있다.
  • github에서 mermaid를 지원하기 때문에 리뷰어에게 코드를 이해할 수 있는 부가적인 정보를 제공할 수 있다.

미션을 진행하는 데 큰 어려움이 있지는 않았고, 페어를 마치기 전 서로 고민되는 부분을 정리했을 때 좋았다.

페어하면서 잘했다고 생각했던 점은 서로의 생각과 리뷰 받은 것을 공유한 것이다.
-리팩터링을 어떻게 했는지? 이런 리뷰에 대해 어떻게 생각하는지 깊게 고민하는 시간을 가질 수 있었다.

부족했던 부분

리팩터링이 끝난 후 메서드명, 테스트시 출력하는 메시지에 대한 코멘트가 많이 달렸다.
-객체가 어떤 책임과 역할을 가지는지 생각하는 시간을 가지고 명확한 메서드명을 작성해야겠다고 생각했다.

평소에 프로그래밍 이야기가 아닌 다른 주제로 이야기하면 잘 들으려고 하는 편이지만
-내가 좋아하는 주제, 관심가는 주제인 프로그래밍에 대한 이야기를 할 땐 말이 많아진다.
-다음 미션부터는 더 많은 시간을 페어의 의견과 이야기를 듣는 곳에 사용해야겠다.

새로 학습한 부분

Assertions extracting

결과 내부에 있는 값을 확인하고 싶을 때 extracting 키워드를 이용해서 내부의 값을 검증할 수 있다.
-이전에는 필요에 따라 stream을 이용하여 검증할 값을 생성했지만, 해당 방법을 이용해서 절차를 줄일 수 있었다.

@Test
void extracting() {
final Cars cars = new Cars(List.of("car1", "car2"));

assertThat(cars.getCars())
.extracting(Car::getName)
.containsExactly("car1", "car2");
}

아래는 리뷰어님과 대화를 나누면서 얻은 답변 + 나의 의견이다.

제어할 수 없는 부분에 대한 테스트

테스트 대상이 검증된 것이라면 작성하지 않거나, 제어할 수 있는 부분에 대한 테스트를 더욱 꼼꼼하게 작성한다.
-이건 개인적인 생각이지만 내가 안정감이 들 수 있을 정도로 출력 범위 내의 결과를 반환하는지 정도 테스트할 수 있지 않을까?

단순 위임을 하는 메서드에 대한 테스트

위임이라는 것은 역할과 책임을 넘겨준다는 것이다.
-호출 횟수를 검증하는 것보다 결과에 대한 테스트하는 것이 좋다.
-단순히 위임만 하는 테스트의 경우 결과를 검증한다면 테스트가 중복되지 않을까 생각했었다.
-따라서 중복된 테스트를 줄이기 위해 내부의 메서드를 호출하는지 검증하는 방법도 있다는 것을 알게 되었지만
-안정적으로 결과를 테스트 하는 것이 더 좋은 방법인 것 같다.

테스트를 위한 getter 사용

테스트 용도로 도메인에 새로운 메서드가 생성되는 것은 좋지 못하다.
-필요의 경우 생성해서 사용할 수 있지만, 기존에 있는 메서드들을 활용해보는 것이 더 좋은 방법이다.
-이 부분에 대해서 매우 동의하고, 앞으로도 최대한 테스트를 위한 코드를 도메인에 작성하지 않을 것 같다.

페어에게 배울 부분

질문이나 생각할 점이 있을 때 매우 깊게 고민하는 것 같았다.
-생각을 정리한 후 자신의 의견을 명료하게 전달해주었다.
-그렇기 때문에 지식을 효율적으로 습득한다.
-난 생각을 잘 정리하지 않은 채로 내버려 둔 얕은 지식이 많은 것 같다. (이런 것도 아는 것이라고 할 수 있을까?)
-앞으로 조금 더 머릿속에서 정리하고, 문제에 대해 깊게 고민하는 시간을 늘려야겠다.

개발에 열정을 가진 게 느껴진다.
-나도 개발을 좋아하지만, 최근에는 의지가 약해졌었다.
-열정이 가득한 사람을 만나니 나도 열정적인 사람이 되는 것 같다.

칭찬을 많이 해준다. 단순히 많이 해주는 것이 아니라, 진심을 담긴 칭찬을 해줬다.
-칭찬은 고래도 춤추게 하던가?
-그래서 즐거운 마음으로 페어 프로그래밍을 했었던 것 같다.

어떤 이유 때문인지 모르겠지만 같이 페어하는데 편한 마음이 들었다.
-이건 바로 배울 수 없지만.
-나도 같이 일할 때 편한 사람, 같이 일하고 싶은 사람이 되기 위해 깊이 고민해봐야겠다.

- - +

· 약 11분

사다리 타기

사다리 타기 미션에서는 우가와 페어가 매칭되었다.
+이전 미션과 달리 TDD로 진행하는 것이 필수였기 때문에 익숙하지 않았지만, 우가와 미션에 관한 소통이 잘 되어서 큰 문제 없이 미션을 마무리할 수 있었다.

우가와 이야기가 잘 통해서 그런지 1단계는 크게 어렵지 않게 진행할 수 있었는데, 2단계에서 많이 고전한 것 같다.

2단계에서는 2가지 방법으로 구현해봤다.

  1. LadderGame에서 Position 기준으로 사다리 게임을 진행하는 방법
  2. Player에게 Ladder를 넘겨서 Ladder에게 Position을 넘겨주며 메시지를 보내는 방법

Position 기준으로 사다리 게임을 진행하는 방법

사실상 index를 Ladder에게 넘겨주고, 해당 index에 대한 결과를 받는 방법과 유사했다.
+구현하고 나니 다른 클래스들이 Position에 대한 의존도가 너무 높은 것 같았다.
+또한 Players가 별다른 책임을 가지고 있지 않다고 느꼈다.

public LadderGameResult play() {
final Map<Player, Item> result = new LinkedHashMap<>();
// 사용자 수만큼 Position을 가져와서 사다리 게임을 진행한다.
for (Position position : Position.range(players.count())) {
final Position resultPosition = ladder.play(position);
result.put(players.get(position), items.get(resultPosition));
}
return new LadderGameResult(result);
}

Player에게 Ladder를 전달하여 게임을 진행하는 방법

Position에 대한 값을 가지고 있는 Player에게 Ladder를 넘겨서, Player가 Ladder에게 메시지를 보내도록 구현하였다.
+이 방법이 사다리 게임을 위해서 객체들이 긴밀하게 협력하고, 조금 더 책임의 분배가 잘 되어있다고 생각이 되었다.

public LadderGameResult play() {
// 참가자들에게 사다리를 전달해서 사다리에게 메시지를 보내도록 한다.
final Map<Player, Position> playResult = players.play(ladder);

final Map<Player, Item> result = new LinkedHashMap<>();
for (Player player : playResult.keySet()) {
result.put(player, toItem(playResult.get(player)));
}
return new LadderGameResult(result);
}

부족했던 부분

유비쿼터스 언어에 시간을 들이기
+유비쿼터스 언어를 정하는데 시간을 조금 더 들여야겠다고 생각했다.
+사다리 타기의 실행 결과를 Item으로 짓다니.. 뭔가 만족스럽지 않다.
+이전 미션과 마찬가지로, 명명하는 부분에서 부족함을 많이 느꼈다.

페어와 조금 더 친해지기
+첫날은 페어와 친해지는 시간을 조금 더 가져야겠다고 생각했다.
+우가랑 회고할 때 내가 시작하자마자 컨벤션 정하자고 해서 많이 당황스러웠다고 한다. 우가 미안.. 🥲

README를 조금 더 꼼꼼하게
+이상하게 코딩에 집중하면 README를 업데이트하면서 같이 커밋 하는 걸 항상 까먹는다.
+다음 미션에는 조금 더 신경 써야겠다.

좋은 질문을 생각하기
+첫 PR때 리뷰어에게 질문을 남기지 못했다.
+리뷰어와의 시간이 소중한 시간이라는 것을 까먹지 말고, 나의 성장에 도움이 될 수 있는 질문을 생각해야겠다.

PR 후에도 꼼꼼하게 확인하기
+분명 알고 있는 부분이지만, 놓친 부분이 많은 것 같았다.
+PR 하기 전에도 계속 확인을 했지만, 아무래도 IntelliJ에서 보니 코드에 익숙해져서 그런지 변경해야 할 부분이 잘 안보였다.
+github pr에서는 전체 변경사항을 확인할 수 있으니 PR 후에도 꼭 확인해야겠다.

적극적으로 나의 의견을 말하기
+의견을 적극적으로 내는 부분에 대해서 페어의 의견이 괜찮다고 생각하면 수용 후 개선을 하는 방향으로 진행을 했었는데, 조금 더 개선할 수 있는 방향이 있다면 나도 적극적으로 의견을 말해야겠다고 생각이 든다.
+나도 설득하는 힘을 기르고, 페어도 좋은 방향을 알 수 있고, 결과물도 좋은 방향으로 나오지 않을까? (고민 들어주신 리뷰어 터틀🐢 감사합니다.)

새로 학습한 부분

객체의 생성 책임
+Players가 Position을 생성하고 Player의 생성자에 넣어주었다. 하지만 이 부분에 대해서 생성 책임에 관련된 코멘트가 달렸다. +시간을 가지고 생각해 보니 Position을 가지고 있는 건 Player기 때문에 생성 책임을 Player가 담당하는 것이 좋다고 생각되었다.

생성 책임에 관한 패턴으로 GRASP의 Creator 패턴이 있는데 다음의 요소를 최대한 만족하는 클래스에 생성 책임을 할당하는 것이 좋다.

  • B가 A 객체를 포함 또는 참조한다.
  • B가 A 객체를 기록한다.
  • B가 A를 긴밀하게 사용한다.
  • B가 A의 초깃값을 가지고 있다.

실제로 객체의 생성 책임에 관해서 깊이 생각하면서 코딩을 하지 않았는데, 이번 미션을 통해 시야가 넓어진 것 같다.

패키지 분리 기준
+패키지 분리에 대한 나만의 기준이 아직 명확하지 않아 질문이 들어와도 명확하게 답변을 하지 못했다.
+마지막 제출 전에 도메인 패키지 내부를 분리해 봤는데, 기준이 명확하지 않았기 때문에 좋지 않은 선택이었던 것 같다. +현재 진행하는 미션의 애플리케이션 크기가 그렇게 크지 않으니, domain 패키지에서 세부 패키지로 분리하지 않아도 될 것 같다.

사용하는 쪽에서 생각하기 & 예측가능한 코드 작성하기
+Position에서 다음 위치나 이전 위치를 반환하는 메서드를 허용 범위(0~19)가 벗어난다면, 의미 없는 값이 들어간 Position을 반환하도록 했다.
+이건 Position을 사용하는 입장을 고려하지 못한 코딩이었는데, 사용하는 입장에서는 0~19의 값이 보장되어 있다고 생각할 것이기 때문이다.
+따라서 hasNext, hasPrevious라는 이전 값, 이후 값이 범위 내에 있는지 확인하는 메서드를 추가하고, 기존의 값을 가져오는 메서드는 범위가 벗어나면 예외를 던지는 방향으로 해결하였다.

페어에게 배울 부분

밝은 기운을 가지고 있고 다른 사람들과 친화력이 좋은 것 같았다.
+이번에 페어 할 때 컨디션 관리를 제대로 못해서 많이 미안했다. 다음에는 최상의 컨디션으로 페어를 준비해 봐야겠다.
+그리고 우가랑 페어를 하고 나서, 나도 다른 사람들과 더 잘 지내봐야겠다는 생각이 들어 조금 더 용기를 내 잡담 중이다!

의견을 적극적으로 내줘서 페어프로그래밍 진행이 잘 되었다.
+또한 페어 진행이 느린 것 같다고 말해줘서 안정적으로 시간 안에 미션을 완료할 수 있었다.
+페어프로그래밍 진행 속도에 대해 조금 더 생각을 해봐야겠다!

항상 지나갈 때마다 웃어주는데, 나도 자주 웃어야겠다고 생각했다.
+웃는 것만으로도 사람이 밝아 보여서 너무 좋은 것 같다!

+ + \ No newline at end of file diff --git a/page/39.html b/page/39.html index 7943a677b..bb5a93bd8 100644 --- a/page/39.html +++ b/page/39.html @@ -13,20 +13,38 @@ - - + +
-

· 약 4분

테스트를 작성하다보면 매개변수에 따라 반복이 되는 테스트들이 생긴다.
-이 때 @ParameterizedTest를 사용하면 단일 테스트를 매개변수를 사용하여 여러 번 반복할 수 있다.

Argument Sources

@ParameterizedTest를 사용하려면 최소 하나 이상의 Source 애노테이션이 필요하다.
-JUnit이 제공하는 다양한 Source가 있기 때문에, 테스트에 맞춰 다양하게 사용할 수 있다.

Value Source

값을 이용하여 제공하는 형태로, 다음과 같은 타입의 값을 매개변수로 제공할 수 있다.

  • short, int, long, float, double
  • byte, char, boolean, String, Class
@ParameterizedTest
@ValueSource(ints = {1, 100, Integer.MAX_VALUE})
void valueTest(final int value) {
Assertions.assertThat(value).isPositive();
}

Null & Empty Source

null 값, 빈 값을 제공한다.
-Empty Source의 경우 다음과 같은 타입에 한해 매개변수로 제공할 수 있다.

  • String
  • java.util.List, java.util.Set, java.util.Map
  • primitive arrays — ex) int[]
  • object arrays — ex) String[]
@ParameterizedTest
@NullAndEmptySource
void nullAndEmptyTest(final String value) {
Assertions.assertThat(value).isNullOrEmpty();
}

Enum Source

EnumSource를 이용하여 Enum 또한 매개변수로 제공할 수 있다.

enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}

@ParameterizedTest
@EnumSource(Day.class)
void enumTest(final Day day) {
assertThat(day).isInstanceOf(Day.class);
}

다음과 같이 mode 값을 이용하여 특징 Enum을 제외하거나, 포함시킬 수 있다. (default: Mode.Include)

@ParameterizedTest
@EnumSource(value = Day.class, names = {"SATURDAY", "SUNDAY"}, mode = Mode.EXCLUDE)
void enumTest(final Day day) {
// MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
assertThat(day).isInstanceOf(Day.class);
}

CSV Source

csv 형식의 값을 이용하여 매개변수를 제공한다.
-구분자의 기본값은 쉼표(,)로 구분자를 변경하고 싶을 땐 delimeter 값을 따로 전달하여 사용할 수 있다. -개인적으로 2개 정도의 값을 매개변수로 전달하는 경우 CsvSource를 사용한다.

@ParameterizedTest
@CsvSource({"1,1", "2,4", "3,9", "4,16"})
void csvTest(final int number, final int result) {
assertThat(number * number).isEqualTo(result);
}

Method Source

복잡한 타입의 값을 전달할 때 사용한다.
-메서드명을 입력하여 매개변수를 제공하는 메서드를 지정할 수 있다.
-메서드명을 따로 입력하지 않으면 테스트명과 동일한 static 메서드가 지정된다.

@ParameterizedTest
@MethodSource
void methodTest(final List<Integer> numbers, final int count) {
assertThat(numbers).hasSize(count);
}

private static Stream<Arguments> methodTest() {
return Stream.of(
Arguments.of(List.of(1), 1),
Arguments.of(List.of(1, 2), 2),
Arguments.of(List.of(1, 2, 3), 3)
);
}

ETC.

위에서 언급한 방법 이외에도 다양한 방법으로 매개변수를 제공할 수 있다.

  • CSV 파일을 이용한 CsvFileSource
  • ArgumentsProvider 구현한 클래스를 이용하는 ArgumentsSource

참고 자료

- - +

· 약 8분

자동차 경주

자동차 경주 미션에서는 다즐과 페어가 매칭되었다.
+우테코 들어와서 첫 페어프로그래밍이라 많이 떨렸지만, 다즐이 대화를 잘 이끌어줘 너무 즐거웠다.

첫날은 간단히 컨벤션과 환경을 설정하는 시간을 가졌고 다음 날부터 자동차 경주를 시작했다.
+시작은 간단하게 요구사항을 정리하고, 어떻게 코드를 작성할지 같이 고민했다.

시작하기 전 아래와 같이 mermaid를 이용하여 의존성 방향에 대해서 간단한 다이어그램을 만들고 시작했다.
+mermaid는 코드로 다이어그램을 생성 해주는 도구로 다음과 같은 장점이 있다고 생각한다.

  • 코드 기반이라 빠른 시간 안에 생각한 것을 시각화할 수 있다.
  • github에서 mermaid를 지원하기 때문에 리뷰어에게 코드를 이해할 수 있는 부가적인 정보를 제공할 수 있다.

미션을 진행하는 데 큰 어려움이 있지는 않았고, 페어를 마치기 전 서로 고민되는 부분을 정리했을 때 좋았다.

페어하면서 잘했다고 생각했던 점은 서로의 생각과 리뷰 받은 것을 공유한 것이다.
+리팩터링을 어떻게 했는지? 이런 리뷰에 대해 어떻게 생각하는지 깊게 고민하는 시간을 가질 수 있었다.

부족했던 부분

리팩터링이 끝난 후 메서드명, 테스트시 출력하는 메시지에 대한 코멘트가 많이 달렸다.
+객체가 어떤 책임과 역할을 가지는지 생각하는 시간을 가지고 명확한 메서드명을 작성해야겠다고 생각했다.

평소에 프로그래밍 이야기가 아닌 다른 주제로 이야기하면 잘 들으려고 하는 편이지만
+내가 좋아하는 주제, 관심가는 주제인 프로그래밍에 대한 이야기를 할 땐 말이 많아진다.
+다음 미션부터는 더 많은 시간을 페어의 의견과 이야기를 듣는 곳에 사용해야겠다.

새로 학습한 부분

Assertions extracting

결과 내부에 있는 값을 확인하고 싶을 때 extracting 키워드를 이용해서 내부의 값을 검증할 수 있다.
+이전에는 필요에 따라 stream을 이용하여 검증할 값을 생성했지만, 해당 방법을 이용해서 절차를 줄일 수 있었다.

@Test
void extracting() {
final Cars cars = new Cars(List.of("car1", "car2"));

assertThat(cars.getCars())
.extracting(Car::getName)
.containsExactly("car1", "car2");
}

아래는 리뷰어님과 대화를 나누면서 얻은 답변 + 나의 의견이다.

제어할 수 없는 부분에 대한 테스트

테스트 대상이 검증된 것이라면 작성하지 않거나, 제어할 수 있는 부분에 대한 테스트를 더욱 꼼꼼하게 작성한다.
+이건 개인적인 생각이지만 내가 안정감이 들 수 있을 정도로 출력 범위 내의 결과를 반환하는지 정도 테스트할 수 있지 않을까?

단순 위임을 하는 메서드에 대한 테스트

위임이라는 것은 역할과 책임을 넘겨준다는 것이다.
+호출 횟수를 검증하는 것보다 결과에 대한 테스트하는 것이 좋다.
+단순히 위임만 하는 테스트의 경우 결과를 검증한다면 테스트가 중복되지 않을까 생각했었다.
+따라서 중복된 테스트를 줄이기 위해 내부의 메서드를 호출하는지 검증하는 방법도 있다는 것을 알게 되었지만
+안정적으로 결과를 테스트 하는 것이 더 좋은 방법인 것 같다.

테스트를 위한 getter 사용

테스트 용도로 도메인에 새로운 메서드가 생성되는 것은 좋지 못하다.
+필요의 경우 생성해서 사용할 수 있지만, 기존에 있는 메서드들을 활용해보는 것이 더 좋은 방법이다.
+이 부분에 대해서 매우 동의하고, 앞으로도 최대한 테스트를 위한 코드를 도메인에 작성하지 않을 것 같다.

페어에게 배울 부분

질문이나 생각할 점이 있을 때 매우 깊게 고민하는 것 같았다.
+생각을 정리한 후 자신의 의견을 명료하게 전달해주었다.
+그렇기 때문에 지식을 효율적으로 습득한다.
+난 생각을 잘 정리하지 않은 채로 내버려 둔 얕은 지식이 많은 것 같다. (이런 것도 아는 것이라고 할 수 있을까?)
+앞으로 조금 더 머릿속에서 정리하고, 문제에 대해 깊게 고민하는 시간을 늘려야겠다.

개발에 열정을 가진 게 느껴진다.
+나도 개발을 좋아하지만, 최근에는 의지가 약해졌었다.
+열정이 가득한 사람을 만나니 나도 열정적인 사람이 되는 것 같다.

칭찬을 많이 해준다. 단순히 많이 해주는 것이 아니라, 진심을 담긴 칭찬을 해줬다.
+칭찬은 고래도 춤추게 하던가?
+그래서 즐거운 마음으로 페어 프로그래밍을 했었던 것 같다.

어떤 이유 때문인지 모르겠지만 같이 페어하는데 편한 마음이 들었다.
+이건 바로 배울 수 없지만.
+나도 같이 일할 때 편한 사람, 같이 일하고 싶은 사람이 되기 위해 깊이 고민해봐야겠다.

+ + \ No newline at end of file diff --git a/page/4.html b/page/4.html index 0b6a57b45..ac1ff4395 100644 --- a/page/4.html +++ b/page/4.html @@ -13,27 +13,65 @@ - - + +
-

· 약 4분

회고

지난 8주는 레벨 1, 2 때보다 5배 정도 빠르게 지나간 것 같은 느낌이 들었다.
-레벨 3에는 기술적인 부분에서도, 기술 외적인 부분에서도 부족함이 많이 보였던 것 같다.
-부족한 부분을 알았기에, 앞으로 더욱 성장할 수 있을 것 같다.
-내가 부족했던 부분을 팀원들이 잘 보충해 줘서 든든했다.

아쉬운 점

문서화

개인적으로는 기술 외적으로 학습한 부분을 잘 정리하지 못했다.
-프로젝트를 진행하면서 내가 한 부분을 조금 더 꼼꼼하게, 이해하기 쉽게 문서화를 했더라면 팀원들에게 더욱 도움이 되었을 텐데 이 부분에 시간을 조금 더 투자하지 못했던 부분에서 아쉬움이 많이 들었다.
-방학 기간 동안 문서화를 하지 못했던 부분을 개인 블로그 올리면서 조금 더 채워보려고 한다.

내가 못하는 부분이라면 시간을 들이자

잘 못하는 부분이라면 시간을 들여서라도 중간은 가도록 해야겠다는 생각이 많이 들었다.
-말을 하기 전에 정리해서 의견을 내는 것, 발표 준비, 감정 조절 등등 -못하는 부분을 인지하고, 개선하자.

컴포트 존 벗어나기

조금 더 도전적으로 목표를 잡았으면 좋았을 것 같다.
-매번 근거를 가지고 기술을 도입하고, 코드를 작성하려고 노력했다.
-하지만 지속적으로 개선하려고 하는 부분이 다소 부족했다.

좋았던 점

좋았던 점도 문서화

팀 블로그도 먼저 도입하자고 제안하고, 내가 했던 부분은 문서화를 꽤 많이 해서 팀원들과 공유할 수 있었다.
-백엔드 크루 4명이서 같이 한 부분에 대해서는 기능 구현한다고 문서화가 조금 미흡해서 보충을 해야겠다.

내가 디자인한 트립드로우 로고

트립드로우 로고를 만들었다.
-팀원들이 대표 색상(파란색)을 정해줬고, 주말 동안 신나게 로고 디자인을 했던 것 같다.
-아래의 D 부분은 유튜브 강의 들으면서 직접 만들어서 뿌듯하다.

기술 선택의 이유

기술의 학습 비용, 현재 구조에 적합한지, 실제 가지고 있는 리소스를 고려해서 기술 선택을 하고, 도입했던 부분이 좋았다.
-100% 좋은 선택일 순 없지만, 그래도 선택에 대한 근거가 존재한다면 확률을 높혀주는 것 같다.

마치며

플레이스토어에 앱이 올라가 있는 거 너무 신기하다.
-안드로이드 브레멘 음악대(멧돼지, 수달, 핑구), 그리고 백엔드 팀원들(체인저, 후추, 리오) 너무 고생이 많았다.

- - +

· 약 21분

복제(Replication)

한 서버에서 다른 서버로 데이터를 동기화하는 것을 의미한다.
+원본 데이터를 가지는 서버를 Primary 또는 Source 라고 부르고, 복제된 데이터를 가지는 서버를 Secondary 또는 Replica 라고 부른다.

복제를 하는 이유

1. 스케일 아웃

사용자의 트래픽이 증가하는 경우, 데이터베이스에 가해지는 부하도 자연스럽게 증가한다.
+이를 처리하기 위해 복제를 통한 스케일 아웃을 적용하여 애플리케이션에서 사용하는 쿼리들을 각각의 데이터베이스로 분산 시킬 수 있다.

2. 데이터 백업

실제 운영되는 서비스가 사용하고 있는 DB에서 백업을 진행하는 경우, 서비스에 영향을 미칠 수 있다.
+따라서 실제 서비스에 영향이 가지 않도록 복제를 통해 Replica 서버를 구축하여, Replica 서버에서 복제를 진행하는 방법으로 영향을 최소화 할 수 있다.

3. 데이터 분석

백업과 마찬가지로 복잡하고 무거운 분석용 쿼리의 서비스에 영향을 미칠 수 있다.
+마찬가지로 복제를 사용해 분석용 쿼리를 사용할 수 있는 환경을 만들 수 있다.

4. 데이터의 지리적 분산

빠른 응답을 위해 애플리케이션 서버에 가깝게 서버를 구성하거나, 고가용성(High Availability)을 위해서도 사용된다.

바이너리 로그 파일 위치 기반 복제

MySQL 서버에서 발생하는 변경사항에 대한 로그 파일을 바이너리 로그라고 한다.
+바이너리 로그를 통해 데이터 변경, 테이블 구조 변경, 계정이나 권한 변경에 대한 정보가 저장된다.
+MySQL의 복제는 바이너리 로그 기반으로 구현되어 있다. 이를 Replica 서버로 전달하고 바이너리 로그 기반으로 데이터를 변경 사항을 반영한다.

스레드별 역할

Binary Log Dump Thread: 바이너리 로그의 내용을 Replica 서버로 전달
+Replication I/O Thread: Binary 로그 이벤트를 가져와 로컬 서버의 파일(Relay Log)로 저장
+Replication SQL Thread: 릴레이 로그 파일의 이벤트를 읽고 실행

바이너리 로그 방식의 문제점

바이너리 로그 방식은 서버에 장애가 발생했을 때 복제 토폴로지 변경이 까다롭다.
+토폴로지란 네트워크의 요소들을 물리적으로 연결해 놓은 것, 또는 그 연결 방식을 말한다.

위와 같이 Source 서버, Replica 2대가 존재하고, C 서버에 복제 지연이 되었을 때 문제가 발생한다.

A 서버에서 장애가 발생한다면 B 서버를 Source 서버로 승격하고, C에게 조회 쿼리를 분산시킨다.
+하지만 여기서 C 서버에는 A 서버와 동기화가 안되었으니 조회 시 문제가 발생한다.
+뒤늦게 B 서버와 동기화를 하려고 해도, 어떤 바이너리 로그, 어떤 위치와 동기화해야하는지 알기 어렵다.

글로벌 트랜잭션 아이디(GTID) 기반 복제

GTID 방식을 사용하여 참여한 모든 데이터베이스가 발생한 이벤트에 고유한 식별값을 부여한다면, 동기화에 대한 문제를 간단하게 해결할 수 있다.
+위의 예시와 같이 복제 지연과 함께 장애가 발생한다해도 특정 GTID 부터 복제를 재개하면 된다.

GTID

복제 토폴로지에 참여한 모든 서버에서 고유하도록 각 이벤트에 부여된 식별값
+[source_id]:[transaction_id]로 구성되며, source_id는 서버를 식별하기 위한 값이고 transaction_id는 커밋된 트랜잭션을 식별하기 위한 값으로 1씩 증가하는 형태로 발급된다.

복제 토폴로지

싱글 레플리카 복제 구성

가장 간단한 구성으로 제일 많이 사용하는 형태다.
+replica 서버를 읽기 전용, 예비 서버, 백업 용도로 많이 사용한다.

멀티 레플리카 복제 구성

2개의 replica 서버를 사용하는 형태다.
+하나의 replica는 예비 용도로 남겨두는 형태다.
+추후에 트래픽이 증가하는 경우 예비 용도의 replica를 사용함으로 읽기 요청의 부하 분산을 할 수 있다.

체인 복제 구성

replica 서버가 많은 경우 바이너리 로그를 전달하는 작업 자체가 부하가 될 수 있다.
+따라서 1:M:M 구조로 체인 복제 구성을 고려할 수 있다.

듀얼 소스 복제 구성

2개의 MySQL 서버 모두 읽기와 쓰기가 가능하도록 하는 구성이다.
+각 서버에서 변경된 데이터는 다른 서버에 반영된다.
+목적에 따라 ACTIVE-ACTIVE 형태 또는 ACTIVE-PASSIVE 형태로 사용할 수 있다.
+ACTIVE-PASSIVE 형태인 경우 싱글 레플리카 복제 구성과 동일해보이지만, ACTIVE 서버에서 문제가 발생하면 설정의 변경없이 PASSIVE 서버로 쓰기 작업을 전환할 수 있다는 것이 장점이다.

ACTIVE-ACTIVE, ACTIVE-PASSIVE

ACTIVE-ACTIVE: 2개의 서버 모두 쓰기 작업을 수행하는 형태
+ACTIVE-PASSIVE: 하나의 서버에서만 쓰기 작업을 수행하는 형태

멀티 소스 복제 구성

여러개의 source 서버와 하나의 replica 서버를 사용하는 구성이다.
+이는 source 서버의 데이터를 한 곳에 백업하는 용도로 사용, 여러 서버에 존재하는 데이터를 통합, 샤딩되어있는 테이블 데이터를 통합할 때 사용한다.

바이너리 로그 방식 Replication 구성하기

mysql 2대를 이용하여 replication을 구성하고, spring boot application으로 source, replica 데이터베이스에 접근해보는 예제이다.
+https://github.com/bbiac/db-replication

MySQL 환경 구성

MySQL 버전은 8.1을 사용했다.
+13306, 13307 포트를 사용해서 MySQL 서버 2대를 띄웠다.
+또한 사실 IP 대역으로 통신할 수 있도록 커스텀 네트워크를 추가했다.

version: '3.8'

services:
source:
platform: linux/x86_64
image: mysql:latest
restart: always
container_name: mysql-source
environment:
TZ: 'Asia/Seoul'
MYSQL_DATABASE: 'db'
MYSQL_USER: 'user'
MYSQL_PASSWORD: 'password'
MYSQL_ROOT_PASSWORD: 'password'
ports:
- "13306:3306"
volumes:
- db-source:/var/lib/mysql
- db-source:/var/lib/mysql-files
- ./docker/source.cnf:/etc/mysql/my.cnf
networks:
- mysql_network

replica:
platform: linux/x86_64
image: mysql:latest
restart: always
container_name: mysql-replica
environment:
TZ: 'Asia/Seoul'
MYSQL_DATABASE: 'db'
MYSQL_USER: 'user'
MYSQL_PASSWORD: 'password'
MYSQL_ROOT_PASSWORD: 'password'
ports:
- "13307:3306"
volumes:
- db-replica:/var/lib/mysql
- db-replica:/var/lib/mysql-files
- ./docker/replica.cnf:/etc/mysql/my.cnf
networks:
- mysql_network

volumes:
db-source:
db-replica:

networks:
mysql_network:
driver: bridge

또한 source, replica 각각 다음과 같이 db 설정을 했다.

설정설명
server_id각각의 mysql 마다 고유한 값을 가져야 한다.
log_bin바이너리 로그 파일 경로 설정으로 절대경로를 사용하지 않는다면 /var/lib/mysql 아래 해당 log_bin에 설정된 값으로 로그가 생성된다.
sync_binlogN개의 트랜잭션 당 바이너리 로그를 디스크와 동기화 작업을 하도록 한다. 1은 기본값으로 안정적이지만, 가장 느리다.
relay_log릴레이 로그 파일 경로 설정
relay_log_purge필요 없는 릴레이 로그 파일을 자동으로 삭제하는 옵션
read_only읽기 전용 설정
log_replica_updatesReplication SQL Thread로 인해 실행되는 정보를 바이너리 로그에 기록 추후에 소스 서버로 승격되는 경우를 고려하면 설정하는 것이 좋다.
/docker/source.cnf
[mysqld]
server_id=1
log_bin=mysql-bin
sync_binlog=1

도커 실행

docker-compose up 명령어로 docker-compose 설정으로 docker를 띄운다.
+-d 옵션을 붙이면 백그라운드 모드로 실행된다.

docker-compose up -d

replication slave 권한 설정

REPLICATION SLAVE 권한이 설정되어 있어야 replica 서버에서 source 서버에 접근하여 로그를 읽어올 수 있다.
+source 서버에 접근하여 user 계정에 해당 권한을 설정해준다.

SOURCE 접속

docker exec -it mysql-source mysql -u root -p

user 계정에 REPLICATION SLAVE 권한 추가

GRANT REPLICATION SLAVE ON *.* TO 'user'@'%';
FLUSH PRIVILEGES;

SOURCE DB 정보 확인

replica 설정에 필요한 source db의 바이너리 로그 파일명과 Position을 확인한다.
+Position 값은 실제 파일의 바이트 수를 의미한다.
+확인한 File(SOURCE_LOG_FILE)과 Position(SOURCE_LOG_POS) 값은 replica 설정에서 사용한다.

SHOW MASTER STATUS;

+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 | 1082 | | | |
+------------------+----------+--------------+------------------+-------------------+

SOURCE ip 주소 확인

docker inspect -f 옵션을 사용하면 해당 컨테이너의 세부 정보를 확인할 수 있다.
+다음 명령어를 이용해 docker-compose 파일에 설정해둔 mysql_network에서 사용되는 사설 아이피 주소를 확인한다.

docker inspect -f "{{with index .NetworkSettings.Networks \"db-replication_mysql_network\"}}{{.IPAddress}}{{end}}" mysql-source

ip 주소가 나오지 않는 경우 docker inspect mysql-source로 확인한다.
+확인한 IP주소(SOURCE_HOST) 값은 replica 설정에서 사용한다.

replica mysql 접속

source db에 접속했던 방법과 동일하게 replica db에 접속한다.

docker exec -it mysql-replica mysql -u root -p

replica 설정

이전에 source db에서 얻었던 정보들을 사용하여 replica 설정을 진행한다.
+실제 DB 서버에서 복제하는 경우 추가적으로 source DB의 파일을 복제해야하지만 현재 복제할 데이터가 없기 때문에 해당 부분은 생략했다.
+SOURCE_HOST, SOURCE_LOG_FILE, SOURCE_LOG_POS 를 적절히 변경한다.

STOP REPLICA;

CHANGE REPLICATION SOURCE TO
SOURCE_HOST='172.29.0.2',
SOURCE_USER='user',
SOURCE_PASSWORD='password',
SOURCE_LOG_FILE='mysql-bin.000001',
SOURCE_LOG_POS=0,
GET_SOURCE_PUBLIC_KEY=1;

START REPLICA;

설정 확인

SHOW REPLICA STATUS;

+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+
| Replica_IO_State | Source_Host | Source_User | Source_Port | Connect_Retry | Source_Log_File | Read_Source_Log_Pos | Relay_Log_File | Relay_Log_Pos | Relay_Source_Log_File | Replica_IO_Running | Replica_SQL_Running | Replicate_Do_DB | Replicate_Ignore_DB | Replicate_Do_Table | Replicate_Ignore_Table | Replicate_Wild_Do_Table | Replicate_Wild_Ignore_Table | Last_Errno | Last_Error | Skip_Counter | Exec_Source_Log_Pos | Relay_Log_Space | Until_Condition | Until_Log_File | Until_Log_Pos | Source_SSL_Allowed | Source_SSL_CA_File | Source_SSL_CA_Path | Source_SSL_Cert | Source_SSL_Cipher | Source_SSL_Key | Seconds_Behind_Source | Source_SSL_Verify_Server_Cert | Last_IO_Errno | Last_IO_Error | Last_SQL_Errno | Last_SQL_Error | Replicate_Ignore_Server_Ids | Source_Server_Id | Source_UUID | Source_Info_File | SQL_Delay | SQL_Remaining_Delay | Replica_SQL_Running_State | Source_Retry_Count | Source_Bind | Last_IO_Error_Timestamp | Last_SQL_Error_Timestamp | Source_SSL_Crl | Source_SSL_Crlpath | Retrieved_Gtid_Set | Executed_Gtid_Set | Auto_Position | Replicate_Rewrite_DB | Channel_Name | Source_TLS_Version | Source_public_key_path | Get_Source_public_key | Network_Namespace |
+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+
| Waiting for source to send event | 172.25.0.3 | user | 3306 | 60 | mysql-bin.000003 | 1082 | mysql-relay-bin.000002 | 868 | mysql-bin.000003 | Yes | Yes | | | | | | | 0 | | 0 | 1082 | 1078 | None | | 0 | No | | | | | | 0 | No | 0 | | 0 | | | 1 | 5a396b02-41c6-11ee-a56d-0242ac190003 | mysql.slave_master_info | 0 | NULL | Replica has read all relay log; waiting for more updates | 86400 | | | | | | | | 0 | | | | | 1 | |
+----------------------------------+-------------+-------------+-------------+---------------+------------------+---------------------+------------------------+---------------+-----------------------+--------------------+---------------------+-----------------+---------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-------------------------+-----------+---------------------+----------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+------------------------+-----------------------+-------------------+

Replica_IO_Running, Replica_SQL_Running 값이 YES라면 정상적으로 replication 구성이 완료된 것이다.

설정을 마친 후 source db에 다음과 같이 create table 명령어를 입력한다.
+replica db에 동일한 member table이 생성된 것을 확인할 수 있다.

CREATE TABLE member
(
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255)
);

스프링 부트로 DB 접근하기

일반적인 트랜잭션의 경우 source, 읽기 전용 트랜잭션인 경우 replica로 요청이 가도록 구성해보자.

Environment 설정

다음과 같이 source, replica로 구분하여 설정한다.

application.yml
spring:
datasource:
source:
username: user
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:13306/db
replica:
username: user
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:13307/db

DataSourceType 설정

단순 문자열로도 구분할 수 있지만, enum을 이용해서 트랜잭션을 구분하도록 생성한다.
+Key는 추후에 빈 설정에 사용한다.

DataSourceType
public enum DataSourceType {
SOURCE(SOURCE_NAME),
REPLICA(REPLICA_NAME),
;

private final String key;

DataSourceType(String key) {
this.key = key;
}

public static class Key {
public static final String ROUTING_NAME = "ROUTING";
public static final String SOURCE_NAME = "SOURCE";
public static final String REPLICA_NAME = "REPLICA";
}
}

AbstractRoutingDataSource 설정

스프링이 지원해주는 AbstractRoutingDataSource를 상속받아 트랜잭션의 읽기 여부에 따라 다른 DataSource를 향하도록 설정한다.

정적 팩터리 메서드는 Map<DataSourceKey, DataSource>에 해당하는 값을 받아 데이터 소스를 설정한다.

  • setDefaultTargetDataSource: 기본 데이터 소스를 설정한다.
  • setTargetDataSources: 맵 형태로 받은 데이터 소스 값들을 설정한다.

determineCurrentLookupKey를 오버라이딩하여 트랜잭션의 읽기 여부에 따라 다른 DataSourceType을 반환하도록 설정한다.

  • isCurrentTransactionReadOnly() 메서드를 통해 트랜잭션이 읽기 전용인지 확인할 수 있다.
  • DataSourceType을 반환하도록 설정하고, 반환한 값에 해당하는 데이터 소스가 사용된다.
RoutingDataSource
public class RoutingDataSource extends AbstractRoutingDataSource {

private final Logger log = LoggerFactory.getLogger(getClass());

public static RoutingDataSource from(Map<Object, Object> dataSources) {
RoutingDataSource routingDataSource = new RoutingDataSource();
routingDataSource.setDefaultTargetDataSource(dataSources.get(DataSourceType.SOURCE));
routingDataSource.setTargetDataSources(dataSources);
return routingDataSource;
}

@Override
protected Object determineCurrentLookupKey() {
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();

if (readOnly) {
log.info("readOnly = true, request to replica");
return DataSourceType.REPLICA;
}
log.info("readOnly = false, request to source");
return DataSourceType.SOURCE;
}
}

DataSource 설정

위에서부터 순서대로 Source, Replica, RoutingDataSource, LazyConnectionDataSourceProxy 설정이다.
+스프링은 트랜잭션 시작시에 커넥션의 사용여부와 상관없이 커넥션을 확보한다.
+따라서 readOnly 트랜잭션이 설정된 메서드를 사용하더라도 미리 확보된 커넥션을 사용하기 때문에 replica db로 요청을 하지 않고 setDefaultTargetDataSource로 설정한 source db로 요청을 한다.
+LazyConnectionDataSourceProxy를 설정하는 경우 실제 DataSource를 사용하는 시점에 커넥션을 획득해서 사용하기 때문에 설정한대로 replica db로 조회 요청을 한다.

DataSourceConfiguration
@Configuration
public class DataSourceConfiguration {

@Bean
@Qualifier(SOURCE_NAME)
@ConfigurationProperties(prefix = "spring.datasource.source")
public DataSource sourceDataSource() {
return DataSourceBuilder.create().build();
}

@Bean
@Qualifier(REPLICA_NAME)
@ConfigurationProperties(prefix = "spring.datasource.replica")
public DataSource replicaDataSource() {
return DataSourceBuilder.create().build();
}

@Bean
@Qualifier(ROUTING_NAME)
public DataSource routingDataSource(
@Qualifier(SOURCE_NAME) DataSource sourceDataSource,
@Qualifier(REPLICA_NAME) DataSource replicaDataSource
) {
return RoutingDataSource.from(Map.of(
DataSourceType.SOURCE, sourceDataSource,
DataSourceType.REPLICA, replicaDataSource
));
}

@Bean
@Primary
public DataSource dataSource(
@Qualifier(ROUTING_NAME) DataSource routingDataSource
) {
return new LazyConnectionDataSourceProxy(routingDataSource);
}
}

최종적으로 DataSource 빈은 다음과 같은 형태가 된다.

동작 확인

간단하게 테스트를 작성해서 설정한대로 동작이 되는지 확인해보았다.
+save 메서드의 경우 @Transactional, findById 메서드의 경우 @Transactional(readOnly = true)가 설정되어있다.
+로그를 통해 save의 경우 source db로 findById의 경우 replica db로 요청을 하는 것을 알 수 있다.

MemberServiceTest
@SpringBootTest
class MemberServiceTest {

@Autowired
private MemberService memberService;

@Test
void 사용자를_저장한다() {
// RoutingDataSource log: readOnly = false
memberService.save("bbiac");
}

@Test
void 사용자를_조회한다() {
// RoutingDataSource log: readOnly = true
assertThatThrownBy(() -> memberService.findById(MAX_VALUE))
.isInstanceOf(NoSuchElementException.class);
}
}

DB에서는 확인하려면 root 계정으로 접속한 후 general log를 활성화 시킨다.

SET GLOBAL log_output = 'table';
SET GLOBAL general_log = 1;

general log를 활성화 한 후 읽기 전용 메서드를 실행한다.
+server_id, 실행한 쿼리문을 확인할 수 있다.

SELECT user_host, thread_id, server_id, convert(argument using utf8) FROM mysql.general_log where argument like '%select%';

+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+
| user_host | thread_id | server_id | convert(argument using utf8) |
+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+
| user[user] @ [172.25.0.1] | 277 | 2 | select m1_0.id,m1_0.name from member m1_0 where m1_0.id=9223372036854775807 |
+----------------------------+-----------+-----------+-----------------------------------------------------------------------------+

확인 후 general log를 비활성화 한 후 비활성화 되었는지 확인한다.

SET GLOBAL general_log = 0;
SHOW VARIABLES LIKE '%general%';

+------------------+---------------------------------+
| Variable_name | Value |
+------------------+---------------------------------+
| general_log | OFF |
| general_log_file | /var/lib/mysql/4b6b9db98290.log |
+------------------+---------------------------------+

참고 자료

16장 복제, Real MySQL 8.0 - 백은빈, 이성욱
+Replication, MySQL Docs
+MySql - Master Slave Replication 구조 만들어보기
+Spring 레플리케이션 트랜잭션 처리 방식
+replication-datasource
+Simplified Guide to MySQL Replication with Docker Compose
+Dockerfile에서 자주 쓰이는 명령어
+CHANGE REPLICATION SOURCE TO Statement
+LazyConnectionDataSourceProxy
+데이터베이스 레플리케이션을 통한 쿼리 성능 개선 (feat. Mysql, SpringBoot)
+부하 분산을 위한 MySQL Replication 구성 및 쿼리 요청 분기
+Use Docker Compose, Docker

+ + \ No newline at end of file diff --git a/page/40.html b/page/40.html index 087b78e15..0249c6cee 100644 --- a/page/40.html +++ b/page/40.html @@ -13,13 +13,20 @@ - - + +
-

· 약 1분

Import 자동 적용

Prefrences > Editor > General > Auto Import > Add unambiguous imports on the fly

auto-import

저장시 동작

Prefrences > Tools > Actions on Save

actions-on-save

Reformat Code: Code Reformmating

Optimize imports: 사용하지 않는 Import 제거

Rearrange: Code Style > Arrangement 설정 기반 코드 재정렬

메소드 추출, 변수 추출시 final 적용

Prefrences > Editor > Code Style > Java > Code Generation > Final Modifier

final-modifier

- - +

· 약 4분

테스트를 작성하다보면 매개변수에 따라 반복이 되는 테스트들이 생긴다.
+이 때 @ParameterizedTest를 사용하면 단일 테스트를 매개변수를 사용하여 여러 번 반복할 수 있다.

Argument Sources

@ParameterizedTest를 사용하려면 최소 하나 이상의 Source 애노테이션이 필요하다.
+JUnit이 제공하는 다양한 Source가 있기 때문에, 테스트에 맞춰 다양하게 사용할 수 있다.

Value Source

값을 이용하여 제공하는 형태로, 다음과 같은 타입의 값을 매개변수로 제공할 수 있다.

  • short, int, long, float, double
  • byte, char, boolean, String, Class
@ParameterizedTest
@ValueSource(ints = {1, 100, Integer.MAX_VALUE})
void valueTest(final int value) {
Assertions.assertThat(value).isPositive();
}

Null & Empty Source

null 값, 빈 값을 제공한다.
+Empty Source의 경우 다음과 같은 타입에 한해 매개변수로 제공할 수 있다.

  • String
  • java.util.List, java.util.Set, java.util.Map
  • primitive arrays — ex) int[]
  • object arrays — ex) String[]
@ParameterizedTest
@NullAndEmptySource
void nullAndEmptyTest(final String value) {
Assertions.assertThat(value).isNullOrEmpty();
}

Enum Source

EnumSource를 이용하여 Enum 또한 매개변수로 제공할 수 있다.

enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}

@ParameterizedTest
@EnumSource(Day.class)
void enumTest(final Day day) {
assertThat(day).isInstanceOf(Day.class);
}

다음과 같이 mode 값을 이용하여 특징 Enum을 제외하거나, 포함시킬 수 있다. (default: Mode.Include)

@ParameterizedTest
@EnumSource(value = Day.class, names = {"SATURDAY", "SUNDAY"}, mode = Mode.EXCLUDE)
void enumTest(final Day day) {
// MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
assertThat(day).isInstanceOf(Day.class);
}

CSV Source

csv 형식의 값을 이용하여 매개변수를 제공한다.
+구분자의 기본값은 쉼표(,)로 구분자를 변경하고 싶을 땐 delimeter 값을 따로 전달하여 사용할 수 있다. +개인적으로 2개 정도의 값을 매개변수로 전달하는 경우 CsvSource를 사용한다.

@ParameterizedTest
@CsvSource({"1,1", "2,4", "3,9", "4,16"})
void csvTest(final int number, final int result) {
assertThat(number * number).isEqualTo(result);
}

Method Source

복잡한 타입의 값을 전달할 때 사용한다.
+메서드명을 입력하여 매개변수를 제공하는 메서드를 지정할 수 있다.
+메서드명을 따로 입력하지 않으면 테스트명과 동일한 static 메서드가 지정된다.

@ParameterizedTest
@MethodSource
void methodTest(final List<Integer> numbers, final int count) {
assertThat(numbers).hasSize(count);
}

private static Stream<Arguments> methodTest() {
return Stream.of(
Arguments.of(List.of(1), 1),
Arguments.of(List.of(1, 2), 2),
Arguments.of(List.of(1, 2, 3), 3)
);
}

ETC.

위에서 언급한 방법 이외에도 다양한 방법으로 매개변수를 제공할 수 있다.

  • CSV 파일을 이용한 CsvFileSource
  • ArgumentsProvider 구현한 클래스를 이용하는 ArgumentsSource

참고 자료

+ + \ No newline at end of file diff --git a/page/41.html b/page/41.html index 85de02bd7..384147cfa 100644 --- a/page/41.html +++ b/page/41.html @@ -13,24 +13,13 @@ - - + +
-

· 약 5분

nullable 타입

코틀린은 NullPointerException 예외를 최대한 발생시키지 않기 위해 타입 시스템이 설계되어 있다.
-이는 실행 시점이 아닌 컴파일 시 미리 오류가 발생할 가능성이 있는 부분을 미리 감지하여 NPE 발생의 가능성을 줄여준다.

코틀린의 경우 nullable 타입을 다음과 같이 표현한다.

val number: Int?

타입 뒤에 ?를 붙여 해당 값이 null이 될 수 있다는 것을 의미한다.
-만약 ?를 붙이지 않을 때 null을 받는 경우 컴파일 시 오류가 발생한다.

?. Safe Calls 연산자

자바에서 NPE를 발생시키지 않기 위해 null을 처리하는 가장 간단한 방법으로는 분기를 사용하는 방법이 있다.

코틀린은 안전한 호출 연산자인 ?. 연산자를 지원한다.
-따라서 참조 값이 null이 아닐 경우에만 메서드 호출을 할 수 있다.
-참조 값이 null인 경우 메서드 호출이 무시되고, null을 반환한다.

public String repeat(String word) {
if (word == null) {
return null;
}
return word.repeat(2);
}

?: 엘비스 연산자

참조하려는 값이 null일 경우 기본 값을 반환하고 싶을 때는 어떻게 해야 할까?
-코틀린은 null이 아닌 경우 기본 값을 지정할 때 사용할 수 있는 엘비스 연산자를 지원한다.

public String stringSafe(String word) {
if (word == null) {
return "";
}
return word;
}

코틀린에서는 throw도 식이기 때문에 엘비스 연산자를 이용하여 예외를 던질 수 있다.
-예를 들어 사용자 정보가 있는 저장소에 찾는 사용자가 없는 경우 아래와 같이 사용할 수 있다.

userRepository.findByName(name) ?: throw IllegalArgumentException()

!! 널 아님 단언 연산자

!! 연산자를 이용한다면 강제로 어떤 값이든 non-nullable 타입으로 변경할 수 있다.
-하지만 null인 값에 사용한다면 NPE가 발생하게 된다.
-일반적인 경우에는 !! 연산자를 사용하는 것은 위험하다.
-사용하기 쉽지만, 리스크가 크고 혹시나 해당 값이 추후에는 null이 될 수 있기 때문에 지양해야 된다고 생각한다.

val length: Int = word!!.length

as? 안전한 캐스팅

타입 변환을 할 때 지정한 타입으로 변경할 수 없다면 ClassCastException이 발생한다.
-코틀린에서는 as 뒤에 ?를 붙여 안전하게 타입 변환을 할 수 있다.
-따라서 미리 변환 가능한 타입인지 확인하지 않고, 안전하게 타입을 변환 할 수 있다.

타입 변환이 불가능 할 경우 예외를 발생시키지 않고 null을 반환한다.

val value: Int? = something as? Int

List에서의 null 처리

List에는 null이 아닌 값만 반환하는 filterNotNull 유틸리티 메서드를 제공한다.

val foodsWithNull: List<String?> = listOf("Pizza", "Cheese", null, "Potato")
val foods = foodsWithNull.filterNotNull()

참고 자료

- - +

· 약 1분

Import 자동 적용

Prefrences > Editor > General > Auto Import > Add unambiguous imports on the fly

auto-import

저장시 동작

Prefrences > Tools > Actions on Save

actions-on-save

Reformat Code: Code Reformmating

Optimize imports: 사용하지 않는 Import 제거

Rearrange: Code Style > Arrangement 설정 기반 코드 재정렬

메소드 추출, 변수 추출시 final 적용

Prefrences > Editor > Code Style > Java > Code Generation > Final Modifier

final-modifier

+ + \ No newline at end of file diff --git a/page/42.html b/page/42.html index 90960a225..45b6cc4a3 100644 --- a/page/42.html +++ b/page/42.html @@ -13,19 +13,24 @@ - - + +
-

· 약 2분

이전에 많은 문제가 있던 자바의 클래스(Calendar, Date)를 대체하는 날짜와 시간 API
-ISO-8601을 기반으로 작성
-설계 목표 → 불변, Fluent API, 명확하고 명시적, 확장 가능성

ISO-8601

날짜와 시간에 관련된 데이터를 다루는 국제 표준

LocalDate, LocalTime, LocalDateTime

날짜와 시간을 표현하는 클래스

Instant

유닉스 시간(1970-01-01, 00:00:00 UTC) 기준으로 특정 지점까지의 시간을 초로 표현하는 클래스
-기계의 관점에서 시간 표현

Duration, Period

간격을 표현하는 클래스

TemporalAdjusters

복잡한 날짜 조정이 필요할 때 사용
-필요한 경우 다음 인터페이스를 구현하여 커스텀 TemporalAdjuster를 구현 가능

@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}

DateTimeFormatter

날짜와 시간 포맷 클래스
-특정 날짜 패턴이나, DateTimeFormatterBuilder를 이용해서 커스텀한 포맷을 생성 가능

ZoneId, ZoneOffset

ZoneId는 지역 ID는 ‘지역/도시’ 형식, ZoneOffset은 시차 UTC 기준 고정된 시간 차이 이용
-ZoneId의 경우 IANA Time Zone Database에서 제공하는 지역 집합 정보 사용

Instant instant = Instant.now();
LocalDateTime utc = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

참고 자료

- - +

· 약 5분

nullable 타입

코틀린은 NullPointerException 예외를 최대한 발생시키지 않기 위해 타입 시스템이 설계되어 있다.
+이는 실행 시점이 아닌 컴파일 시 미리 오류가 발생할 가능성이 있는 부분을 미리 감지하여 NPE 발생의 가능성을 줄여준다.

코틀린의 경우 nullable 타입을 다음과 같이 표현한다.

val number: Int?

타입 뒤에 ?를 붙여 해당 값이 null이 될 수 있다는 것을 의미한다.
+만약 ?를 붙이지 않을 때 null을 받는 경우 컴파일 시 오류가 발생한다.

?. Safe Calls 연산자

자바에서 NPE를 발생시키지 않기 위해 null을 처리하는 가장 간단한 방법으로는 분기를 사용하는 방법이 있다.

코틀린은 안전한 호출 연산자인 ?. 연산자를 지원한다.
+따라서 참조 값이 null이 아닐 경우에만 메서드 호출을 할 수 있다.
+참조 값이 null인 경우 메서드 호출이 무시되고, null을 반환한다.

public String repeat(String word) {
if (word == null) {
return null;
}
return word.repeat(2);
}

?: 엘비스 연산자

참조하려는 값이 null일 경우 기본 값을 반환하고 싶을 때는 어떻게 해야 할까?
+코틀린은 null이 아닌 경우 기본 값을 지정할 때 사용할 수 있는 엘비스 연산자를 지원한다.

public String stringSafe(String word) {
if (word == null) {
return "";
}
return word;
}

코틀린에서는 throw도 식이기 때문에 엘비스 연산자를 이용하여 예외를 던질 수 있다.
+예를 들어 사용자 정보가 있는 저장소에 찾는 사용자가 없는 경우 아래와 같이 사용할 수 있다.

userRepository.findByName(name) ?: throw IllegalArgumentException()

!! 널 아님 단언 연산자

!! 연산자를 이용한다면 강제로 어떤 값이든 non-nullable 타입으로 변경할 수 있다.
+하지만 null인 값에 사용한다면 NPE가 발생하게 된다.
+일반적인 경우에는 !! 연산자를 사용하는 것은 위험하다.
+사용하기 쉽지만, 리스크가 크고 혹시나 해당 값이 추후에는 null이 될 수 있기 때문에 지양해야 된다고 생각한다.

val length: Int = word!!.length

as? 안전한 캐스팅

타입 변환을 할 때 지정한 타입으로 변경할 수 없다면 ClassCastException이 발생한다.
+코틀린에서는 as 뒤에 ?를 붙여 안전하게 타입 변환을 할 수 있다.
+따라서 미리 변환 가능한 타입인지 확인하지 않고, 안전하게 타입을 변환 할 수 있다.

타입 변환이 불가능 할 경우 예외를 발생시키지 않고 null을 반환한다.

val value: Int? = something as? Int

List에서의 null 처리

List에는 null이 아닌 값만 반환하는 filterNotNull 유틸리티 메서드를 제공한다.

val foodsWithNull: List<String?> = listOf("Pizza", "Cheese", null, "Potato")
val foods = foodsWithNull.filterNotNull()

참고 자료

+ + \ No newline at end of file diff --git a/page/43.html b/page/43.html index f28b1419d..4cbbbd984 100644 --- a/page/43.html +++ b/page/43.html @@ -13,32 +13,19 @@ - - + +
-

· 약 6분

책 정보

객체지향의 사실과 오해
-조영호

읽고 나서

조영호님의 오브젝트를 읽고 나서 다시 한 번 읽어보았다.
-아직 이해가 안되는 부분이 많지만, 그래도 항상 새로움을 느낀다.
-더할 나위 없이 휼륭한 객체지향 책이고, 조금 더 공부하고 다시 읽어봐야될 것 같다.

커피 전문점, 지하철 노선도, 이상한 나라의 엘리스를 예시로 든 설명이 너무 좋았고
-좋은 내용을 담고 있지만 그렇다고 너무 무겁지 않아 가볍게 읽기도 좋은 것 같다.

책임의 자율성을 강조하는 이유 p.173

협력을 단순하게 만든다.

  • 의도를 명확하게 표현 → 협력의 복잡함 저하
  • 책임의 추상화

외부와 내부를 명확하게 분리한다.

  • 요청하는 객체가 몰라도 되는 부분이 캡슐화됨으로 인터페이스와 구현의 분리

책임을 수행하는 내부적인 방법을 변경하더라도 외부에 영향을 미치지 않는다.

  • 변경의 파급효과를 객체 내부로 캡슐화 → 메시지를 보내는 객체와의 결합도 저하

협력의 대상을 다양하게 선택할 수 있는 유연성을 제공한다.

  • 유연한 설계 → 재사용성 증가

객체의 역할을 이해하기 쉬워진다.

  • 응집도를 높은 상태로 유지

밑줄 친 문장들

객체지향의 목표는 실세계를 모방하는 것이 아니다. -오히려 새로운 세계를 창조하는 것이다. -소프트웨어 개발자의 역할은 단순히 실세계를 소프트웨어 안으로 옮겨 담는 것이 아니라 고객과 사용자를 만족시킬 수 있는 신세계를 창조하는 것이다. -p.21

과거의 전통적인 개발 방법은 데이터와 프로세스를 엄격하게 구분한다. -이에 반해 객체지향에서는 데이터와 프로세스를 객체라는 하나의 틀 안에 함께 묶어 놓음으로써 객체의 자율성을 보장한다. -자율적인 객체로 구성된 공동체는 유지 보수가 쉽고 재사용이 용이한 시스템을 구축할 수 있는 가능성을 제시한다. -p.33

객체지향의 본질

시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분할하는 방법

자율적인 객체란 상태와 행위를 함께 지니며 스스로 자기 자신을 책임지는 객체를 의미한다.

객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다. 각 객체는 협력 내에서 정해진 역할을 수행하며 역할은 관련된 책임의 집합이다.

객체는 다른 객체와 협력하기 위해 메시지를 전송하고, 메시지를 수신한 객체는 메시지를 처리하는 데 적합한 메서드를 자율적으로 선택한다. -p.35

클래스의 구조와 메서드가 아니라 객체의 역할, 책임, 협력에 집중하라. -객체지향은 객체를 지향하는 것이지 클래스를 지향하는 것이 아니다. -p.38

객체지향에서 중요한 것은 동적으로 변하는 객체의 ‘상태’와 상태를 변경하는 ‘행위’다. -클래스는 타입을 구현하기 위해 프로그래밍 언어에서 제공하는 구현 메커니즘이라는 사실을 기억하라. -p.105

책임 주도 설계의 핵심은 어떤 행위가 필요한지를 먼저 결정한 후에 이 행위를 수행할 객체를 결정하는 것이다. -이 과정을 흔히 What/Who 사이클이라고 한다. -’어떤 행위(What)’를 수행할 것인지 결정한 후 ‘누가(who)’ 그 행위를 수행할 것인지 결정해야 한다. -여기서 ‘어떤 행위’가 바로 메시지다. -p.158

- - +

· 약 2분

이전에 많은 문제가 있던 자바의 클래스(Calendar, Date)를 대체하는 날짜와 시간 API
+ISO-8601을 기반으로 작성
+설계 목표 → 불변, Fluent API, 명확하고 명시적, 확장 가능성

ISO-8601

날짜와 시간에 관련된 데이터를 다루는 국제 표준

LocalDate, LocalTime, LocalDateTime

날짜와 시간을 표현하는 클래스

Instant

유닉스 시간(1970-01-01, 00:00:00 UTC) 기준으로 특정 지점까지의 시간을 초로 표현하는 클래스
+기계의 관점에서 시간 표현

Duration, Period

간격을 표현하는 클래스

TemporalAdjusters

복잡한 날짜 조정이 필요할 때 사용
+필요한 경우 다음 인터페이스를 구현하여 커스텀 TemporalAdjuster를 구현 가능

@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}

DateTimeFormatter

날짜와 시간 포맷 클래스
+특정 날짜 패턴이나, DateTimeFormatterBuilder를 이용해서 커스텀한 포맷을 생성 가능

ZoneId, ZoneOffset

ZoneId는 지역 ID는 ‘지역/도시’ 형식, ZoneOffset은 시차 UTC 기준 고정된 시간 차이 이용
+ZoneId의 경우 IANA Time Zone Database에서 제공하는 지역 집합 정보 사용

Instant instant = Instant.now();
LocalDateTime utc = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

참고 자료

+ + \ No newline at end of file diff --git a/page/44.html b/page/44.html index 02731544f..af0ea0975 100644 --- a/page/44.html +++ b/page/44.html @@ -13,27 +13,32 @@ - - + +
-

· 약 4분

적당한 전환점, 2022년을 돌아보며

전역

약 1년 6개월간의 공군 정보보호병 생활을 마치고 전역을 했다.
-조기 전역 때문에 2021년 12월에 나왔지만, 실제 전역 날짜는 2022년이니 회고에 적어도 상관없겠지.

조금 더 미래에 대한 생각을 해볼걸 그랬다.
-전역을 했지만 뭐 하나 제대로 할 줄 아는 것도 없으니 넓은 바닷속에 덩그러니 놓아진 기분이 괜히 들었었다.
-일찍 생각을 정리하여 방향을 잡지 못했기에 아쉬움이 많이 남았다.

자바

전역을 하고 진로를 고민하다 향로님의 자바 공화국 포스팅을 읽고 나서 자바 공부를 시작했다.
-유명한 인프런의 김영한님의 스프링 강의도 있고, 좋은 자바 개발 서적이 많아서 독학하기로 결정했다.
-하다 보니 자바와 스프링을 공부하면서 “왜 진작하지 않았지”라는 생각도 많이 들었다.
-양질의 자료도 많았기 때문에, 예전에 노드로 개발했을 때 풀지 못했던 답답함을 많이 해소했던 것 같다.

23년에는 조금 더 깊게 자바를 공부해볼 생각이다.
-언어를 하나 깊게 공부하는 건 많은 도움이 되는 것 같다.

스터디

김영한님의 강의를 거의 다 들었을 때쯤, 항상 강의에서 언급되는 토비의 스프링을 읽어보고 싶어졌고
-혼자 공부하기에는 동기부여도 부족했기 때문에 스터디를 시작했다.
-다른 사람에게 설명을 해야 했기 때문에 더욱 꼼꼼하게 공부를 할 수 있어서 좋았지만 나에게는 내용이 꽤나 어려워서 시간을 많이 소비했다.
-같이 스터디하시는 분과 7개월 동안 스터디를 꾸준히 이어나가 총 3권의 책을 읽을 수 있었다.

우아한 테크코스

군 복무 중일 때 지원했다 떨어진 우아한 테크코스를 다시 지원했다.
-이번 연도에 취업을 하는 게 목표였지만 내가 가지고 있는 특별한 무기가 없다는 걸 깨달았다.
-적지 않은 시간을 투자해 준비를 했고, 감사하게도 이번에는 최종 합격을 했다.

난 사람들과 소통하고, 협업하는 능력이 부족하다고 생각을 많이 했다.
-우아한 테크코스를 통해 그 빈 부분을 채우도록 노력해야겠다.

2023년에는

마음의 여유가 없었던 2022년이었던 것 같다.
-하고 싶은 건 많지만, 이번에는 여유를 가지고 할 수 있는 것에 최선을 다해야겠다.

- - +

· 약 6분

책 정보

객체지향의 사실과 오해
+조영호

읽고 나서

조영호님의 오브젝트를 읽고 나서 다시 한 번 읽어보았다.
+아직 이해가 안되는 부분이 많지만, 그래도 항상 새로움을 느낀다.
+더할 나위 없이 휼륭한 객체지향 책이고, 조금 더 공부하고 다시 읽어봐야될 것 같다.

커피 전문점, 지하철 노선도, 이상한 나라의 엘리스를 예시로 든 설명이 너무 좋았고
+좋은 내용을 담고 있지만 그렇다고 너무 무겁지 않아 가볍게 읽기도 좋은 것 같다.

책임의 자율성을 강조하는 이유 p.173

협력을 단순하게 만든다.

  • 의도를 명확하게 표현 → 협력의 복잡함 저하
  • 책임의 추상화

외부와 내부를 명확하게 분리한다.

  • 요청하는 객체가 몰라도 되는 부분이 캡슐화됨으로 인터페이스와 구현의 분리

책임을 수행하는 내부적인 방법을 변경하더라도 외부에 영향을 미치지 않는다.

  • 변경의 파급효과를 객체 내부로 캡슐화 → 메시지를 보내는 객체와의 결합도 저하

협력의 대상을 다양하게 선택할 수 있는 유연성을 제공한다.

  • 유연한 설계 → 재사용성 증가

객체의 역할을 이해하기 쉬워진다.

  • 응집도를 높은 상태로 유지

밑줄 친 문장들

객체지향의 목표는 실세계를 모방하는 것이 아니다. +오히려 새로운 세계를 창조하는 것이다. +소프트웨어 개발자의 역할은 단순히 실세계를 소프트웨어 안으로 옮겨 담는 것이 아니라 고객과 사용자를 만족시킬 수 있는 신세계를 창조하는 것이다. +p.21

과거의 전통적인 개발 방법은 데이터와 프로세스를 엄격하게 구분한다. +이에 반해 객체지향에서는 데이터와 프로세스를 객체라는 하나의 틀 안에 함께 묶어 놓음으로써 객체의 자율성을 보장한다. +자율적인 객체로 구성된 공동체는 유지 보수가 쉽고 재사용이 용이한 시스템을 구축할 수 있는 가능성을 제시한다. +p.33

객체지향의 본질

시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분할하는 방법

자율적인 객체란 상태와 행위를 함께 지니며 스스로 자기 자신을 책임지는 객체를 의미한다.

객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다. 각 객체는 협력 내에서 정해진 역할을 수행하며 역할은 관련된 책임의 집합이다.

객체는 다른 객체와 협력하기 위해 메시지를 전송하고, 메시지를 수신한 객체는 메시지를 처리하는 데 적합한 메서드를 자율적으로 선택한다. +p.35

클래스의 구조와 메서드가 아니라 객체의 역할, 책임, 협력에 집중하라. +객체지향은 객체를 지향하는 것이지 클래스를 지향하는 것이 아니다. +p.38

객체지향에서 중요한 것은 동적으로 변하는 객체의 ‘상태’와 상태를 변경하는 ‘행위’다. +클래스는 타입을 구현하기 위해 프로그래밍 언어에서 제공하는 구현 메커니즘이라는 사실을 기억하라. +p.105

책임 주도 설계의 핵심은 어떤 행위가 필요한지를 먼저 결정한 후에 이 행위를 수행할 객체를 결정하는 것이다. +이 과정을 흔히 What/Who 사이클이라고 한다. +’어떤 행위(What)’를 수행할 것인지 결정한 후 ‘누가(who)’ 그 행위를 수행할 것인지 결정해야 한다. +여기서 ‘어떤 행위’가 바로 메시지다. +p.158

+ + \ No newline at end of file diff --git a/page/45.html b/page/45.html index 829ec1413..8dbddcda1 100644 --- a/page/45.html +++ b/page/45.html @@ -13,18 +13,27 @@ - - + +
-

· 약 5분

책 정보

글, 우리도 잘 쓸 수 있습니다.
-박솔미

읽고 나서

저자의 경험과 함께 글쓰기에 대한 가벼운 조언이 담겨있어 가볍게 읽기 좋았다.
-글을 잘 작성해 보고 싶을 때 적용해 볼 수 있는 정보가 많아서 도움이 되었다.

우아한 테크코스의 프리코스를 진행할 때 후기를 작성하고 나면 항상 글이 딱딱하다는 느낌을 받았다.
-다른 지원자들의 읽기 편하고, 밝은 느낌을 주는 글을 보면 부러운 마음을 가지기도 했다.
-이 책을 읽었으니 2023년에는 조금 더 글을 잘 적어보려고 한다.

밑줄 친 문장들

문장이 심심하고 지루하다면 -내용을 일목요연하게 정리했고, 글의 의도도 삐뚤지 않고, 단어도 적절한 것으로 골랐는데… 그런데도 어딘가가 심심하고 지루하다면? 축축 처지고 따분하다면? 말꼬리를 모조리 ‘~다’로 통일한 건 아닌지 점검해 보세요.

말꼬리를 잘 갖고 놀아야 합니다. 문장의 마지막 글자를 매번 다르게 고쳐쓰는 것만으로도 글에 활기를 더할 수 있죠. 때론 문장을 다 마치지 않고, 단어로만 끝맺는 것도 방법. 문장과 문장 사이에 쉼표가 들어서며 글 전체에 활기가 돌게 돼요. 문장의 길이도 다채로워지는 덕분에 덤으로 얻게 되는 것도 있습니다. 바로, 글의 리듬.

이전 문장에서 끝난 글자로, 다음 문장을 끝맺지 않기. 한두 문단마다 단어 수준의 아주 짧은 문장 배치하기.

글의 진짜 이유, 글의 진짜 목적, 글의 진짜 대상을 찾으려고 애썼습니다. 지금처럼 틀을 떠올린다거나, 눈치를 본다거나, 정치적인 셈도 하지 않았어요.

제목은 짧게, 보기 쉽게, 읽기 쉽게, 발음이 비슷하게, 순서를 바꿔서

글을 마지막으로 다듬을 때, 노래에 가까워질 방법은 없을지 고민해봅니다. 감히 가 닿을 수 없는 목표이겠지만, 할 수 있는 최소한의 리듬이라도 붙여주고 싶어요.

여는 말과 마지막 말에 작정하고 마음을 담는 연습을 해봅시다. 글의 어느 구석이라도 뻔한 글자는 남기지 않겠노라 다짐하며 써보는 겁니다. 나만이 가진 유일한 메시지에 집중하면서요. 그럼 생각이 달라지고, 고르는 단어도 달라지고, 남긴 문장도 달라져요. 결국에는 글을 쓴 사람인 나 자신도 남달라질 겁니다.

맞춤법은 중요합니다. 하지만 맞춤법보다 더 중요한 건 거기에 담긴 마음입니다. 내 마음을 글에 담아 실어 보내기 전, 맞춤법을 점검하는 이유 역시 그겁니다. 오직 내 마음이 남에게 읽히는 동안 방해가 되지 않기를 바라기 때문이죠. 내가 쓴 글도, 남이 쓴 글도. 언제나 그 안에 담긴 마음이 먼저입니다.

글을 쓴다고 글이 완성되는 게 아니에요. 글과 닮은 모습으로 살 때, 글은 비로소 완성됩니다.

- - +

· 약 4분

적당한 전환점, 2022년을 돌아보며

전역

약 1년 6개월간의 공군 정보보호병 생활을 마치고 전역을 했다.
+조기 전역 때문에 2021년 12월에 나왔지만, 실제 전역 날짜는 2022년이니 회고에 적어도 상관없겠지.

조금 더 미래에 대한 생각을 해볼걸 그랬다.
+전역을 했지만 뭐 하나 제대로 할 줄 아는 것도 없으니 넓은 바닷속에 덩그러니 놓아진 기분이 괜히 들었었다.
+일찍 생각을 정리하여 방향을 잡지 못했기에 아쉬움이 많이 남았다.

자바

전역을 하고 진로를 고민하다 향로님의 자바 공화국 포스팅을 읽고 나서 자바 공부를 시작했다.
+유명한 인프런의 김영한님의 스프링 강의도 있고, 좋은 자바 개발 서적이 많아서 독학하기로 결정했다.
+하다 보니 자바와 스프링을 공부하면서 “왜 진작하지 않았지”라는 생각도 많이 들었다.
+양질의 자료도 많았기 때문에, 예전에 노드로 개발했을 때 풀지 못했던 답답함을 많이 해소했던 것 같다.

23년에는 조금 더 깊게 자바를 공부해볼 생각이다.
+언어를 하나 깊게 공부하는 건 많은 도움이 되는 것 같다.

스터디

김영한님의 강의를 거의 다 들었을 때쯤, 항상 강의에서 언급되는 토비의 스프링을 읽어보고 싶어졌고
+혼자 공부하기에는 동기부여도 부족했기 때문에 스터디를 시작했다.
+다른 사람에게 설명을 해야 했기 때문에 더욱 꼼꼼하게 공부를 할 수 있어서 좋았지만 나에게는 내용이 꽤나 어려워서 시간을 많이 소비했다.
+같이 스터디하시는 분과 7개월 동안 스터디를 꾸준히 이어나가 총 3권의 책을 읽을 수 있었다.

우아한 테크코스

군 복무 중일 때 지원했다 떨어진 우아한 테크코스를 다시 지원했다.
+이번 연도에 취업을 하는 게 목표였지만 내가 가지고 있는 특별한 무기가 없다는 걸 깨달았다.
+적지 않은 시간을 투자해 준비를 했고, 감사하게도 이번에는 최종 합격을 했다.

난 사람들과 소통하고, 협업하는 능력이 부족하다고 생각을 많이 했다.
+우아한 테크코스를 통해 그 빈 부분을 채우도록 노력해야겠다.

2023년에는

마음의 여유가 없었던 2022년이었던 것 같다.
+하고 싶은 건 많지만, 이번에는 여유를 가지고 할 수 있는 것에 최선을 다해야겠다.

+ + \ No newline at end of file diff --git a/page/46.html b/page/46.html new file mode 100644 index 000000000..5a2fe4fd4 --- /dev/null +++ b/page/46.html @@ -0,0 +1,30 @@ + + + + + +Blog | GG + + + + + + + + + + + + + +
+

· 약 5분

책 정보

글, 우리도 잘 쓸 수 있습니다.
+박솔미

읽고 나서

저자의 경험과 함께 글쓰기에 대한 가벼운 조언이 담겨있어 가볍게 읽기 좋았다.
+글을 잘 작성해 보고 싶을 때 적용해 볼 수 있는 정보가 많아서 도움이 되었다.

우아한 테크코스의 프리코스를 진행할 때 후기를 작성하고 나면 항상 글이 딱딱하다는 느낌을 받았다.
+다른 지원자들의 읽기 편하고, 밝은 느낌을 주는 글을 보면 부러운 마음을 가지기도 했다.
+이 책을 읽었으니 2023년에는 조금 더 글을 잘 적어보려고 한다.

밑줄 친 문장들

문장이 심심하고 지루하다면 +내용을 일목요연하게 정리했고, 글의 의도도 삐뚤지 않고, 단어도 적절한 것으로 골랐는데… 그런데도 어딘가가 심심하고 지루하다면? 축축 처지고 따분하다면? 말꼬리를 모조리 ‘~다’로 통일한 건 아닌지 점검해 보세요.

말꼬리를 잘 갖고 놀아야 합니다. 문장의 마지막 글자를 매번 다르게 고쳐쓰는 것만으로도 글에 활기를 더할 수 있죠. 때론 문장을 다 마치지 않고, 단어로만 끝맺는 것도 방법. 문장과 문장 사이에 쉼표가 들어서며 글 전체에 활기가 돌게 돼요. 문장의 길이도 다채로워지는 덕분에 덤으로 얻게 되는 것도 있습니다. 바로, 글의 리듬.

이전 문장에서 끝난 글자로, 다음 문장을 끝맺지 않기. 한두 문단마다 단어 수준의 아주 짧은 문장 배치하기.

글의 진짜 이유, 글의 진짜 목적, 글의 진짜 대상을 찾으려고 애썼습니다. 지금처럼 틀을 떠올린다거나, 눈치를 본다거나, 정치적인 셈도 하지 않았어요.

제목은 짧게, 보기 쉽게, 읽기 쉽게, 발음이 비슷하게, 순서를 바꿔서

글을 마지막으로 다듬을 때, 노래에 가까워질 방법은 없을지 고민해봅니다. 감히 가 닿을 수 없는 목표이겠지만, 할 수 있는 최소한의 리듬이라도 붙여주고 싶어요.

여는 말과 마지막 말에 작정하고 마음을 담는 연습을 해봅시다. 글의 어느 구석이라도 뻔한 글자는 남기지 않겠노라 다짐하며 써보는 겁니다. 나만이 가진 유일한 메시지에 집중하면서요. 그럼 생각이 달라지고, 고르는 단어도 달라지고, 남긴 문장도 달라져요. 결국에는 글을 쓴 사람인 나 자신도 남달라질 겁니다.

맞춤법은 중요합니다. 하지만 맞춤법보다 더 중요한 건 거기에 담긴 마음입니다. 내 마음을 글에 담아 실어 보내기 전, 맞춤법을 점검하는 이유 역시 그겁니다. 오직 내 마음이 남에게 읽히는 동안 방해가 되지 않기를 바라기 때문이죠. 내가 쓴 글도, 남이 쓴 글도. 언제나 그 안에 담긴 마음이 먼저입니다.

글을 쓴다고 글이 완성되는 게 아니에요. 글과 닮은 모습으로 살 때, 글은 비로소 완성됩니다.

+ + + + \ No newline at end of file diff --git a/page/5.html b/page/5.html index ac84f861c..aeb3842e6 100644 --- a/page/5.html +++ b/page/5.html @@ -13,32 +13,27 @@ - - + +
-

· 약 6분

CloudWatch

AWS 리소스와 애플리케이션의 지표와 로그에 대한 모니터링을 제공하는 서비스다.
-지표를 감시하여 알림을 보내는 기능도 제공한다.
-프리티어를 사용하지 않는 경우 대시보드당 3$/M 의 비용이 청구되고, 지표나 로그의 양에 따라 비용이 추가적으로 청구된다.
-요금 정보에 대한 자세한 정보는 다음 링크에서 확인할 수 있다.

CloudWatch Metrics

기본적으로 5분마다 지표에 대한 정보가 수집된다.
-세부 모니터링(Detailed Monitoring)을 활성화하면 1분마다 지표를 수집한다.
-대시보드에서 InstanceId로 검색하여 수집된 지표를 확인할 수 있다.

./cloudwatch1.png

CPUUtilization, NetworkIn, NetworkOut과 같은 기본적인 지표를 제공하고, 메모리, 디스크 공간과 같은 지표를 확인하려면 사용자 지정 지표를 설정해야 한다.

CloudWatch Agent 설치

CloudWatch Agent 사용자 지정 지표와 로그를 수집할 수 있다.

IAM 역할 설정

기본적으로 EC2 인스턴스가 CloudWatchAgentServerPolicy에 대한 권한이 있어야 한다.
-IAM → 역할에서 역할 생성을 클릭한다.

./cloudwatch2.png

CloudWatchAgentServerPolicy 권한 정책을 선택하고, 적당한 역할 이름을 입력해서 역할을 생성한다.

./cloudwatch3.png

EC2 인스턴스 목록으로 들어가서, CloudWatch Agent를 설치할 EC2 인스턴스를 클릭한다.
-작업 → 보안 → IAM 역할 수정에서 이전에 생성한 역할을 지정한다.

./cloudwatch4.png

설치

환경은 다음과 같다.

OS: ubuntu 22.04
-인스턴스 유형: t4g.small (ARM64)

아래 명령어를 입력하여 설치한다.

wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/arm64/latest/amazon-cloudwatch-agent.deb
sudo dpkg -i -E ./amazon-cloudwatch-agent.deb

사용 설명서에 각 인스턴스 유형마다 다운로드 링크가 자세하게 안내되어 있다.

Wizard

CloudWatch Wizard를 사용하면 간단하게 설정 파일 생성할 수 있다.
-로그를 수집하도록 설정하는 경우 Wizard 실행 명령어 입력 전 log 파일의 절대 경로를 복사해두는 것이 좋다.
-아래의 명령어를 입력하여 Wizard를 실행할 수 있다.

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard

설정을 진행하다 보면 설정 파일이 어떻게 구성될지 확인할 수 있다.
-로그를 추가할 것이냐고 물어보는 입력창이 나오면 준비해뒀던 로그 파일의 절대 경로를 입력한다.

./cloudwatch5.png

중간에 SSM parameter store에 설정 파일을 저장할 것이냐고 물어보는 창이 나온다.

Do you want to store the config in the SSM parameter store?
1. yes
2. no

추가적으로 설정하지 않는 경우 2번을 선택한다.
-Parameter Store 관리에 대한 내용은 다음의 문서를 참고하면 좋을 거 같다.
-설정이 완료되면 /opt/aws/amazon-cloudwatch-agent/bin/config.json 에 설정에 대한 내용이 저장된다.

설정 파일 적용

아래의 명령어를 입력하여 설정파일을 적용할 수 있다.
-file 뒤에는 설정 파일에 대한 절대경로(아래 명령어 기준 기본 생성 위치)를 입력하면 된다.

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json

types.db: no such file or directory 에러

다음과 같은 에러가 발생한다면 types.db 파일을 생성해서 문제를 해결할 수 있다.

Error running agent: Error loading config file /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.toml: error parsing socket_listener, open /usr/share/collectd/types.db: no such file or directory

types.db 파일 생성

sudo mkdir /usr/share/collectd
sudo touch /usr/share/collectd/types.db

지표 확인

CloudWatch Metrics에 가보면 CWAgent라는 네임스페이스가 추가된 것을 볼 수 있다.

./cloudwatch6.png

다음과 같이 설정 파일에 네임스페이스를 추가하여 지표에 대한 네임스페이스를 변경할 수 있다.

{
"metrics": {
"namespace": "2023-hello-world",
......
},
}

로그

CloudWatch → 로그 그룹으로 가면 Wizard로 추가한 로그를 확인할 수 있다.

./cloudwatch7.png

참고 자료

CloudWatch란 무엇입니까?
-Amazon CloudWatch 요금
-Linux 인스턴스 지표
-서버에 CloudWatch 에이전트 설치 및 실행
-CloudWatch Agent를 Parameter Store에서 관리해 보기
-CloudWatch에이전트 구성 파일

- - +

· 약 4분

회고

지난 8주는 레벨 1, 2 때보다 5배 정도 빠르게 지나간 것 같은 느낌이 들었다.
+레벨 3에는 기술적인 부분에서도, 기술 외적인 부분에서도 부족함이 많이 보였던 것 같다.
+부족한 부분을 알았기에, 앞으로 더욱 성장할 수 있을 것 같다.
+내가 부족했던 부분을 팀원들이 잘 보충해 줘서 든든했다.

아쉬운 점

문서화

개인적으로는 기술 외적으로 학습한 부분을 잘 정리하지 못했다.
+프로젝트를 진행하면서 내가 한 부분을 조금 더 꼼꼼하게, 이해하기 쉽게 문서화를 했더라면 팀원들에게 더욱 도움이 되었을 텐데 이 부분에 시간을 조금 더 투자하지 못했던 부분에서 아쉬움이 많이 들었다.
+방학 기간 동안 문서화를 하지 못했던 부분을 개인 블로그 올리면서 조금 더 채워보려고 한다.

내가 못하는 부분이라면 시간을 들이자

잘 못하는 부분이라면 시간을 들여서라도 중간은 가도록 해야겠다는 생각이 많이 들었다.
+말을 하기 전에 정리해서 의견을 내는 것, 발표 준비, 감정 조절 등등 +못하는 부분을 인지하고, 개선하자.

컴포트 존 벗어나기

조금 더 도전적으로 목표를 잡았으면 좋았을 것 같다.
+매번 근거를 가지고 기술을 도입하고, 코드를 작성하려고 노력했다.
+하지만 지속적으로 개선하려고 하는 부분이 다소 부족했다.

좋았던 점

좋았던 점도 문서화

팀 블로그도 먼저 도입하자고 제안하고, 내가 했던 부분은 문서화를 꽤 많이 해서 팀원들과 공유할 수 있었다.
+백엔드 크루 4명이서 같이 한 부분에 대해서는 기능 구현한다고 문서화가 조금 미흡해서 보충을 해야겠다.

내가 디자인한 트립드로우 로고

트립드로우 로고를 만들었다.
+팀원들이 대표 색상(파란색)을 정해줬고, 주말 동안 신나게 로고 디자인을 했던 것 같다.
+아래의 D 부분은 유튜브 강의 들으면서 직접 만들어서 뿌듯하다.

기술 선택의 이유

기술의 학습 비용, 현재 구조에 적합한지, 실제 가지고 있는 리소스를 고려해서 기술 선택을 하고, 도입했던 부분이 좋았다.
+100% 좋은 선택일 순 없지만, 그래도 선택에 대한 근거가 존재한다면 확률을 높혀주는 것 같다.

마치며

플레이스토어에 앱이 올라가 있는 거 너무 신기하다.
+안드로이드 브레멘 음악대(멧돼지, 수달, 핑구), 그리고 백엔드 팀원들(체인저, 후추, 리오) 너무 고생이 많았다.

+ + \ No newline at end of file diff --git a/page/6.html b/page/6.html index d179a442b..be34ecc1c 100644 --- a/page/6.html +++ b/page/6.html @@ -13,39 +13,32 @@ - - + +
-

· 약 12분

이전 글

경로 이미지 생성하기 - 기술 선택
-경로 이미지 생성하기 - 구현

개요

현재 여행을 마치는 경우, 감상을 생성하는 경우 이미지 생성 요청이 이루어진다.
-경로 이미지 생성의 경우 위치 정보의 개수에 정비례하여 생성 시간이 증가한다.
-따라서 비동기로 이미지 생성 요청을 처리하여 사용자의 경험을 개선시킬 수 있다고 생각했다.

주기능의 응답속도 개선

여행 종료와 감상 생성이 주기능이고, 이미지 생성 기능은 부기능이다.
-하지만 현재 여행 종료와 감상 생성의 응답 속도가 경로 이미지 생성 시간에 영향을 받고 있다.
-경로 이미지 생성은 비동기 처리하여도 애플리케이션 사용에 문제가 되지 않는다.
-소요 시간이 1초 이상 걸리는 경우가 존재하기에 이미지 생성을 비동기 처리하고 여행 종료와 감상 생성 기능의 응답 시간을 개선하는 것이 더 중요하다.

확장성 대비

현재 10분 간격으로 위치 정보를 서버에 저장하고 있다.
-조금 더 짧은 간격으로 위치 정보를 그리는 경우 하나의 여행에 많은 위치 정보가 저장될 수밖에 없고 따라서 경로 이미지 생성에 걸리는 시간이 더 길어질 수 있다.
-따라서 추후에 더 짧은 간격으로 위치 정보를 저장하는 경우를 대비하여 이미지 생성은 비동기로 처리하는 것이 합당하다.

비동기 처리

@Async를 사용하면 간단하게 메서드를 비동기로 동작하도록 만들 수 있다.

비동기 설정

사용하기 전에 설정 파일을 하나 만들어서 EnableAsync 설정을 해야한다.
-해당 설정을 적용하면 비동기적으로 실행하려는 메서드에 @Async 애너테이션을 붙여주기만 하면 비동기로 동작한다.

AsyncConfig
@EnableAsync
@Configuration
public class AsyncConfig {
}

스프링 부트를 사용하지 않는 경우 기본적으로 비동기 처리를 할 때 매번 새로운 스레드를 생성하기 때문에 스레드 풀 설정을 따로 해줘야 한다. 하지만 스프링 부트를 사용하는 경우 ThreadPoolTaskExecutor를 따로 설정하지 않아도 기본적으로 스프링 부트가 생성을 도와준다.

In the absence of an Executor bean in the context, Spring Boot auto-configures a ThreadPoolTaskExecutor with sensible defaults that can be automatically associated to asynchronous task execution (@EnableAsync) and Spring MVC asynchronous request processing. -7.7. Task Execution and Scheduling, Spring Boot Docs

@Async 적용

이미지 생성기에 Async 애너테이션을 붙여 비동기로 동작하도록 한다.

RouteImageGenerator
@Async
public void generate(
List<Double> latitudes,
List<Double> longitudes,
List<Double> pointedLatitudes,
List<Double> pointedLongitudes,
Long tripId
) {
// 이미지 생성
RouteImageDrawer routeImageDrawer = RouteImageDrawer.from(IMAGE_SIZE);
Coordinates coordinates = Coordinates.of(latitudes, longitudes);
Coordinates pointedCoordinates = Coordinates.of(pointedLatitudes, pointedLongitudes);
drawImage(coordinates, routeImageDrawer, pointedCoordinates);

// 이미지 저장
String imageName = routeImageUploader.upload(routeImageDrawer.bufferedImage());

// 자원 할당 해제
routeImageDrawer.dispose();

// 데이터베이스 값 변경
Trip trip = tripRepository.findById(tripId)
.orElseThrow();
trip.changeRouteImageUrl(imageUrl);
tripRepository.save(trip);
}

비동기 처리시 문제점

현재 이미지 생성을 하고 저장 후, 저장 경로를 DB에 반영해야 한다.
-따라서 패키지 간 순환 참조 형태가 되며 의존성 방향이 문제가 생긴다.

이를 해결하기 위해서는 인터페이스를 사용하는 방법과 이벤트를 사용하는 방법이 있다.
-인터페이스를 사용한다면 다음과 같은 구조가 된다.

패키지 간 의존성은 해결되었지만, 이미지 경로 저장을 위해 tripId를 받아야하는 등의 논리적인 의존성은 아직 해결되지 않았다.
-따라서 이벤트를 사용하기로 했다.

이벤트 사용

스프링의 애플리케이션 이벤트를 사용하면 비즈니스 로직의 비관심사(ex. 경로 이미지 생성)을 효율적인 방법으로 처리할 수 있다.

이벤트 발행

이벤트를 사용하려면 먼저 이벤트를 발행해야 한다.
-스프링에서는 ApplicationEventPublisher 인터페이스를 사용하여 이벤트를 발행할 수 있다.
-해당 인터페이스는 내부적으로 ApplicationContext가 구현하여 이벤트를 발행한다.

TripService & TripUpdateEvent
public void updateTripById(LoginUser loginUser, Long tripId, TripUpdateRequest tripUpdateRequest) {
...

// 이벤트 발행
applicationEventPublisher.publishEvent(new TripUpdateEvent(trip.id()));
}

public record TripUpdateEvent(Long tripId) {
}

이벤트를 발행할 때 발행하는 이벤트명이 중요하다.
-이벤트를 구독하는 도메인의 행위를 담고 있는 이벤트를 발행(ex. RouteImageGenerateEvent)한다면 논리적인 의존 관계가 남아있기에 이벤트를 적절히 사용했다고 보기 어렵다.
-발행하는 이벤트명은 주기능이 어떤 행위(ex. TripUpdateEvent)를 했는지에 대한 정보가 담겨있는 이벤트명으로 발행하는 것이 중요하다.

이벤트 구독

이벤트를 구독하여 실행하는 메서드는 비동기로 처리하기 위하여 @Async 애너테이션을 적용했다.
-이벤트의 구독은 여행이 정상적으로 종료될 때 여행에 대한 정보를 가지고 경로 이미지를 생성하기 위해 @TransactionalEventListener를 사용했다.

TransactionPhase 설정

TransactionPhase을 사용하여 트랜잭션 이벤트를 어떤 단계에서 수신하고 처리할지를 지정할 수 있다.

AFTER_COMMIT(기본값): 트랜잭션이 정상적으로 커밋 되는 경우 이벤트 실행
-AFTER_ROLLBACK: 트랜잭션이 롤백되는 경우 이벤트 실행
-AFTER_COMPLETION: 트랜잭션이 커밋 또는 롤백 되었을 경우 이벤트 실행
-BEFORE_COMMIT: 트랜잭션이 커밋 되기 전 이벤트 실행

이미지 생성의 경우 트랜잭션에서 제외하기 위해 @Transactional 애너테이션을 사용하지 않았다.

TripUpdateEventHandler
@Component
public class TripUpdateEventHandler {

private final RouteImageGenerator routeImageGenerator;
private final TripRepository tripRepository;

public TripUpdateEventHandler(RouteImageGenerator routeImageGenerator, TripRepository tripRepository) {
this.routeImageGenerator = routeImageGenerator;
this.tripRepository = tripRepository;
}

@Async
@TransactionalEventListener(phase = AFTER_COMMIT)
public void handle(TripUpdateEvent tripUpdateEvent) {
Trip trip = tripRepository.getTripWithPoints(tripUpdateEvent.tripId());

String imageUrl = routeImageGenerator.generate(
trip.getLatitudes(),
trip.getLongitudes(),
trip.getPointedLatitudes(),
trip.getPointedLongitudes()
);

trip.changeRouteImageUrl(imageUrl);
tripRepository.save(trip);
}
}

이벤트를 사용함으로써 패키지 간 순환 참조 문제가 다음과 같이 해결되었다.
-또한 주기능과 부기능을 분리함으로써 경로 이미지 생성 기능에 대한 전체적인 결합도를 낮추었다.

테스트

비동기로 동작하는 메서드를 테스트하기 위해서는 아래와 같은 방법이 있다.

@ContextConfiguration(classes = TestSyncConfig.class)
@SpringBootTest
public class TripUpdateEventHandlerIntegrationTest {

...

@Test
void 여행수정_이벤트를_발생시키면_이미지를_생성_요청을_한다() {
// given
TripUpdateEvent tripUpdateEvent = new TripUpdateEvent(1L);
given(tripRepository.getTripWithPoints(tripUpdateEvent.tripId()))
.willReturn(여행());

// when
transactionTemplate.executeWithoutResult(action -> applicationEventPublisher.publishEvent(tripUpdateEvent));

// then
then(routeImageGenerator)
.should(times(1))
.generate(any(), any(), any(), any());
}
}

처음에는 테스트에서만 동기로 설정 후 검증하려고 했다.
-통합 테스트에선 트랜잭션이 정상 종료되었을 때 비동기로 이벤트를 구독하여 이미지 생성 메서드를 호출하는지 검증이 필요했기 때문에 최종적으로 Mockito.timeout 메서드를 사용하여 비동기 메서드가 통과될 때까지 대기하는 방향으로 변경했다.

결과

./time.png

위 응답 시간은 위치 정보 1000개를 기준으로 테스트한 값이다.
-응답 시간에 이미지 생성 시간이 포함되지 않아서 성능이 개선된 것을 볼 수 있다.

참고 자료

7.7. Task Execution and Scheduling, Spring Boot Docs
-Spring Events, Baeldung
-회원시스템 이벤트기반 아키텍처 구축하기

- - +

· 약 6분

CloudWatch

AWS 리소스와 애플리케이션의 지표와 로그에 대한 모니터링을 제공하는 서비스다.
+지표를 감시하여 알림을 보내는 기능도 제공한다.
+프리티어를 사용하지 않는 경우 대시보드당 3$/M 의 비용이 청구되고, 지표나 로그의 양에 따라 비용이 추가적으로 청구된다.
+요금 정보에 대한 자세한 정보는 다음 링크에서 확인할 수 있다.

CloudWatch Metrics

기본적으로 5분마다 지표에 대한 정보가 수집된다.
+세부 모니터링(Detailed Monitoring)을 활성화하면 1분마다 지표를 수집한다.
+대시보드에서 InstanceId로 검색하여 수집된 지표를 확인할 수 있다.

./cloudwatch1.png

CPUUtilization, NetworkIn, NetworkOut과 같은 기본적인 지표를 제공하고, 메모리, 디스크 공간과 같은 지표를 확인하려면 사용자 지정 지표를 설정해야 한다.

CloudWatch Agent 설치

CloudWatch Agent 사용자 지정 지표와 로그를 수집할 수 있다.

IAM 역할 설정

기본적으로 EC2 인스턴스가 CloudWatchAgentServerPolicy에 대한 권한이 있어야 한다.
+IAM → 역할에서 역할 생성을 클릭한다.

./cloudwatch2.png

CloudWatchAgentServerPolicy 권한 정책을 선택하고, 적당한 역할 이름을 입력해서 역할을 생성한다.

./cloudwatch3.png

EC2 인스턴스 목록으로 들어가서, CloudWatch Agent를 설치할 EC2 인스턴스를 클릭한다.
+작업 → 보안 → IAM 역할 수정에서 이전에 생성한 역할을 지정한다.

./cloudwatch4.png

설치

환경은 다음과 같다.

OS: ubuntu 22.04
+인스턴스 유형: t4g.small (ARM64)

아래 명령어를 입력하여 설치한다.

wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/arm64/latest/amazon-cloudwatch-agent.deb
sudo dpkg -i -E ./amazon-cloudwatch-agent.deb

사용 설명서에 각 인스턴스 유형마다 다운로드 링크가 자세하게 안내되어 있다.

Wizard

CloudWatch Wizard를 사용하면 간단하게 설정 파일 생성할 수 있다.
+로그를 수집하도록 설정하는 경우 Wizard 실행 명령어 입력 전 log 파일의 절대 경로를 복사해두는 것이 좋다.
+아래의 명령어를 입력하여 Wizard를 실행할 수 있다.

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard

설정을 진행하다 보면 설정 파일이 어떻게 구성될지 확인할 수 있다.
+로그를 추가할 것이냐고 물어보는 입력창이 나오면 준비해뒀던 로그 파일의 절대 경로를 입력한다.

./cloudwatch5.png

중간에 SSM parameter store에 설정 파일을 저장할 것이냐고 물어보는 창이 나온다.

Do you want to store the config in the SSM parameter store?
1. yes
2. no

추가적으로 설정하지 않는 경우 2번을 선택한다.
+Parameter Store 관리에 대한 내용은 다음의 문서를 참고하면 좋을 거 같다.
+설정이 완료되면 /opt/aws/amazon-cloudwatch-agent/bin/config.json 에 설정에 대한 내용이 저장된다.

설정 파일 적용

아래의 명령어를 입력하여 설정파일을 적용할 수 있다.
+file 뒤에는 설정 파일에 대한 절대경로(아래 명령어 기준 기본 생성 위치)를 입력하면 된다.

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json

types.db: no such file or directory 에러

다음과 같은 에러가 발생한다면 types.db 파일을 생성해서 문제를 해결할 수 있다.

Error running agent: Error loading config file /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.toml: error parsing socket_listener, open /usr/share/collectd/types.db: no such file or directory

types.db 파일 생성

sudo mkdir /usr/share/collectd
sudo touch /usr/share/collectd/types.db

지표 확인

CloudWatch Metrics에 가보면 CWAgent라는 네임스페이스가 추가된 것을 볼 수 있다.

./cloudwatch6.png

다음과 같이 설정 파일에 네임스페이스를 추가하여 지표에 대한 네임스페이스를 변경할 수 있다.

{
"metrics": {
"namespace": "2023-hello-world",
......
},
}

로그

CloudWatch → 로그 그룹으로 가면 Wizard로 추가한 로그를 확인할 수 있다.

./cloudwatch7.png

참고 자료

CloudWatch란 무엇입니까?
+Amazon CloudWatch 요금
+Linux 인스턴스 지표
+서버에 CloudWatch 에이전트 설치 및 실행
+CloudWatch Agent를 Parameter Store에서 관리해 보기
+CloudWatch에이전트 구성 파일

+ + \ No newline at end of file diff --git a/page/7.html b/page/7.html index eb260e07e..84b0795d2 100644 --- a/page/7.html +++ b/page/7.html @@ -13,23 +13,39 @@ - - + +
-

· 약 12분

개요

여행에 대한 경로를 보여주기 위해 경로 이미지를 생성하는 기능을 추가했다.
-경로 이미지에 대한 요구사항 및 기술 선택에 대한 내용은 링크에 있다.

구현 결과

./result.png

예시 데이터는 다음과 같다.
-서울역(점) → 신사역 → 노량진역 → 홍대입구역 → 종로3가역 → 옥수역 → 구로역(점) → 신림역 → 발산역

예시 데이터
List<Double> x = List.of(
126.97094933811682, 127.02154822802501, 126.94218991864345, 126.92402556641424,
126.99265358592287, 127.01779856076462, 126.88474839801178, 126.92900751277035, 126.83930056313639
);
List<Double> y = List.of(
37.55302829553499, 37.51619698970427, 37.51294119442773, 37.5565933969331,
37.57034879708931, 37.54027238225762, 37.50129417536773, 37.48258811529137, 37.557607696911184
);
List<Double> xPoints = List.of(126.97094933811682, 126.88474839801178);
List<Double> yPoints = List.of(37.55302829553499, 37.50129417536773);

IMAGE_SIZE & ROUTE_SIZE

RouteImageGenerator.java
private static final int IMAGE_SIZE = 800;
private static final int ROUTE_SIZE = 600;

코드를 보면 IMAGE_SIZE와 ROUTE_SIZE가 있다.
-IMAGE_SIZE는 말 그대로 이미지의 width와 height를 의미한다.
-ROUTE_SIZE의 경우 상하좌우 100px 만큼의 간격을 위해 존재한다.
-따라서 실제 경로 이미지의 크기는 600 * 600 사이즈로 생성된다.

./600.png

사이즈 변경의 이유

255 255 정도의 작은 사이즈로 이미지를 생성해보려고 했는데, 이미지의 선명도가 좋지 않아 800 800 사이즈로 변경했다.

주요 클래스

요약

클래스명설명특이사항
Coordinate위도, 경도로 이루어진 위치 값좌표를 뜻하지만 여행 도메인에 포함된 Point 클래스와 구분하기 위해 longitude, latitude를 사용하지 않고 x, y 사용
CoordinatesCoordinate의 일급 컬렉션-
Position실제 이미지 생성에 사용할 위치 값Integer 타입의 x, y 사용
PositionsPositions의 일급 컬렉션-
RouteImageDrawer실제 이미지에 경로를 그려주는 클래스 BufferedImage, Graphics2D를 가지고 있음이미지 생성에 필요한 상수가 정의되어 있음
RouteImageUploaderBufferedImage를 받아 서버에 업로드 하는 클래스현재 업로드 위치가 정해지지 않아 일단 기본(프로젝트 루트) 위치에 생성
RouteImageGenerator이미지를 생성하고 업로드하는 서비스여행 종료, 감상 저장시 해당 클래스를 통해 이미지 생성 요청
BufferedImage(AWT)이미지 데이터를 처리하고 조작하는 데 사용왼쪽 상단의 좌표가 (0, 0)
Graphics2D(AWT)선 그리기, 색상 관리 등을 지원하는 클래스 실제 해당 클래스의 draw 메서드를 경로를 그림JDK 1.2 이후에 추가됨, 2D(평면) 그래픽 환경을 지원, bufferedImage.createGraphics 메서드를 통해 생성

의존관계

Coordinates(위도, 경도의 일급 컬렉션)

List<Double> 2개(위도, 경도)인 형태로 관리하는 방법이 있었지만, 위치 점을 여러개 찍는 부분에서 로직이 복잡해 질 것 같아서 Coordinate(x, y)와 일급 컬렉션인 Coordinates로 관리하기로 했다.
-Coordinates 클래스에는 다음 두 개의 인터페이스가 존재한다.

  • calculatePositions: 경로 이미지의 크기를 받아 실제 이미지 생성시 사용될 Positions를 반환
  • indexOf: 다른 Coordinates를 받아 동일한 위치점에 해당하는 인덱스를 반환하는

Positions 계산 로직은 다음과 같다.
-위도, 경도 각각에 대한 부분을 이미지 생성시 필요한 값으로 변환한다.

Coordinates.java
// 호출
// List<Integer> xPositions = toPositions(xValues, maxDifference, routeImageSize);
// List<Integer> yPositions = toPositions(yValues, maxDifference, routeImageSize);

private List<Integer> toPositions(List<Double> values, Double maxDifference, Integer routeImageSize) {
Double minValue = Collections.min(values);
return values.stream()
.map(value -> normalizeCoordinate(value, maxDifference, minValue))
.map(value -> mapToPosition(value, routeImageSize))
.toList();
}

private double normalizeCoordinate(Double coordinate, Double maxDifference, Double minValue) {
return (coordinate - minValue) / maxDifference;
}

private int mapToPosition(Double coordinate, Integer routeImageSize) {
return (int) (coordinate * routeImageSize);
}

위도로 예시든 내용이다.

  1. Collections.min(values) → 위도 리스트의 최소값을 구한다.
  2. normalizeCoordinate → 각각의 위도 값에서 최소값을 빼고 0 ~ 1 사이 값으로 변환 후 위경도의 최대 차이로 나눈다.
  3. mapToPosition → 그래프 크기를 받아 0 ~ 1 사이 값을 실제 이미지를 위한 위치값으로 변환한다.

Positions(실제 이미지 생성에 사용할 위치)

Positions 클래스에는 다음 다섯 개의 인터페이스가 존재한다.

  • align: 이미지 사이즈와 경로 이미지 사이즈를 받아 Position 값들을 중앙 정렬한다.
  • getPositionsByIndexes: 인덱스 리스트를 받아 입력받은 인덱스에 해당하는 값들을 반환한다.
  • size: 크기를 반환한다.
  • xPositions: x 값들을 반환한다.
  • yPositions: y 값들을 반환한다.

중앙 정렬 로직은 다음과 같다.

Positions.java
public Positions align(int imageSize, int routeSize) {
int xOffset = calculateOffset(Position::x, imageSize);
int yOffset = calculateOffset(Position::y, imageSize);

return items.stream()
.map(item -> new Position(item.x() + xOffset, imageSize - (item.y() + yOffset)))
.collect(collectingAndThen(toList(), Positions::new));
}

private int calculateOffset(ToIntFunction<Position> positionToInteger, int imageSize) {
List<Integer> positions = items.stream()
.mapToInt(positionToInteger)
.boxed()
.toList();

int midValue = (Collections.min(positions) + Collections.max(positions)) / 2;
return imageSize / 2 - midValue;
}

상하좌우 여백을 동일하게 주기 위해서 offset 값을 구해서 x, y 값에 각각 더하는 형태로 중앙 정렬을 수행했다.
-BufferedImage를 사용할 때 왼쪽 상단의 좌표 (0, 0) 기준으로 아래로 내려갈수록 y 값이 커지고, 오른쪽으로 갈 수록 x 값이 커진다.

./800.png

따라서 최종적으로 이미지를 생성하기 위한 값을 다음과 같이 구했다.

x 값 → 계산한 offset 그대로 더한다.
-y 값 → imageSize(800)에서 y + offset 값을 뺀다.

RouteImageDrawer(실제 이미지에 경로를 그려주는 클래스)

BufferedImage, Graphics2D를 필드로 가지고 있는 클래스다.
-그림을 그리기 위해 설정한 상수들이 존재한다.

RouteImageDrawer.java
// RGB에 각각 8비트씩 할당한 값을 24비트 트루컬러라 부른다.
// 해당 설정은 24비트 + 8비트(alpha, 투명도)를 추가한 32비트 이미지 타입이다.
// 이를 RGBA라고 부른다.
private static final int IMAGE_TYPE = BufferedImage.TYPE_INT_ARGB;
// 배경 투명색
private static final Color TRANSPARENT = new Color(0, 0, 0, 0);
// 경로를 위한 STROKE
private static final int LINE_STROKE_WIDTH = 7;
private static final Stroke LINE_STROKE = new BasicStroke(LINE_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);
// 위치 점을 위한 STROKE
private static final int POINT_STROKE_WIDTH = 20;
private static final Stroke POINT_STROKE = new BasicStroke(POINT_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);
// 안티앨리어싱 등 화질 개선을 위한 설정
private static final Map<Object, Object> renderingHints = Map.of(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON,
RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY,
RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC
);

RouteImageDrawer 클래스에는 다음 세 개의 인터페이스가 존재한다.

  • drawLine: 선을 그린다.
  • drawPoint: 점을 찍는다.
  • dispose: 자원 할당을 해제한다.

dispose의 경우 내부에서 생성된 graphics2D에 대한 자원 할당을 해제하는 메서드인 graphics2D.dispose를 호출한다.

이미지 생성 Flow

1. 이미지 생성 준비

2. 선 그리기 요청

3. 위치 점 그리기 요청

4. 업로드 요청

전체 Flow

- - +

· 약 12분

이전 글

경로 이미지 생성하기 - 기술 선택
+경로 이미지 생성하기 - 구현

개요

현재 여행을 마치는 경우, 감상을 생성하는 경우 이미지 생성 요청이 이루어진다.
+경로 이미지 생성의 경우 위치 정보의 개수에 정비례하여 생성 시간이 증가한다.
+따라서 비동기로 이미지 생성 요청을 처리하여 사용자의 경험을 개선시킬 수 있다고 생각했다.

주기능의 응답속도 개선

여행 종료와 감상 생성이 주기능이고, 이미지 생성 기능은 부기능이다.
+하지만 현재 여행 종료와 감상 생성의 응답 속도가 경로 이미지 생성 시간에 영향을 받고 있다.
+경로 이미지 생성은 비동기 처리하여도 애플리케이션 사용에 문제가 되지 않는다.
+소요 시간이 1초 이상 걸리는 경우가 존재하기에 이미지 생성을 비동기 처리하고 여행 종료와 감상 생성 기능의 응답 시간을 개선하는 것이 더 중요하다.

확장성 대비

현재 10분 간격으로 위치 정보를 서버에 저장하고 있다.
+조금 더 짧은 간격으로 위치 정보를 그리는 경우 하나의 여행에 많은 위치 정보가 저장될 수밖에 없고 따라서 경로 이미지 생성에 걸리는 시간이 더 길어질 수 있다.
+따라서 추후에 더 짧은 간격으로 위치 정보를 저장하는 경우를 대비하여 이미지 생성은 비동기로 처리하는 것이 합당하다.

비동기 처리

@Async를 사용하면 간단하게 메서드를 비동기로 동작하도록 만들 수 있다.

비동기 설정

사용하기 전에 설정 파일을 하나 만들어서 EnableAsync 설정을 해야한다.
+해당 설정을 적용하면 비동기적으로 실행하려는 메서드에 @Async 애너테이션을 붙여주기만 하면 비동기로 동작한다.

AsyncConfig
@EnableAsync
@Configuration
public class AsyncConfig {
}

스프링 부트를 사용하지 않는 경우 기본적으로 비동기 처리를 할 때 매번 새로운 스레드를 생성하기 때문에 스레드 풀 설정을 따로 해줘야 한다. 하지만 스프링 부트를 사용하는 경우 ThreadPoolTaskExecutor를 따로 설정하지 않아도 기본적으로 스프링 부트가 생성을 도와준다.

In the absence of an Executor bean in the context, Spring Boot auto-configures a ThreadPoolTaskExecutor with sensible defaults that can be automatically associated to asynchronous task execution (@EnableAsync) and Spring MVC asynchronous request processing. +7.7. Task Execution and Scheduling, Spring Boot Docs

@Async 적용

이미지 생성기에 Async 애너테이션을 붙여 비동기로 동작하도록 한다.

RouteImageGenerator
@Async
public void generate(
List<Double> latitudes,
List<Double> longitudes,
List<Double> pointedLatitudes,
List<Double> pointedLongitudes,
Long tripId
) {
// 이미지 생성
RouteImageDrawer routeImageDrawer = RouteImageDrawer.from(IMAGE_SIZE);
Coordinates coordinates = Coordinates.of(latitudes, longitudes);
Coordinates pointedCoordinates = Coordinates.of(pointedLatitudes, pointedLongitudes);
drawImage(coordinates, routeImageDrawer, pointedCoordinates);

// 이미지 저장
String imageName = routeImageUploader.upload(routeImageDrawer.bufferedImage());

// 자원 할당 해제
routeImageDrawer.dispose();

// 데이터베이스 값 변경
Trip trip = tripRepository.findById(tripId)
.orElseThrow();
trip.changeRouteImageUrl(imageUrl);
tripRepository.save(trip);
}

비동기 처리시 문제점

현재 이미지 생성을 하고 저장 후, 저장 경로를 DB에 반영해야 한다.
+따라서 패키지 간 순환 참조 형태가 되며 의존성 방향이 문제가 생긴다.

이를 해결하기 위해서는 인터페이스를 사용하는 방법과 이벤트를 사용하는 방법이 있다.
+인터페이스를 사용한다면 다음과 같은 구조가 된다.

패키지 간 의존성은 해결되었지만, 이미지 경로 저장을 위해 tripId를 받아야하는 등의 논리적인 의존성은 아직 해결되지 않았다.
+따라서 이벤트를 사용하기로 했다.

이벤트 사용

스프링의 애플리케이션 이벤트를 사용하면 비즈니스 로직의 비관심사(ex. 경로 이미지 생성)을 효율적인 방법으로 처리할 수 있다.

이벤트 발행

이벤트를 사용하려면 먼저 이벤트를 발행해야 한다.
+스프링에서는 ApplicationEventPublisher 인터페이스를 사용하여 이벤트를 발행할 수 있다.
+해당 인터페이스는 내부적으로 ApplicationContext가 구현하여 이벤트를 발행한다.

TripService & TripUpdateEvent
public void updateTripById(LoginUser loginUser, Long tripId, TripUpdateRequest tripUpdateRequest) {
...

// 이벤트 발행
applicationEventPublisher.publishEvent(new TripUpdateEvent(trip.id()));
}

public record TripUpdateEvent(Long tripId) {
}

이벤트를 발행할 때 발행하는 이벤트명이 중요하다.
+이벤트를 구독하는 도메인의 행위를 담고 있는 이벤트를 발행(ex. RouteImageGenerateEvent)한다면 논리적인 의존 관계가 남아있기에 이벤트를 적절히 사용했다고 보기 어렵다.
+발행하는 이벤트명은 주기능이 어떤 행위(ex. TripUpdateEvent)를 했는지에 대한 정보가 담겨있는 이벤트명으로 발행하는 것이 중요하다.

이벤트 구독

이벤트를 구독하여 실행하는 메서드는 비동기로 처리하기 위하여 @Async 애너테이션을 적용했다.
+이벤트의 구독은 여행이 정상적으로 종료될 때 여행에 대한 정보를 가지고 경로 이미지를 생성하기 위해 @TransactionalEventListener를 사용했다.

TransactionPhase 설정

TransactionPhase을 사용하여 트랜잭션 이벤트를 어떤 단계에서 수신하고 처리할지를 지정할 수 있다.

AFTER_COMMIT(기본값): 트랜잭션이 정상적으로 커밋 되는 경우 이벤트 실행
+AFTER_ROLLBACK: 트랜잭션이 롤백되는 경우 이벤트 실행
+AFTER_COMPLETION: 트랜잭션이 커밋 또는 롤백 되었을 경우 이벤트 실행
+BEFORE_COMMIT: 트랜잭션이 커밋 되기 전 이벤트 실행

이미지 생성의 경우 트랜잭션에서 제외하기 위해 @Transactional 애너테이션을 사용하지 않았다.

TripUpdateEventHandler
@Component
public class TripUpdateEventHandler {

private final RouteImageGenerator routeImageGenerator;
private final TripRepository tripRepository;

public TripUpdateEventHandler(RouteImageGenerator routeImageGenerator, TripRepository tripRepository) {
this.routeImageGenerator = routeImageGenerator;
this.tripRepository = tripRepository;
}

@Async
@TransactionalEventListener(phase = AFTER_COMMIT)
public void handle(TripUpdateEvent tripUpdateEvent) {
Trip trip = tripRepository.getTripWithPoints(tripUpdateEvent.tripId());

String imageUrl = routeImageGenerator.generate(
trip.getLatitudes(),
trip.getLongitudes(),
trip.getPointedLatitudes(),
trip.getPointedLongitudes()
);

trip.changeRouteImageUrl(imageUrl);
tripRepository.save(trip);
}
}

이벤트를 사용함으로써 패키지 간 순환 참조 문제가 다음과 같이 해결되었다.
+또한 주기능과 부기능을 분리함으로써 경로 이미지 생성 기능에 대한 전체적인 결합도를 낮추었다.

테스트

비동기로 동작하는 메서드를 테스트하기 위해서는 아래와 같은 방법이 있다.

@ContextConfiguration(classes = TestSyncConfig.class)
@SpringBootTest
public class TripUpdateEventHandlerIntegrationTest {

...

@Test
void 여행수정_이벤트를_발생시키면_이미지를_생성_요청을_한다() {
// given
TripUpdateEvent tripUpdateEvent = new TripUpdateEvent(1L);
given(tripRepository.getTripWithPoints(tripUpdateEvent.tripId()))
.willReturn(여행());

// when
transactionTemplate.executeWithoutResult(action -> applicationEventPublisher.publishEvent(tripUpdateEvent));

// then
then(routeImageGenerator)
.should(times(1))
.generate(any(), any(), any(), any());
}
}

처음에는 테스트에서만 동기로 설정 후 검증하려고 했다.
+통합 테스트에선 트랜잭션이 정상 종료되었을 때 비동기로 이벤트를 구독하여 이미지 생성 메서드를 호출하는지 검증이 필요했기 때문에 최종적으로 Mockito.timeout 메서드를 사용하여 비동기 메서드가 통과될 때까지 대기하는 방향으로 변경했다.

결과

./time.png

위 응답 시간은 위치 정보 1000개를 기준으로 테스트한 값이다.
+응답 시간에 이미지 생성 시간이 포함되지 않아서 성능이 개선된 것을 볼 수 있다.

참고 자료

7.7. Task Execution and Scheduling, Spring Boot Docs
+Spring Events, Baeldung
+회원시스템 이벤트기반 아키텍처 구축하기

+ + \ No newline at end of file diff --git a/page/8.html b/page/8.html index c14eb5a40..2cb1accef 100644 --- a/page/8.html +++ b/page/8.html @@ -13,33 +13,23 @@ - - + +
-

· 약 7분

개요

이전에 기술 구현 가능 여부를 조사하면서 파이썬을 사용한 내용을 정리한 내용이다.

사용 기술

언어: Python 3.10
-이미지 생성: matplotlib
-서비스: AWS Lambda, AWS API Gateway
-이미지 저장 및 URL: AWS S3, AWS CloudFront

플로우는 다음과 같다.

요구사항

./route.png

우측 상단의 경로 이미지를 생성하려고 한다.
-경로 이미지 생성에 대한 요구사항은 다음과 같다.

  • 위도, 경도로 이루어진 배열을 입력받는다.
  • 이미지 생성
  • 선과 점 표현
  • 투명한 배경색
  • 위경도 차이가 크든 작든 제공하는 이미지 내에 경로가 다 포함되어 있어야 한다.

이미지 출력 방식

  1. 위경도를 처리한 값으로 직접 경로를 그린 다음 이미지 형태로 저장
  2. 플롯을 그려주는 라이브러리 사용하여 이미지 형태로 저장

이미지 출력 방식의 경우 1번과 2번을 고민했었다.
-파이썬으로는 플롯을 그려주는 라이브러리인 matplotlib을 사용했다.

로컬에서 기능 구현

import time

import matplotlib.pyplot as plt


def draw(point):
start = time.time()
x, y = zip(*point)
pixel_x, pixel_y = convert_to_pixel_values(x, y)
draw_lines(pixel_x, pixel_y)
end = time.time()
print(end - start)

def convert_to_pixel_values(x, y):
max_diff = max(max(x) - min(x), max(y) - min(y))
return scale_to_pixel_values(x, max_diff), scale_to_pixel_values(y, max_diff)


def scale_to_pixel_values(points, max_diff):
min_value = min(points)
scaled_coordinates = [(p - min_value) / max_diff for p in points]
return scaled_coordinates


def draw_lines(x, y):
figure = plt.gcf()
figure.set_size_inches(5, 5)
plt.plot(x, y, c = 'w',linewidth=5)
plt.scatter(x[3],y[3], c = 'w', s = 125)
plt.axis('off')
plt.savefig('name.png', transparent=True, format='png')

point = [
[126.96352960597338, 37.590841000217125],
[126.96987292787792, 37.58435564234159],
[126.98128481452298, 37.58594375113966],
[126.99360339342958, 37.58248524741927],
[126.99867565340067, 37.56778118088622],
[127.001935378366117, 37.55985240444085],
[126.9831048919687, 37.548030119488665],
[126.97189273528845, 37.5119879225856],
[127.02689859997221, 37.48488593333883]
]

draw(point)

생성 결과는 아래와 같다. (예시를 위해 검은색으로 출력)

./routeImage.png

AWS Lambda

썸네일 생성 서버를 따로 두기는 기능 대비 비용이 너무 클 것이라고 생각했다.
-따라서 서버리스로 파일을 처리했다.
-추가로 s3 접근은 boto3를 사용했다.

람다 S3 접근을 위한 IAM 생성

AmazonS3FullAccess, AmazonS3ObjectLambdaExecutionRolePolicy 두가지를 추가해서 Lambda 전용 역할을 만들어 사용했다.

람다 배포용 코드

기술 구현 가능 여부를 확인할 땐 위치 점을 찍는 기능을 람다에 배포하지 않았다.


import io
import uuid

import boto3
import matplotlib.pyplot as plt

PIXEL = 255
BUCKET_NAME = 'image-plot'
S3 = 's3'

def lambda_handler(event, context):
x = event['x']
y = event['y']
image_name = str(uuid.uuid4())

img_data = draw(x, y)
s3 = boto3.client(S3)
s3.put_object(Body=img_data.getvalue(), ContentType='image/png', Bucket=BUCKET_NAME, Key=image_name)
url = f'https://{BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com/{image_name}'

return {
'statusCode': 200,
'body': url
}

def draw(x, y):
pixel_x, pixel_y = convert_to_pixel_values(x, y)
img_data = draw_lines(pixel_x, pixel_y)
plt.close()
return img_data

def convert_to_pixel_values(x, y):
max_diff = max(max(x) - min(x), max(y) - min(y))
return scale_to_pixel_values(x, max_diff), scale_to_pixel_values(y, max_diff)

def scale_to_pixel_values(points, max_diff):
min_value = min(points)
scaled_coordinates = [(p - min_value) / max_diff for p in points]
pixel_values = [int(p * PIXEL) for p in scaled_coordinates]
return pixel_values

def draw_lines(x, y):
plt.plot(x, y, 'k-', linewidth=10)
plt.axis('off')
img_data = io.BytesIO()
plt.savefig(img_data, transparent=True, format='png')
img_data.seek(0)
return img_data

Layer 추가를 위한 zip 파일 생성

matplotlib의 경우 외부 라이브러리기 때문에 따로 Layer를 추가해야 한다.
-zip 파일을 만들어서 업로드해야한다.
-이때 python의 Lambda 런타임에 대한 계층 경로는 python이다.
-따라서 압축한 zip 파일은 다음과 같은 구조를 띄어야 한다.

pillow.zip
│ python/PIL
└ python/Pillow-5.3.0.dist-info

Ubuntu 기준 다음 명령어를 입력하여 생성을 진행했다.

sudo apt update
sudo apt install zip
sudo apt install python3-pip

mkdir python
pip3 install matplotlib -t python # pip3 install 설치할_패키지 -t 설치_경로
zip -r my_layer.zip python # zip -r 압축_파일명 압축_파일이_존재하는_경로

No module named 'numpy.core._multiarray_umath' 에러

Layer 추가 후 람다 실행 시 발생한 에러였다.
-처음에 mac에서 zip 파일을 생성해서 업로드했는데 해당 문제가 발생했다.
-이는 lambda가 돌아가는 동일한 환경에서 layer를 위한 zip 파일을 만들지 않아서 발생하는 문제다.
-간단하게 ec2 인스턴스를 하나 만들어서 따로 Layer를 생성하면 문제가 발생하지 않는다.

적정기술에 대한 생각

프로젝트에 Lambda와 Python을 사용하려고 했지만 아쉽게도 반려당했다.
-AWS Lambda를 사용하는 것은 인스턴스에 해당 코드를 배포하는 것보다 더 효율적인 방법일 수 있다.
-하지만 현재 프로젝트에서 가용 가능한 자원, 기술의 난이도, 사용하는 팀원을 고려한다면 Lambda는 적정기술이 아닐 수 있다.
-따라서 해당 이미지 생성기를 어떻게 적용할지 조금 더 고려를 해야 될 것으로 보인다.

최종적으로 Java AWT를 사용하기로 결정했다.

참고 자료

AWS Lambda
-Lambda Layer
-Python Lambda 함수에 대한 .zip 파일 아카이브 작업
-No module named 'numpy.core._multiarray_umath'
-사례별로 알아본 안전한 S3 사용 가이드

- - +

· 약 12분

개요

여행에 대한 경로를 보여주기 위해 경로 이미지를 생성하는 기능을 추가했다.
+경로 이미지에 대한 요구사항 및 기술 선택에 대한 내용은 링크에 있다.

구현 결과

./result.png

예시 데이터는 다음과 같다.
+서울역(점) → 신사역 → 노량진역 → 홍대입구역 → 종로3가역 → 옥수역 → 구로역(점) → 신림역 → 발산역

예시 데이터
List<Double> x = List.of(
126.97094933811682, 127.02154822802501, 126.94218991864345, 126.92402556641424,
126.99265358592287, 127.01779856076462, 126.88474839801178, 126.92900751277035, 126.83930056313639
);
List<Double> y = List.of(
37.55302829553499, 37.51619698970427, 37.51294119442773, 37.5565933969331,
37.57034879708931, 37.54027238225762, 37.50129417536773, 37.48258811529137, 37.557607696911184
);
List<Double> xPoints = List.of(126.97094933811682, 126.88474839801178);
List<Double> yPoints = List.of(37.55302829553499, 37.50129417536773);

IMAGE_SIZE & ROUTE_SIZE

RouteImageGenerator.java
private static final int IMAGE_SIZE = 800;
private static final int ROUTE_SIZE = 600;

코드를 보면 IMAGE_SIZE와 ROUTE_SIZE가 있다.
+IMAGE_SIZE는 말 그대로 이미지의 width와 height를 의미한다.
+ROUTE_SIZE의 경우 상하좌우 100px 만큼의 간격을 위해 존재한다.
+따라서 실제 경로 이미지의 크기는 600 * 600 사이즈로 생성된다.

./600.png

사이즈 변경의 이유

255 255 정도의 작은 사이즈로 이미지를 생성해보려고 했는데, 이미지의 선명도가 좋지 않아 800 800 사이즈로 변경했다.

주요 클래스

요약

클래스명설명특이사항
Coordinate위도, 경도로 이루어진 위치 값좌표를 뜻하지만 여행 도메인에 포함된 Point 클래스와 구분하기 위해 longitude, latitude를 사용하지 않고 x, y 사용
CoordinatesCoordinate의 일급 컬렉션-
Position실제 이미지 생성에 사용할 위치 값Integer 타입의 x, y 사용
PositionsPositions의 일급 컬렉션-
RouteImageDrawer실제 이미지에 경로를 그려주는 클래스 BufferedImage, Graphics2D를 가지고 있음이미지 생성에 필요한 상수가 정의되어 있음
RouteImageUploaderBufferedImage를 받아 서버에 업로드 하는 클래스현재 업로드 위치가 정해지지 않아 일단 기본(프로젝트 루트) 위치에 생성
RouteImageGenerator이미지를 생성하고 업로드하는 서비스여행 종료, 감상 저장시 해당 클래스를 통해 이미지 생성 요청
BufferedImage(AWT)이미지 데이터를 처리하고 조작하는 데 사용왼쪽 상단의 좌표가 (0, 0)
Graphics2D(AWT)선 그리기, 색상 관리 등을 지원하는 클래스 실제 해당 클래스의 draw 메서드를 경로를 그림JDK 1.2 이후에 추가됨, 2D(평면) 그래픽 환경을 지원, bufferedImage.createGraphics 메서드를 통해 생성

의존관계

Coordinates(위도, 경도의 일급 컬렉션)

List<Double> 2개(위도, 경도)인 형태로 관리하는 방법이 있었지만, 위치 점을 여러개 찍는 부분에서 로직이 복잡해 질 것 같아서 Coordinate(x, y)와 일급 컬렉션인 Coordinates로 관리하기로 했다.
+Coordinates 클래스에는 다음 두 개의 인터페이스가 존재한다.

  • calculatePositions: 경로 이미지의 크기를 받아 실제 이미지 생성시 사용될 Positions를 반환
  • indexOf: 다른 Coordinates를 받아 동일한 위치점에 해당하는 인덱스를 반환하는

Positions 계산 로직은 다음과 같다.
+위도, 경도 각각에 대한 부분을 이미지 생성시 필요한 값으로 변환한다.

Coordinates.java
// 호출
// List<Integer> xPositions = toPositions(xValues, maxDifference, routeImageSize);
// List<Integer> yPositions = toPositions(yValues, maxDifference, routeImageSize);

private List<Integer> toPositions(List<Double> values, Double maxDifference, Integer routeImageSize) {
Double minValue = Collections.min(values);
return values.stream()
.map(value -> normalizeCoordinate(value, maxDifference, minValue))
.map(value -> mapToPosition(value, routeImageSize))
.toList();
}

private double normalizeCoordinate(Double coordinate, Double maxDifference, Double minValue) {
return (coordinate - minValue) / maxDifference;
}

private int mapToPosition(Double coordinate, Integer routeImageSize) {
return (int) (coordinate * routeImageSize);
}

위도로 예시든 내용이다.

  1. Collections.min(values) → 위도 리스트의 최소값을 구한다.
  2. normalizeCoordinate → 각각의 위도 값에서 최소값을 빼고 0 ~ 1 사이 값으로 변환 후 위경도의 최대 차이로 나눈다.
  3. mapToPosition → 그래프 크기를 받아 0 ~ 1 사이 값을 실제 이미지를 위한 위치값으로 변환한다.

Positions(실제 이미지 생성에 사용할 위치)

Positions 클래스에는 다음 다섯 개의 인터페이스가 존재한다.

  • align: 이미지 사이즈와 경로 이미지 사이즈를 받아 Position 값들을 중앙 정렬한다.
  • getPositionsByIndexes: 인덱스 리스트를 받아 입력받은 인덱스에 해당하는 값들을 반환한다.
  • size: 크기를 반환한다.
  • xPositions: x 값들을 반환한다.
  • yPositions: y 값들을 반환한다.

중앙 정렬 로직은 다음과 같다.

Positions.java
public Positions align(int imageSize, int routeSize) {
int xOffset = calculateOffset(Position::x, imageSize);
int yOffset = calculateOffset(Position::y, imageSize);

return items.stream()
.map(item -> new Position(item.x() + xOffset, imageSize - (item.y() + yOffset)))
.collect(collectingAndThen(toList(), Positions::new));
}

private int calculateOffset(ToIntFunction<Position> positionToInteger, int imageSize) {
List<Integer> positions = items.stream()
.mapToInt(positionToInteger)
.boxed()
.toList();

int midValue = (Collections.min(positions) + Collections.max(positions)) / 2;
return imageSize / 2 - midValue;
}

상하좌우 여백을 동일하게 주기 위해서 offset 값을 구해서 x, y 값에 각각 더하는 형태로 중앙 정렬을 수행했다.
+BufferedImage를 사용할 때 왼쪽 상단의 좌표 (0, 0) 기준으로 아래로 내려갈수록 y 값이 커지고, 오른쪽으로 갈 수록 x 값이 커진다.

./800.png

따라서 최종적으로 이미지를 생성하기 위한 값을 다음과 같이 구했다.

x 값 → 계산한 offset 그대로 더한다.
+y 값 → imageSize(800)에서 y + offset 값을 뺀다.

RouteImageDrawer(실제 이미지에 경로를 그려주는 클래스)

BufferedImage, Graphics2D를 필드로 가지고 있는 클래스다.
+그림을 그리기 위해 설정한 상수들이 존재한다.

RouteImageDrawer.java
// RGB에 각각 8비트씩 할당한 값을 24비트 트루컬러라 부른다.
// 해당 설정은 24비트 + 8비트(alpha, 투명도)를 추가한 32비트 이미지 타입이다.
// 이를 RGBA라고 부른다.
private static final int IMAGE_TYPE = BufferedImage.TYPE_INT_ARGB;
// 배경 투명색
private static final Color TRANSPARENT = new Color(0, 0, 0, 0);
// 경로를 위한 STROKE
private static final int LINE_STROKE_WIDTH = 7;
private static final Stroke LINE_STROKE = new BasicStroke(LINE_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);
// 위치 점을 위한 STROKE
private static final int POINT_STROKE_WIDTH = 20;
private static final Stroke POINT_STROKE = new BasicStroke(POINT_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);
// 안티앨리어싱 등 화질 개선을 위한 설정
private static final Map<Object, Object> renderingHints = Map.of(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON,
RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY,
RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC
);

RouteImageDrawer 클래스에는 다음 세 개의 인터페이스가 존재한다.

  • drawLine: 선을 그린다.
  • drawPoint: 점을 찍는다.
  • dispose: 자원 할당을 해제한다.

dispose의 경우 내부에서 생성된 graphics2D에 대한 자원 할당을 해제하는 메서드인 graphics2D.dispose를 호출한다.

이미지 생성 Flow

1. 이미지 생성 준비

2. 선 그리기 요청

3. 위치 점 그리기 요청

4. 업로드 요청

전체 Flow

+ + \ No newline at end of file diff --git a/page/9.html b/page/9.html index 84a33cd70..c43da4b83 100644 --- a/page/9.html +++ b/page/9.html @@ -13,21 +13,33 @@ - - + +
-

· 약 3분

개요

정적 팩터리 메서드를 모킹한다는 것은 객체지향적인 관점에서 볼 때 안티패턴이다.
-하지만 특수한 경우에는 정적 메서드를 모킹하는 것이 필요할 수 있다고 생각한다.

예를 들어 레거시 코드를 테스트 한다던지, IO 관련한 부분을 테스트 할 때 정말 필요한 부분에만 적용할 수 있을 것이다.

프로젝트를 진행하며 ImageIo.write 메서드가 호출되는 지 검증이 필요했다.
-해당 static 메서드를 호출하는 부분을 따로 RouteImageUploader 클래스로 최대한 분리했다.
-이미지 저장 기능 자체가 외부로 나가는 상호작용이고, 호출 횟수를 검사하는데는 mock을 사용하는게 적절하다고 판단했다.

public void upload(BufferedImage bufferedImage) {
File file = new File(파일경로);
try {
ImageIO.write(bufferedImage, ROUTE_IMAGE_FORMAT, file);
} catch (IOException e) {
throw new DrawException(IMAGE_SAVE_FAIL);
}
}

Mocking static methods

Mockito 3.4.0 이후에는 static method를 모킹할 수 있는 Mockito.mockStatic 메서드를 지원한다.
-mockStatic을 사용하면 MockedStatic<T>이 반환되는데 사용 후 꼭 close를 해줘야 한다.

JUnit의 @BeforeAll로 설정하고 @AfterAll 메서드로 종료하는 방법도 있지만 MockedStatic<T>의 상위 인터페이스인 ScopedMock이 AutoCloseable을 구현하고 있기에 try-with-resources를 사용하는 방법이 더욱 좋은 것 같다.

// given
BufferedImage bufferedImage = new BufferedImage(800, 800, BufferedImage.TYPE_INT_ARGB);
RouteImageUploader routeImageUploader = new RouteImageUploader();

// expect
try (MockedStatic<ImageIO> imageIO = Mockito.mockStatic(ImageIO.class)) {
routeImageUploader.upload(bufferedImage);
imageIO.verify(
() -> ImageIO.write(any(BufferedImage.class), any(String.class), any(File.class)),
times(1)
);
}

마치며

정적 메서드를 모킹하는 것은 안티패턴이으로 적절한 추상화를 이용해 테스트 하기 좋은 코드를 만드는 연습을 하자.
-하지만 추상화를 하면 할 수록 코드의 복잡도는 증가한다.
-항상 상황을 고려하고 간결함을 포기할 만큼 중요한 부분인지 적절한 트레이드오프를 고려하자.

참고 자료

Mocking static methods
-Mockito mock static methods
-Enable mocking static methods in Mockito

- - +

· 약 7분

개요

이전에 기술 구현 가능 여부를 조사하면서 파이썬을 사용한 내용을 정리한 내용이다.

사용 기술

언어: Python 3.10
+이미지 생성: matplotlib
+서비스: AWS Lambda, AWS API Gateway
+이미지 저장 및 URL: AWS S3, AWS CloudFront

플로우는 다음과 같다.

요구사항

./route.png

우측 상단의 경로 이미지를 생성하려고 한다.
+경로 이미지 생성에 대한 요구사항은 다음과 같다.

  • 위도, 경도로 이루어진 배열을 입력받는다.
  • 이미지 생성
  • 선과 점 표현
  • 투명한 배경색
  • 위경도 차이가 크든 작든 제공하는 이미지 내에 경로가 다 포함되어 있어야 한다.

이미지 출력 방식

  1. 위경도를 처리한 값으로 직접 경로를 그린 다음 이미지 형태로 저장
  2. 플롯을 그려주는 라이브러리 사용하여 이미지 형태로 저장

이미지 출력 방식의 경우 1번과 2번을 고민했었다.
+파이썬으로는 플롯을 그려주는 라이브러리인 matplotlib을 사용했다.

로컬에서 기능 구현

import time

import matplotlib.pyplot as plt


def draw(point):
start = time.time()
x, y = zip(*point)
pixel_x, pixel_y = convert_to_pixel_values(x, y)
draw_lines(pixel_x, pixel_y)
end = time.time()
print(end - start)

def convert_to_pixel_values(x, y):
max_diff = max(max(x) - min(x), max(y) - min(y))
return scale_to_pixel_values(x, max_diff), scale_to_pixel_values(y, max_diff)


def scale_to_pixel_values(points, max_diff):
min_value = min(points)
scaled_coordinates = [(p - min_value) / max_diff for p in points]
return scaled_coordinates


def draw_lines(x, y):
figure = plt.gcf()
figure.set_size_inches(5, 5)
plt.plot(x, y, c = 'w',linewidth=5)
plt.scatter(x[3],y[3], c = 'w', s = 125)
plt.axis('off')
plt.savefig('name.png', transparent=True, format='png')

point = [
[126.96352960597338, 37.590841000217125],
[126.96987292787792, 37.58435564234159],
[126.98128481452298, 37.58594375113966],
[126.99360339342958, 37.58248524741927],
[126.99867565340067, 37.56778118088622],
[127.001935378366117, 37.55985240444085],
[126.9831048919687, 37.548030119488665],
[126.97189273528845, 37.5119879225856],
[127.02689859997221, 37.48488593333883]
]

draw(point)

생성 결과는 아래와 같다. (예시를 위해 검은색으로 출력)

./routeImage.png

AWS Lambda

썸네일 생성 서버를 따로 두기는 기능 대비 비용이 너무 클 것이라고 생각했다.
+따라서 서버리스로 파일을 처리했다.
+추가로 s3 접근은 boto3를 사용했다.

람다 S3 접근을 위한 IAM 생성

AmazonS3FullAccess, AmazonS3ObjectLambdaExecutionRolePolicy 두가지를 추가해서 Lambda 전용 역할을 만들어 사용했다.

람다 배포용 코드

기술 구현 가능 여부를 확인할 땐 위치 점을 찍는 기능을 람다에 배포하지 않았다.


import io
import uuid

import boto3
import matplotlib.pyplot as plt

PIXEL = 255
BUCKET_NAME = 'image-plot'
S3 = 's3'

def lambda_handler(event, context):
x = event['x']
y = event['y']
image_name = str(uuid.uuid4())

img_data = draw(x, y)
s3 = boto3.client(S3)
s3.put_object(Body=img_data.getvalue(), ContentType='image/png', Bucket=BUCKET_NAME, Key=image_name)
url = f'https://{BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com/{image_name}'

return {
'statusCode': 200,
'body': url
}

def draw(x, y):
pixel_x, pixel_y = convert_to_pixel_values(x, y)
img_data = draw_lines(pixel_x, pixel_y)
plt.close()
return img_data

def convert_to_pixel_values(x, y):
max_diff = max(max(x) - min(x), max(y) - min(y))
return scale_to_pixel_values(x, max_diff), scale_to_pixel_values(y, max_diff)

def scale_to_pixel_values(points, max_diff):
min_value = min(points)
scaled_coordinates = [(p - min_value) / max_diff for p in points]
pixel_values = [int(p * PIXEL) for p in scaled_coordinates]
return pixel_values

def draw_lines(x, y):
plt.plot(x, y, 'k-', linewidth=10)
plt.axis('off')
img_data = io.BytesIO()
plt.savefig(img_data, transparent=True, format='png')
img_data.seek(0)
return img_data

Layer 추가를 위한 zip 파일 생성

matplotlib의 경우 외부 라이브러리기 때문에 따로 Layer를 추가해야 한다.
+zip 파일을 만들어서 업로드해야한다.
+이때 python의 Lambda 런타임에 대한 계층 경로는 python이다.
+따라서 압축한 zip 파일은 다음과 같은 구조를 띄어야 한다.

pillow.zip
│ python/PIL
└ python/Pillow-5.3.0.dist-info

Ubuntu 기준 다음 명령어를 입력하여 생성을 진행했다.

sudo apt update
sudo apt install zip
sudo apt install python3-pip

mkdir python
pip3 install matplotlib -t python # pip3 install 설치할_패키지 -t 설치_경로
zip -r my_layer.zip python # zip -r 압축_파일명 압축_파일이_존재하는_경로

No module named 'numpy.core._multiarray_umath' 에러

Layer 추가 후 람다 실행 시 발생한 에러였다.
+처음에 mac에서 zip 파일을 생성해서 업로드했는데 해당 문제가 발생했다.
+이는 lambda가 돌아가는 동일한 환경에서 layer를 위한 zip 파일을 만들지 않아서 발생하는 문제다.
+간단하게 ec2 인스턴스를 하나 만들어서 따로 Layer를 생성하면 문제가 발생하지 않는다.

적정기술에 대한 생각

프로젝트에 Lambda와 Python을 사용하려고 했지만 아쉽게도 반려당했다.
+AWS Lambda를 사용하는 것은 인스턴스에 해당 코드를 배포하는 것보다 더 효율적인 방법일 수 있다.
+하지만 현재 프로젝트에서 가용 가능한 자원, 기술의 난이도, 사용하는 팀원을 고려한다면 Lambda는 적정기술이 아닐 수 있다.
+따라서 해당 이미지 생성기를 어떻게 적용할지 조금 더 고려를 해야 될 것으로 보인다.

최종적으로 Java AWT를 사용하기로 결정했다.

참고 자료

AWS Lambda
+Lambda Layer
+Python Lambda 함수에 대한 .zip 파일 아카이브 작업
+No module named 'numpy.core._multiarray_umath'
+사례별로 알아본 안전한 S3 사용 가이드

+ + \ No newline at end of file diff --git a/parameterized-tests.html b/parameterized-tests.html index 8f93d442f..9fca013f6 100644 --- a/parameterized-tests.html +++ b/parameterized-tests.html @@ -13,12 +13,12 @@ - - + +
-

Parameterized Tests

· 약 4분

테스트를 작성하다보면 매개변수에 따라 반복이 되는 테스트들이 생긴다.
+

Parameterized Tests

· 약 4분

테스트를 작성하다보면 매개변수에 따라 반복이 되는 테스트들이 생긴다.
이 때 @ParameterizedTest를 사용하면 단일 테스트를 매개변수를 사용하여 여러 번 반복할 수 있다.

Argument Sources

@ParameterizedTest를 사용하려면 최소 하나 이상의 Source 애노테이션이 필요하다.
JUnit이 제공하는 다양한 Source가 있기 때문에, 테스트에 맞춰 다양하게 사용할 수 있다.

Value Source

값을 이용하여 제공하는 형태로, 다음과 같은 타입의 값을 매개변수로 제공할 수 있다.

  • short, int, long, float, double
  • byte, char, boolean, String, Class
@ParameterizedTest
@ValueSource(ints = {1, 100, Integer.MAX_VALUE})
void valueTest(final int value) {
Assertions.assertThat(value).isPositive();
}

Null & Empty Source

null 값, 빈 값을 제공한다.
Empty Source의 경우 다음과 같은 타입에 한해 매개변수로 제공할 수 있다.

  • String
  • java.util.List, java.util.Set, java.util.Map
  • primitive arrays — ex) int[]
  • object arrays — ex) String[]
@ParameterizedTest
@NullAndEmptySource
void nullAndEmptyTest(final String value) {
Assertions.assertThat(value).isNullOrEmpty();
}

Enum Source

EnumSource를 이용하여 Enum 또한 매개변수로 제공할 수 있다.

enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}

@ParameterizedTest
@EnumSource(Day.class)
void enumTest(final Day day) {
assertThat(day).isInstanceOf(Day.class);
}

다음과 같이 mode 값을 이용하여 특징 Enum을 제외하거나, 포함시킬 수 있다. (default: Mode.Include)

@ParameterizedTest
@EnumSource(value = Day.class, names = {"SATURDAY", "SUNDAY"}, mode = Mode.EXCLUDE)
void enumTest(final Day day) {
// MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
assertThat(day).isInstanceOf(Day.class);
}

CSV Source

csv 형식의 값을 이용하여 매개변수를 제공한다.
@@ -26,7 +26,7 @@ 개인적으로 2개 정도의 값을 매개변수로 전달하는 경우 CsvSource를 사용한다.

@ParameterizedTest
@CsvSource({"1,1", "2,4", "3,9", "4,16"})
void csvTest(final int number, final int result) {
assertThat(number * number).isEqualTo(result);
}

Method Source

복잡한 타입의 값을 전달할 때 사용한다.
메서드명을 입력하여 매개변수를 제공하는 메서드를 지정할 수 있다.
메서드명을 따로 입력하지 않으면 테스트명과 동일한 static 메서드가 지정된다.

@ParameterizedTest
@MethodSource
void methodTest(final List<Integer> numbers, final int count) {
assertThat(numbers).hasSize(count);
}

private static Stream<Arguments> methodTest() {
return Stream.of(
Arguments.of(List.of(1), 1),
Arguments.of(List.of(1, 2), 2),
Arguments.of(List.of(1, 2, 3), 3)
);
}

ETC.

위에서 언급한 방법 이외에도 다양한 방법으로 매개변수를 제공할 수 있다.

  • CSV 파일을 이용한 CsvFileSource
  • ArgumentsProvider 구현한 클래스를 이용하는 ArgumentsSource

참고 자료

- - + + \ No newline at end of file diff --git a/performance-test-type.html b/performance-test-type.html index 26c3448a9..05195989f 100644 --- a/performance-test-type.html +++ b/performance-test-type.html @@ -13,12 +13,12 @@ - - + +
-

성능 테스트 종류

· 약 6분

성능 테스트

API의 요청이 많은 상황에서 서버가 어떻게 동작하는지 확인하는 테스트

시스템에 부하가 걸리면 문제 상황이 발생할 수 있다.
+

성능 테스트 종류

· 약 6분

성능 테스트

API의 요청이 많은 상황에서 서버가 어떻게 동작하는지 확인하는 테스트

시스템에 부하가 걸리면 문제 상황이 발생할 수 있다.
다양한 상황에 대비해서 성능 테스트를 해야한다.

./test.png

스모크 테스트(Smoke Test)

최소한의 부하를 주어 시스템이 정상적으로 동작하는지 확인하는 테스트

VU를 최소한으로 두고, 짧은 시간을 가지고 테스트한다.
다른 테스트를 시작하기 전에 스모크 테스트를 함으로써 테스트 스크립트에 오류가 없는지 확인할 수 있고, 성능 지표가 정상적으로 수집, 모니터링 되고 있는지 확인할 수 있다.

가상 사용자(VU)

가상 사용자는 서버 애플리케이션에 대해 특정 테스트를 실행한다.
이는 다른 가상 사용자와 독립적으로 실행되며, 여러 가상 사용자를 사용하여 동시 연결을 할 수 있다.
@@ -33,7 +33,7 @@ 다만 Auto Scaling이 적용된 클라우드 환경에서는 진행하지 않아야 한다.

참고 자료

Load test types, k6
자바 최적화 - 벤저민 J. 에번스, 제임스 고프, 크리스 뉴랜드
아마존 웹 서비스 부하 테스트 입문 - 나카가와 타루하치, 모리시타 켄

- - + + \ No newline at end of file diff --git a/racing-car-retrospective.html b/racing-car-retrospective.html index f18d53ddf..39b9e8442 100644 --- a/racing-car-retrospective.html +++ b/racing-car-retrospective.html @@ -13,12 +13,12 @@ - - + +
-

자동차 경주 미션 회고

· 약 8분

자동차 경주

자동차 경주 미션에서는 다즐과 페어가 매칭되었다.
+

자동차 경주 미션 회고

· 약 8분

자동차 경주

자동차 경주 미션에서는 다즐과 페어가 매칭되었다.
우테코 들어와서 첫 페어프로그래밍이라 많이 떨렸지만, 다즐이 대화를 잘 이끌어줘 너무 즐거웠다.

첫날은 간단히 컨벤션과 환경을 설정하는 시간을 가졌고 다음 날부터 자동차 경주를 시작했다.
시작은 간단하게 요구사항을 정리하고, 어떻게 코드를 작성할지 같이 고민했다.

시작하기 전 아래와 같이 mermaid를 이용하여 의존성 방향에 대해서 간단한 다이어그램을 만들고 시작했다.
mermaid는 코드로 다이어그램을 생성 해주는 도구로 다음과 같은 장점이 있다고 생각한다.

  • 코드 기반이라 빠른 시간 안에 생각한 것을 시각화할 수 있다.
  • github에서 mermaid를 지원하기 때문에 리뷰어에게 코드를 이해할 수 있는 부가적인 정보를 제공할 수 있다.

미션을 진행하는 데 큰 어려움이 있지는 않았고, 페어를 마치기 전 서로 고민되는 부분을 정리했을 때 좋았다.

페어하면서 잘했다고 생각했던 점은 서로의 생각과 리뷰 받은 것을 공유한 것이다.
@@ -44,7 +44,7 @@ 그래서 즐거운 마음으로 페어 프로그래밍을 했었던 것 같다.

어떤 이유 때문인지 모르겠지만 같이 페어하는데 편한 마음이 들었다.
이건 바로 배울 수 없지만.
나도 같이 일할 때 편한 사람, 같이 일하고 싶은 사람이 되기 위해 깊이 고민해봐야겠다.

- - + + \ No newline at end of file diff --git a/route-image-async-with-event.html b/route-image-async-with-event.html index 8de84f14c..a88f02776 100644 --- a/route-image-async-with-event.html +++ b/route-image-async-with-event.html @@ -13,12 +13,12 @@ - - + +
-

경로 이미지 생성하기 - 비동기 처리

· 약 12분

이전 글

경로 이미지 생성하기 - 기술 선택
+

경로 이미지 생성하기 - 비동기 처리

· 약 12분

이전 글

경로 이미지 생성하기 - 기술 선택
경로 이미지 생성하기 - 구현

개요

현재 여행을 마치는 경우, 감상을 생성하는 경우 이미지 생성 요청이 이루어진다.
경로 이미지 생성의 경우 위치 정보의 개수에 정비례하여 생성 시간이 증가한다.
따라서 비동기로 이미지 생성 요청을 처리하여 사용자의 경험을 개선시킬 수 있다고 생각했다.

주기능의 응답속도 개선

여행 종료와 감상 생성이 주기능이고, 이미지 생성 기능은 부기능이다.
@@ -45,7 +45,7 @@ 응답 시간에 이미지 생성 시간이 포함되지 않아서 성능이 개선된 것을 볼 수 있다.

참고 자료

7.7. Task Execution and Scheduling, Spring Boot Docs
Spring Events, Baeldung
회원시스템 이벤트기반 아키텍처 구축하기

- - + + \ No newline at end of file diff --git a/route-image-implementation.html b/route-image-implementation.html index 1851e7407..d15f141b6 100644 --- a/route-image-implementation.html +++ b/route-image-implementation.html @@ -13,12 +13,12 @@ - - + +
-

경로 이미지 생성하기 - 구현

· 약 12분

개요

여행에 대한 경로를 보여주기 위해 경로 이미지를 생성하는 기능을 추가했다.
+

경로 이미지 생성하기 - 구현

· 약 12분

개요

여행에 대한 경로를 보여주기 위해 경로 이미지를 생성하는 기능을 추가했다.
경로 이미지에 대한 요구사항 및 기술 선택에 대한 내용은 링크에 있다.

구현 결과

./result.png

예시 데이터는 다음과 같다.
서울역(점) → 신사역 → 노량진역 → 홍대입구역 → 종로3가역 → 옥수역 → 구로역(점) → 신림역 → 발산역

예시 데이터
List<Double> x = List.of(
126.97094933811682, 127.02154822802501, 126.94218991864345, 126.92402556641424,
126.99265358592287, 127.01779856076462, 126.88474839801178, 126.92900751277035, 126.83930056313639
);
List<Double> y = List.of(
37.55302829553499, 37.51619698970427, 37.51294119442773, 37.5565933969331,
37.57034879708931, 37.54027238225762, 37.50129417536773, 37.48258811529137, 37.557607696911184
);
List<Double> xPoints = List.of(126.97094933811682, 126.88474839801178);
List<Double> yPoints = List.of(37.55302829553499, 37.50129417536773);

IMAGE_SIZE & ROUTE_SIZE

RouteImageGenerator.java
private static final int IMAGE_SIZE = 800;
private static final int ROUTE_SIZE = 600;

코드를 보면 IMAGE_SIZE와 ROUTE_SIZE가 있다.
IMAGE_SIZE는 말 그대로 이미지의 width와 height를 의미한다.
@@ -29,7 +29,7 @@ BufferedImage를 사용할 때 왼쪽 상단의 좌표 (0, 0) 기준으로 아래로 내려갈수록 y 값이 커지고, 오른쪽으로 갈 수록 x 값이 커진다.

./800.png

따라서 최종적으로 이미지를 생성하기 위한 값을 다음과 같이 구했다.

x 값 → 계산한 offset 그대로 더한다.
y 값 → imageSize(800)에서 y + offset 값을 뺀다.

RouteImageDrawer(실제 이미지에 경로를 그려주는 클래스)

BufferedImage, Graphics2D를 필드로 가지고 있는 클래스다.
그림을 그리기 위해 설정한 상수들이 존재한다.

RouteImageDrawer.java
// RGB에 각각 8비트씩 할당한 값을 24비트 트루컬러라 부른다.
// 해당 설정은 24비트 + 8비트(alpha, 투명도)를 추가한 32비트 이미지 타입이다.
// 이를 RGBA라고 부른다.
private static final int IMAGE_TYPE = BufferedImage.TYPE_INT_ARGB;
// 배경 투명색
private static final Color TRANSPARENT = new Color(0, 0, 0, 0);
// 경로를 위한 STROKE
private static final int LINE_STROKE_WIDTH = 7;
private static final Stroke LINE_STROKE = new BasicStroke(LINE_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);
// 위치 점을 위한 STROKE
private static final int POINT_STROKE_WIDTH = 20;
private static final Stroke POINT_STROKE = new BasicStroke(POINT_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);
// 안티앨리어싱 등 화질 개선을 위한 설정
private static final Map<Object, Object> renderingHints = Map.of(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON,
RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY,
RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC
);

RouteImageDrawer 클래스에는 다음 세 개의 인터페이스가 존재한다.

  • drawLine: 선을 그린다.
  • drawPoint: 점을 찍는다.
  • dispose: 자원 할당을 해제한다.

dispose의 경우 내부에서 생성된 graphics2D에 대한 자원 할당을 해제하는 메서드인 graphics2D.dispose를 호출한다.

이미지 생성 Flow

1. 이미지 생성 준비

2. 선 그리기 요청

3. 위치 점 그리기 요청

4. 업로드 요청

전체 Flow

- - + + \ No newline at end of file diff --git a/route-image-intro.html b/route-image-intro.html index ef110ceca..4d9161f1d 100644 --- a/route-image-intro.html +++ b/route-image-intro.html @@ -13,19 +13,19 @@ - - + +
-

경로 이미지 생성하기 - 기술 선택

· 약 6분

./route.png

이미지 생성의 책임

위 와이어 프레임에서 여행 히스토리여행에 대한 감상을 위한 경로 이미지의 경우 네이버 지도를 사용하여 해당 기능을 구현할 수 없으니 당연히 맵 API에서 제공하는 도형 그리기 API(네이버 맵 API 기준 Polyline)를 사용할 수 없다.
+

경로 이미지 생성하기 - 기술 선택

· 약 6분

./route.png

이미지 생성의 책임

위 와이어 프레임에서 여행 히스토리여행에 대한 감상을 위한 경로 이미지의 경우 네이버 지도를 사용하여 해당 기능을 구현할 수 없으니 당연히 맵 API에서 제공하는 도형 그리기 API(네이버 맵 API 기준 Polyline)를 사용할 수 없다.
따라서 이미지를 직접 생성하거나, 클라이언트에서 직접 위경도를 이용하여 그려야 한다.

해당 요구사항을 해결하기 위해서는 다음과 같은 기능을 가진 라이브러리가 필요하다.

  • 이미지 생성
  • 선과 점 표현
  • 투명한 배경색

현재 클라이언트의 바쁜 일정과 기능 구현에 있어 약간의 연산이 들어간다는 부분을 고려하여 백엔드에서 이미지를 생성하기로 결정을 내렸다.

고려한 기술

백엔드에서 이미지 생성을 하기 위해 다음과 같은 라이브러리 또는 기술들을 확인해 보았다.

  • Python의 Matplotlib
  • AWT(Abstract Window Toolkit) [최종 선택]
  • 이미지 처리 라이브러리 및 Java에서 내부적으로 Matplotlib 사용할 수 있는 라이브러리 (원하는 기능 없음)
  • Java Swing, Java FX (단순한 선 그리기 + 점 찍기라 불필요)

Python & Matplotlib

데이터 시각화 라이브러리
이미지 생성 및 로컬에 저장까지 걸리는 시간: 0.2초

  • 코드가 간단해서 유지 보수성이 좋다.
  • AWS Lambda 같은 서버리스 컴퓨팅 서비스나 FastAPI와 같은 웹 프레임워크로 추가적인 API를 구현해야 한다.
  • Spring Boot에서 추가적인 API 호출을 해야하고, 확장성과 비동기 처리 등 고려 해야 할 부분이 많다.

Java AWT 이외의 라이브러리

Python이 아닌 Java에서의 라이브러리도 고려를 해봤지만 요구사항에 적합하지 않거나, 적은 요구사항에 비해 무거운 라이브러리들이 많아서 제외했다.

라이브러리설명제외 이유
SwingAWT 이후에 나온 GUI 라이브러리, 네이티브 UI를 사용하지 않고 모든 운영체제 상에서 동일한 UI를 가지도록 함요구사항에 비해 무겁고 복잡도가 높음
JavaFXSwing 이후에 나온 GUI 라이브러리, 3차원 그래픽을 지원함요구사항에 비해 무겁고 복잡도가 높음
simple-java-plotAWT로 구현된 플로팅 라이브러리AWT 기반이긴 하지만 직접 AWT를 사용하는 것에 비해 메리트가 없음, 커스텀 설정 기능이 없음
matplotlib4jMatplotlib를 Java에서 사용할 수 있게 하는 라이브러리내부적으로 파이썬 사용하기에 무거움, 배경 투명화 기능 없음

Java & AWT(Abstract Window Toolkit)

그래픽과 이미지를 그리기 위한 도구
이미지 생성 및 로컬에 저장까지 걸리는 시간: 1.75초

  • 플로팅 라이브러리를 사용하는 것보다 구현의 난이도가 다소 존재한다.
  • 이미지 생성 시간이 다소 소요되기 때문에 빠른 응답 반환을 위해 비동기 처리를 고려할 수 있을 것 같다.
  • 추가적인 api 호출을 하지 않아도 된다.

기술 선택

AWT의 경우 Matplotlib에 비해 구현의 난이도가 다소 있고, 이미지 생성 시간이 더 많이 걸리는 단점이 있다.
하지만 추가적인 api 호출을 하지 않아도 되는 부분, Python을 사용하는 경우 추가적인 웹 프레임워크의 학습 비용을 고려하여 AWT를 사용하기로 결정했다.

유지 보수

AWT라는 생소한 기술을 사용하기 때문에 유지 보수성을 위해 팀원들과 공유하는 것이 중요하다고 생각했다.
따라서 다음과 같은 방법으로 공유하기로 했다.

  1. 코드 리뷰와 PR을 통해 작성한 AWT 코드에 대한 설명 및 리뷰 받는다.
  2. AWT를 사용한 부분을 문서화하여 공유한다.

레벨 3를 마무리하며 내용 추가

기술 선택을 하기 위한 실행 시간 측정에 오류가 있었다.
AWT를 사용하는 부분에서 애플리케이션 실행 시간을 제외하면 파이썬과 비슷한 시간안에 이미지를 생성할 수 있었다.

- - + + \ No newline at end of file diff --git a/route-image-python.html b/route-image-python.html index 947ea0f73..6c0368618 100644 --- a/route-image-python.html +++ b/route-image-python.html @@ -13,12 +13,12 @@ - - + +
-

경로 이미지 생성하기 - 파이썬

· 약 7분

개요

이전에 기술 구현 가능 여부를 조사하면서 파이썬을 사용한 내용을 정리한 내용이다.

사용 기술

언어: Python 3.10
+

- - + + \ No newline at end of file diff --git a/rss.xml b/rss.xml index 7eef474d2..34ae84ed2 100644 --- a/rss.xml +++ b/rss.xml @@ -4,10 +4,24 @@ GG Blog https://greeng00se.github.io/ GG Blog - Mon, 11 Sep 2023 00:00:00 GMT + Mon, 18 Sep 2023 00:00:00 GMT https://validator.w3.org/feed/docs/rss2.html https://github.com/jpmonette/feed ko + + <![CDATA[비동기 예외 처리]]> + https://greeng00se.github.io/async-exception + https://greeng00se.github.io/async-exception + Mon, 18 Sep 2023 00:00:00 GMT + + 개요

현재 트립드로우의 경로 이미지 생성 기능은 비동기로 처리되고 있다. 로그를 확인하는 도중 @Async가 적용된 메서드에서 예외가 발생하는 경우 로그가 정상적으로 출력되지 않는 문제가 발생했다.

확인해 보니 Spring의 @ControllerAdvice + @ExceptionHandler의 경우 동기 예외만 처리하고, 비동기 예외를 처리하지 않았다. 따라서 Spring에서 지원해 주는 AsyncUncaughtExceptionHandler 인터페이스를 구현해서 예외를 처리하는 클래스를 생성했다.

비동기 예외처리

AsyncExceptionHandler
@Slf4j
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

private static final String LOG_FORMAT = "[%s] %s";

@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
log.info(String.format(LOG_FORMAT, MDC.get(REQUEST_ID.key()), throwable.getMessage()), throwable);
}
}

해당 AsyncExceptionHandler의 경우 AsyncConfigurer를 구현한 Configuration 클래스를 사용하여 등록할 수 있다. getAsyncUncaughtExceptionHandler 메서드를 오버라이딩하여 이전에 생성해 준 AsyncExceptionHandler를 반환하도록 설정했다.
+이렇게 설정한다면 예외가 발생하는 경우 AsyncUncaughtExceptionHandler의 구현체인 AsyncExceptionHandler가 예외를 잡아서 처리를 해준다.

AsyncConfig
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}

MDC 정보 연동 문제

./mdc-null.png

기존 예외가 발생할 때 실행 흐름을 추적하기 위해 MDC(Mapped Diagnostic Context)를 사용한다.
+비동기 처리의 경우 별도의 스레드에서 동작하기 때문에 ThreadLocal 기반으로 동작하는 MDC의 정보를 얻어올 수 없었다.

이를 적절하게 Decorator 클래스를 설정하여 MDC의 정보를 복사해서 넘겨줄 수 있다.

다음과 같이 TaskDecorator를 구현한 클래스를 하나 생성하고, Task가 실행되기 전 MDC의 정보를 복사하도록 설정했다.

MdcTaskDecorator
public class MdcTaskDecorator implements TaskDecorator {

@Override
public Runnable decorate(final Runnable runnable) {
Map<String, String> threadContext = MDC.getCopyOfContextMap();
return () -> {
MDC.setContextMap(threadContext);
runnable.run();
};
}
}

해당 Decorator 클래스를 설정 파일에 등록해 준다.

AsyncConfig
@RequiredArgsConstructor
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

private final AsyncConfigurationProperties properties;

@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(properties.coreSize());
executor.setMaxPoolSize(properties.maxSize());
executor.setQueueCapacity(properties.queueCapacity());

executor.setTaskDecorator(new MdcTaskDecorator());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}

설정 후에는 정상적으로 MDC에 들어가 있는 UUID가 출력되는 것을 볼 수 있다.

./mdc-not-null.png

참고 자료

spring async, baeldung
+@Async will not call by @ControllerAdvice for global exception
+Spring 의 동기, 비동기, 배치 처리시 항상 context 를 유지하고 로깅하기, 강남언니

]]>
+ async + exception +
<![CDATA[톰캣 구현 미션 회고]]> https://greeng00se.github.io/tomcat-retrospective diff --git a/search.html b/search.html index 2342b05f3..702142741 100644 --- a/search.html +++ b/search.html @@ -13,13 +13,13 @@ - - + + - - + + \ No newline at end of file diff --git a/shopping-cart-retrospective.html b/shopping-cart-retrospective.html index 6a2d8e0ac..c50aca081 100644 --- a/shopping-cart-retrospective.html +++ b/shopping-cart-retrospective.html @@ -13,12 +13,12 @@ - - + +
-

웹 장바구니 미션 회고

· 약 5분

웹 장바구니 미션

장바구니 미션은 블랙캣이랑 진행했다.
+

웹 장바구니 미션 회고

· 약 5분

웹 장바구니 미션

장바구니 미션은 블랙캣이랑 진행했다.
요구사항이 엄청 복잡한 미션은 아니었고, 스프링을 사용하여 기본적인 CRUD를 구현하는 미션이었다.
2단계에서는 Basic 인증을 통해 자신의 장바구니에만 상품을 담고, 제거할 수 있도록 구현하는 요구사항이 추가되었다.
Interceptor나 Argument Resolver에 대한 이해도가 높지 않았는데, 이번 미션을 통해 조금 더 알아간 느낌이다.
@@ -32,7 +32,7 @@ 추가적으로 이모지를 적극적으로 사용하여 더욱 좋았다!

의견 일치시키기

페어 시간은 한정되어 있고, 기간 내 요구사항을 만족해야 한다.
따라서 적당히 타협을 봐서 의견을 빠르게 수용해 데드라인을 맞추는 것도 중요하다고 생각한다.
블랙캣은 내 의견을 잘 들어줬고, 덕분에 막히는 부분 없이 빠르게 미션을 진행할 수 있었다.

빨리 친해졌고, 의사소통이 잘 돼서 재밌게 코딩할 수 있었다!

- - + + \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index df93566ec..4be82832c 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1 +1 @@ -https://greeng00se.github.io/2022-retrospectiveweekly0.5https://greeng00se.github.io/accidental-duplicationweekly0.5https://greeng00se.github.io/blackjack-retrospectiveweekly0.5https://greeng00se.github.io/blogweekly0.5https://greeng00se.github.io/book-leadership-and-self-deceptionweekly0.5https://greeng00se.github.io/book-writerweekly0.5https://greeng00se.github.io/chess-retrospectiveweekly0.5https://greeng00se.github.io/cloudwatchweekly0.5https://greeng00se.github.io/compositeweekly0.5https://greeng00se.github.io/custom-jdbc-templateweekly0.5https://greeng00se.github.io/db-replicationweekly0.5https://greeng00se.github.io/docs/tagsweekly0.5https://greeng00se.github.io/docs/tags/bookweekly0.5https://greeng00se.github.io/docs/tags/etcweekly0.5https://greeng00se.github.io/docs/tags/jpaweekly0.5https://greeng00se.github.io/docs/tags/latencyweekly0.5https://greeng00se.github.io/docs/tags/load-balancingweekly0.5https://greeng00se.github.io/docs/tags/monitoringweekly0.5https://greeng00se.github.io/docs/tags/networkweekly0.5https://greeng00se.github.io/docs/tags/nginxweekly0.5https://greeng00se.github.io/docs/tags/packageweekly0.5https://greeng00se.github.io/docs/tags/performanceweekly0.5https://greeng00se.github.io/docs/tags/postmortemweekly0.5https://greeng00se.github.io/docs/tags/testweekly0.5https://greeng00se.github.io/docs/tags/throughputweekly0.5https://greeng00se.github.io/docusaurusweekly0.5https://greeng00se.github.io/graspweekly0.5https://greeng00se.github.io/innodb-lockweekly0.5https://greeng00se.github.io/intellij-settingsweekly0.5https://greeng00se.github.io/java-class-fileweekly0.5https://greeng00se.github.io/java-spring-springbootweekly0.5https://greeng00se.github.io/jenkinsweekly0.5https://greeng00se.github.io/jsr-310weekly0.5https://greeng00se.github.io/kotlin-nullweekly0.5https://greeng00se.github.io/ladder-retrospectiveweekly0.5https://greeng00se.github.io/level2-interview-retrospectiveweekly0.5https://greeng00se.github.io/mock-static-methodweekly0.5https://greeng00se.github.io/mysql-lockweekly0.5https://greeng00se.github.io/order-retrospectiveweekly0.5https://greeng00se.github.io/page/10weekly0.5https://greeng00se.github.io/page/11weekly0.5https://greeng00se.github.io/page/12weekly0.5https://greeng00se.github.io/page/13weekly0.5https://greeng00se.github.io/page/14weekly0.5https://greeng00se.github.io/page/15weekly0.5https://greeng00se.github.io/page/16weekly0.5https://greeng00se.github.io/page/17weekly0.5https://greeng00se.github.io/page/18weekly0.5https://greeng00se.github.io/page/19weekly0.5https://greeng00se.github.io/page/2weekly0.5https://greeng00se.github.io/page/20weekly0.5https://greeng00se.github.io/page/21weekly0.5https://greeng00se.github.io/page/22weekly0.5https://greeng00se.github.io/page/23weekly0.5https://greeng00se.github.io/page/24weekly0.5https://greeng00se.github.io/page/25weekly0.5https://greeng00se.github.io/page/26weekly0.5https://greeng00se.github.io/page/27weekly0.5https://greeng00se.github.io/page/28weekly0.5https://greeng00se.github.io/page/29weekly0.5https://greeng00se.github.io/page/3weekly0.5https://greeng00se.github.io/page/30weekly0.5https://greeng00se.github.io/page/31weekly0.5https://greeng00se.github.io/page/32weekly0.5https://greeng00se.github.io/page/33weekly0.5https://greeng00se.github.io/page/34weekly0.5https://greeng00se.github.io/page/35weekly0.5https://greeng00se.github.io/page/36weekly0.5https://greeng00se.github.io/page/37weekly0.5https://greeng00se.github.io/page/38weekly0.5https://greeng00se.github.io/page/39weekly0.5https://greeng00se.github.io/page/4weekly0.5https://greeng00se.github.io/page/40weekly0.5https://greeng00se.github.io/page/41weekly0.5https://greeng00se.github.io/page/42weekly0.5https://greeng00se.github.io/page/43weekly0.5https://greeng00se.github.io/page/44weekly0.5https://greeng00se.github.io/page/45weekly0.5https://greeng00se.github.io/page/5weekly0.5https://greeng00se.github.io/page/6weekly0.5https://greeng00se.github.io/page/7weekly0.5https://greeng00se.github.io/page/8weekly0.5https://greeng00se.github.io/page/9weekly0.5https://greeng00se.github.io/parameterized-testsweekly0.5https://greeng00se.github.io/performance-test-typeweekly0.5https://greeng00se.github.io/racing-car-retrospectiveweekly0.5https://greeng00se.github.io/route-image-async-with-eventweekly0.5https://greeng00se.github.io/route-image-implementationweekly0.5https://greeng00se.github.io/route-image-introweekly0.5https://greeng00se.github.io/route-image-pythonweekly0.5https://greeng00se.github.io/searchweekly0.5https://greeng00se.github.io/shopping-cart-retrospectiveweekly0.5https://greeng00se.github.io/subway-retrospectiveweekly0.5https://greeng00se.github.io/tagsweekly0.5https://greeng00se.github.io/tags/asyncweekly0.5https://greeng00se.github.io/tags/awtweekly0.5https://greeng00se.github.io/tags/awt/page/2weekly0.5https://greeng00se.github.io/tags/bookweekly0.5https://greeng00se.github.io/tags/book/page/2weekly0.5https://greeng00se.github.io/tags/book/page/3weekly0.5https://greeng00se.github.io/tags/classweekly0.5https://greeng00se.github.io/tags/cloudwatchweekly0.5https://greeng00se.github.io/tags/compositeweekly0.5https://greeng00se.github.io/tags/data-baseweekly0.5https://greeng00se.github.io/tags/data-base/page/2weekly0.5https://greeng00se.github.io/tags/data-base/page/3weekly0.5https://greeng00se.github.io/tags/documentationweekly0.5https://greeng00se.github.io/tags/dtoweekly0.5https://greeng00se.github.io/tags/elastic-beanstalkweekly0.5https://greeng00se.github.io/tags/eventweekly0.5https://greeng00se.github.io/tags/graspweekly0.5https://greeng00se.github.io/tags/imageweekly0.5https://greeng00se.github.io/tags/image/page/2weekly0.5https://greeng00se.github.io/tags/image/page/3weekly0.5https://greeng00se.github.io/tags/inno-dbweekly0.5https://greeng00se.github.io/tags/intelli-jweekly0.5https://greeng00se.github.io/tags/isolationweekly0.5https://greeng00se.github.io/tags/javaweekly0.5https://greeng00se.github.io/tags/java/page/2weekly0.5https://greeng00se.github.io/tags/java/page/3weekly0.5https://greeng00se.github.io/tags/java/page/4weekly0.5https://greeng00se.github.io/tags/java/page/5weekly0.5https://greeng00se.github.io/tags/jdbcweekly0.5https://greeng00se.github.io/tags/jenkinsweekly0.5https://greeng00se.github.io/tags/kotlinweekly0.5https://greeng00se.github.io/tags/lockweekly0.5https://greeng00se.github.io/tags/lock/page/2weekly0.5https://greeng00se.github.io/tags/logweekly0.5https://greeng00se.github.io/tags/mockweekly0.5https://greeng00se.github.io/tags/mockitoweekly0.5https://greeng00se.github.io/tags/monitoringweekly0.5https://greeng00se.github.io/tags/my-sqlweekly0.5https://greeng00se.github.io/tags/mysqlweekly0.5https://greeng00se.github.io/tags/oopweekly0.5https://greeng00se.github.io/tags/patternweekly0.5https://greeng00se.github.io/tags/performance-testweekly0.5https://greeng00se.github.io/tags/pythonweekly0.5https://greeng00se.github.io/tags/replicationweekly0.5https://greeng00se.github.io/tags/retrospectiveweekly0.5https://greeng00se.github.io/tags/retrospective/page/10weekly0.5https://greeng00se.github.io/tags/retrospective/page/11weekly0.5https://greeng00se.github.io/tags/retrospective/page/12weekly0.5https://greeng00se.github.io/tags/retrospective/page/13weekly0.5https://greeng00se.github.io/tags/retrospective/page/14weekly0.5https://greeng00se.github.io/tags/retrospective/page/15weekly0.5https://greeng00se.github.io/tags/retrospective/page/16weekly0.5https://greeng00se.github.io/tags/retrospective/page/17weekly0.5https://greeng00se.github.io/tags/retrospective/page/2weekly0.5https://greeng00se.github.io/tags/retrospective/page/3weekly0.5https://greeng00se.github.io/tags/retrospective/page/4weekly0.5https://greeng00se.github.io/tags/retrospective/page/5weekly0.5https://greeng00se.github.io/tags/retrospective/page/6weekly0.5https://greeng00se.github.io/tags/retrospective/page/7weekly0.5https://greeng00se.github.io/tags/retrospective/page/8weekly0.5https://greeng00se.github.io/tags/retrospective/page/9weekly0.5https://greeng00se.github.io/tags/springweekly0.5https://greeng00se.github.io/tags/spring-bootweekly0.5https://greeng00se.github.io/tags/staticweekly0.5https://greeng00se.github.io/tags/teco-chatweekly0.5https://greeng00se.github.io/tags/teco-chat/page/2weekly0.5https://greeng00se.github.io/tags/teco-chat/page/3weekly0.5https://greeng00se.github.io/tags/testweekly0.5https://greeng00se.github.io/tags/timeweekly0.5https://greeng00se.github.io/tags/transactionweekly0.5https://greeng00se.github.io/tags/web-socketweekly0.5https://greeng00se.github.io/tags/woowahan-techcourseweekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/10weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/11weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/12weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/13weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/2weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/3weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/4weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/5weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/6weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/7weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/8weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/9weekly0.5https://greeng00se.github.io/tecochat-retrospective-1weekly0.5https://greeng00se.github.io/tecochat-retrospective-2weekly0.5https://greeng00se.github.io/tecochat-retrospective-3weekly0.5https://greeng00se.github.io/test-doubleweekly0.5https://greeng00se.github.io/the-essence-of-object-orientationweekly0.5https://greeng00se.github.io/tomcat-retrospectiveweekly0.5https://greeng00se.github.io/transaction-and-isolationweekly0.5https://greeng00se.github.io/web-racing-car-retrospectiveweekly0.5https://greeng00se.github.io/websocketweekly0.5https://greeng00se.github.io/woowacourse-level1-retrospectiveweekly0.5https://greeng00se.github.io/woowacourse-level2-retrospectiveweekly0.5https://greeng00se.github.io/woowacourse-level3-retrospectiveweekly0.5https://greeng00se.github.io/docsweekly0.5https://greeng00se.github.io/docs/book/getting-out-of-the-boxweekly0.5https://greeng00se.github.io/docs/culture/postmortemweekly0.5https://greeng00se.github.io/docs/design/packageweekly0.5https://greeng00se.github.io/docs/etc/communicationweekly0.5https://greeng00se.github.io/docs/etc/develop-with-springweekly0.5https://greeng00se.github.io/docs/etc/experience-and-self-questionweekly0.5https://greeng00se.github.io/docs/etc/healthful-growthweekly0.5https://greeng00se.github.io/docs/jpa/keyweekly0.5https://greeng00se.github.io/docs/linux/shellweekly0.5https://greeng00se.github.io/docs/linux/swapweekly0.5https://greeng00se.github.io/docs/monitoring/introweekly0.5https://greeng00se.github.io/docs/network/load-balancingweekly0.5https://greeng00se.github.io/docs/network/load-balancing-algorithmweekly0.5https://greeng00se.github.io/docs/nginx/commandweekly0.5https://greeng00se.github.io/docs/nginx/static-fileweekly0.5https://greeng00se.github.io/docs/performance/throughputweekly0.5https://greeng00se.github.io/docs/performance/throughput-latencyweekly0.5https://greeng00se.github.io/docs/performance/typesweekly0.5https://greeng00se.github.io/docs/test/benefitweekly0.5https://greeng00se.github.io/docs/test/firstweekly0.5https://greeng00se.github.io/docs/test/heuristicsweekly0.5https://greeng00se.github.io/docs/test/stairstepweekly0.5https://greeng00se.github.io/weekly0.5https://greeng00se.github.io/weekly0.5 \ No newline at end of file +https://greeng00se.github.io/2022-retrospectiveweekly0.5https://greeng00se.github.io/accidental-duplicationweekly0.5https://greeng00se.github.io/async-exceptionweekly0.5https://greeng00se.github.io/blackjack-retrospectiveweekly0.5https://greeng00se.github.io/blogweekly0.5https://greeng00se.github.io/book-leadership-and-self-deceptionweekly0.5https://greeng00se.github.io/book-writerweekly0.5https://greeng00se.github.io/chess-retrospectiveweekly0.5https://greeng00se.github.io/cloudwatchweekly0.5https://greeng00se.github.io/compositeweekly0.5https://greeng00se.github.io/custom-jdbc-templateweekly0.5https://greeng00se.github.io/db-replicationweekly0.5https://greeng00se.github.io/docs/tagsweekly0.5https://greeng00se.github.io/docs/tags/bookweekly0.5https://greeng00se.github.io/docs/tags/etcweekly0.5https://greeng00se.github.io/docs/tags/jpaweekly0.5https://greeng00se.github.io/docs/tags/latencyweekly0.5https://greeng00se.github.io/docs/tags/load-balancingweekly0.5https://greeng00se.github.io/docs/tags/monitoringweekly0.5https://greeng00se.github.io/docs/tags/networkweekly0.5https://greeng00se.github.io/docs/tags/nginxweekly0.5https://greeng00se.github.io/docs/tags/packageweekly0.5https://greeng00se.github.io/docs/tags/performanceweekly0.5https://greeng00se.github.io/docs/tags/postmortemweekly0.5https://greeng00se.github.io/docs/tags/testweekly0.5https://greeng00se.github.io/docs/tags/throughputweekly0.5https://greeng00se.github.io/docusaurusweekly0.5https://greeng00se.github.io/graspweekly0.5https://greeng00se.github.io/innodb-lockweekly0.5https://greeng00se.github.io/intellij-settingsweekly0.5https://greeng00se.github.io/java-class-fileweekly0.5https://greeng00se.github.io/java-spring-springbootweekly0.5https://greeng00se.github.io/jenkinsweekly0.5https://greeng00se.github.io/jsr-310weekly0.5https://greeng00se.github.io/kotlin-nullweekly0.5https://greeng00se.github.io/ladder-retrospectiveweekly0.5https://greeng00se.github.io/level2-interview-retrospectiveweekly0.5https://greeng00se.github.io/mock-static-methodweekly0.5https://greeng00se.github.io/mysql-lockweekly0.5https://greeng00se.github.io/order-retrospectiveweekly0.5https://greeng00se.github.io/page/10weekly0.5https://greeng00se.github.io/page/11weekly0.5https://greeng00se.github.io/page/12weekly0.5https://greeng00se.github.io/page/13weekly0.5https://greeng00se.github.io/page/14weekly0.5https://greeng00se.github.io/page/15weekly0.5https://greeng00se.github.io/page/16weekly0.5https://greeng00se.github.io/page/17weekly0.5https://greeng00se.github.io/page/18weekly0.5https://greeng00se.github.io/page/19weekly0.5https://greeng00se.github.io/page/2weekly0.5https://greeng00se.github.io/page/20weekly0.5https://greeng00se.github.io/page/21weekly0.5https://greeng00se.github.io/page/22weekly0.5https://greeng00se.github.io/page/23weekly0.5https://greeng00se.github.io/page/24weekly0.5https://greeng00se.github.io/page/25weekly0.5https://greeng00se.github.io/page/26weekly0.5https://greeng00se.github.io/page/27weekly0.5https://greeng00se.github.io/page/28weekly0.5https://greeng00se.github.io/page/29weekly0.5https://greeng00se.github.io/page/3weekly0.5https://greeng00se.github.io/page/30weekly0.5https://greeng00se.github.io/page/31weekly0.5https://greeng00se.github.io/page/32weekly0.5https://greeng00se.github.io/page/33weekly0.5https://greeng00se.github.io/page/34weekly0.5https://greeng00se.github.io/page/35weekly0.5https://greeng00se.github.io/page/36weekly0.5https://greeng00se.github.io/page/37weekly0.5https://greeng00se.github.io/page/38weekly0.5https://greeng00se.github.io/page/39weekly0.5https://greeng00se.github.io/page/4weekly0.5https://greeng00se.github.io/page/40weekly0.5https://greeng00se.github.io/page/41weekly0.5https://greeng00se.github.io/page/42weekly0.5https://greeng00se.github.io/page/43weekly0.5https://greeng00se.github.io/page/44weekly0.5https://greeng00se.github.io/page/45weekly0.5https://greeng00se.github.io/page/46weekly0.5https://greeng00se.github.io/page/5weekly0.5https://greeng00se.github.io/page/6weekly0.5https://greeng00se.github.io/page/7weekly0.5https://greeng00se.github.io/page/8weekly0.5https://greeng00se.github.io/page/9weekly0.5https://greeng00se.github.io/parameterized-testsweekly0.5https://greeng00se.github.io/performance-test-typeweekly0.5https://greeng00se.github.io/racing-car-retrospectiveweekly0.5https://greeng00se.github.io/route-image-async-with-eventweekly0.5https://greeng00se.github.io/route-image-implementationweekly0.5https://greeng00se.github.io/route-image-introweekly0.5https://greeng00se.github.io/route-image-pythonweekly0.5https://greeng00se.github.io/searchweekly0.5https://greeng00se.github.io/shopping-cart-retrospectiveweekly0.5https://greeng00se.github.io/subway-retrospectiveweekly0.5https://greeng00se.github.io/tagsweekly0.5https://greeng00se.github.io/tags/asyncweekly0.5https://greeng00se.github.io/tags/async/page/2weekly0.5https://greeng00se.github.io/tags/awtweekly0.5https://greeng00se.github.io/tags/awt/page/2weekly0.5https://greeng00se.github.io/tags/bookweekly0.5https://greeng00se.github.io/tags/book/page/2weekly0.5https://greeng00se.github.io/tags/book/page/3weekly0.5https://greeng00se.github.io/tags/classweekly0.5https://greeng00se.github.io/tags/cloudwatchweekly0.5https://greeng00se.github.io/tags/compositeweekly0.5https://greeng00se.github.io/tags/data-baseweekly0.5https://greeng00se.github.io/tags/data-base/page/2weekly0.5https://greeng00se.github.io/tags/data-base/page/3weekly0.5https://greeng00se.github.io/tags/documentationweekly0.5https://greeng00se.github.io/tags/dtoweekly0.5https://greeng00se.github.io/tags/elastic-beanstalkweekly0.5https://greeng00se.github.io/tags/eventweekly0.5https://greeng00se.github.io/tags/exceptionweekly0.5https://greeng00se.github.io/tags/graspweekly0.5https://greeng00se.github.io/tags/imageweekly0.5https://greeng00se.github.io/tags/image/page/2weekly0.5https://greeng00se.github.io/tags/image/page/3weekly0.5https://greeng00se.github.io/tags/inno-dbweekly0.5https://greeng00se.github.io/tags/intelli-jweekly0.5https://greeng00se.github.io/tags/isolationweekly0.5https://greeng00se.github.io/tags/javaweekly0.5https://greeng00se.github.io/tags/java/page/2weekly0.5https://greeng00se.github.io/tags/java/page/3weekly0.5https://greeng00se.github.io/tags/java/page/4weekly0.5https://greeng00se.github.io/tags/java/page/5weekly0.5https://greeng00se.github.io/tags/jdbcweekly0.5https://greeng00se.github.io/tags/jenkinsweekly0.5https://greeng00se.github.io/tags/kotlinweekly0.5https://greeng00se.github.io/tags/lockweekly0.5https://greeng00se.github.io/tags/lock/page/2weekly0.5https://greeng00se.github.io/tags/logweekly0.5https://greeng00se.github.io/tags/mockweekly0.5https://greeng00se.github.io/tags/mockitoweekly0.5https://greeng00se.github.io/tags/monitoringweekly0.5https://greeng00se.github.io/tags/my-sqlweekly0.5https://greeng00se.github.io/tags/mysqlweekly0.5https://greeng00se.github.io/tags/oopweekly0.5https://greeng00se.github.io/tags/patternweekly0.5https://greeng00se.github.io/tags/performance-testweekly0.5https://greeng00se.github.io/tags/pythonweekly0.5https://greeng00se.github.io/tags/replicationweekly0.5https://greeng00se.github.io/tags/retrospectiveweekly0.5https://greeng00se.github.io/tags/retrospective/page/10weekly0.5https://greeng00se.github.io/tags/retrospective/page/11weekly0.5https://greeng00se.github.io/tags/retrospective/page/12weekly0.5https://greeng00se.github.io/tags/retrospective/page/13weekly0.5https://greeng00se.github.io/tags/retrospective/page/14weekly0.5https://greeng00se.github.io/tags/retrospective/page/15weekly0.5https://greeng00se.github.io/tags/retrospective/page/16weekly0.5https://greeng00se.github.io/tags/retrospective/page/17weekly0.5https://greeng00se.github.io/tags/retrospective/page/2weekly0.5https://greeng00se.github.io/tags/retrospective/page/3weekly0.5https://greeng00se.github.io/tags/retrospective/page/4weekly0.5https://greeng00se.github.io/tags/retrospective/page/5weekly0.5https://greeng00se.github.io/tags/retrospective/page/6weekly0.5https://greeng00se.github.io/tags/retrospective/page/7weekly0.5https://greeng00se.github.io/tags/retrospective/page/8weekly0.5https://greeng00se.github.io/tags/retrospective/page/9weekly0.5https://greeng00se.github.io/tags/springweekly0.5https://greeng00se.github.io/tags/spring-bootweekly0.5https://greeng00se.github.io/tags/staticweekly0.5https://greeng00se.github.io/tags/teco-chatweekly0.5https://greeng00se.github.io/tags/teco-chat/page/2weekly0.5https://greeng00se.github.io/tags/teco-chat/page/3weekly0.5https://greeng00se.github.io/tags/testweekly0.5https://greeng00se.github.io/tags/timeweekly0.5https://greeng00se.github.io/tags/transactionweekly0.5https://greeng00se.github.io/tags/web-socketweekly0.5https://greeng00se.github.io/tags/woowahan-techcourseweekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/10weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/11weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/12weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/13weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/2weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/3weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/4weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/5weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/6weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/7weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/8weekly0.5https://greeng00se.github.io/tags/woowahan-techcourse/page/9weekly0.5https://greeng00se.github.io/tecochat-retrospective-1weekly0.5https://greeng00se.github.io/tecochat-retrospective-2weekly0.5https://greeng00se.github.io/tecochat-retrospective-3weekly0.5https://greeng00se.github.io/test-doubleweekly0.5https://greeng00se.github.io/the-essence-of-object-orientationweekly0.5https://greeng00se.github.io/tomcat-retrospectiveweekly0.5https://greeng00se.github.io/transaction-and-isolationweekly0.5https://greeng00se.github.io/web-racing-car-retrospectiveweekly0.5https://greeng00se.github.io/websocketweekly0.5https://greeng00se.github.io/woowacourse-level1-retrospectiveweekly0.5https://greeng00se.github.io/woowacourse-level2-retrospectiveweekly0.5https://greeng00se.github.io/woowacourse-level3-retrospectiveweekly0.5https://greeng00se.github.io/docsweekly0.5https://greeng00se.github.io/docs/book/getting-out-of-the-boxweekly0.5https://greeng00se.github.io/docs/culture/postmortemweekly0.5https://greeng00se.github.io/docs/design/packageweekly0.5https://greeng00se.github.io/docs/etc/communicationweekly0.5https://greeng00se.github.io/docs/etc/develop-with-springweekly0.5https://greeng00se.github.io/docs/etc/experience-and-self-questionweekly0.5https://greeng00se.github.io/docs/etc/healthful-growthweekly0.5https://greeng00se.github.io/docs/jpa/keyweekly0.5https://greeng00se.github.io/docs/linux/shellweekly0.5https://greeng00se.github.io/docs/linux/swapweekly0.5https://greeng00se.github.io/docs/monitoring/introweekly0.5https://greeng00se.github.io/docs/network/load-balancingweekly0.5https://greeng00se.github.io/docs/network/load-balancing-algorithmweekly0.5https://greeng00se.github.io/docs/nginx/commandweekly0.5https://greeng00se.github.io/docs/nginx/static-fileweekly0.5https://greeng00se.github.io/docs/performance/throughputweekly0.5https://greeng00se.github.io/docs/performance/throughput-latencyweekly0.5https://greeng00se.github.io/docs/performance/typesweekly0.5https://greeng00se.github.io/docs/test/benefitweekly0.5https://greeng00se.github.io/docs/test/firstweekly0.5https://greeng00se.github.io/docs/test/heuristicsweekly0.5https://greeng00se.github.io/docs/test/stairstepweekly0.5https://greeng00se.github.io/weekly0.5https://greeng00se.github.io/weekly0.5 \ No newline at end of file diff --git a/subway-retrospective.html b/subway-retrospective.html index fc37839ce..d1a831938 100644 --- a/subway-retrospective.html +++ b/subway-retrospective.html @@ -13,12 +13,12 @@ - - + +
-

지하철 미션 회고

· 약 8분

지하철 미션

점점 일정이 많아지는 느낌이 들면서 회고가 늦어진다.
+

지하철 미션 회고

· 약 8분

지하철 미션

점점 일정이 많아지는 느낌이 들면서 회고가 늦어진다.
지하철 미션은 밀리랑 페어를 진행했다.
간단한 CRUD만 있던 이전 미션들과 달리, 조금 복잡한 도메인 요구사항이 있었다.
이때 API, 테이블, 도메인 설계를 해야 했는데 어떤 것부터 해야 할지 고민을 많이 했다.
@@ -39,7 +39,7 @@ 또한 코딩할 때 내가 평소에 사용하는 코딩 컨벤션에 맞춰주는 것 같아서 페어 할 때 편했다!

편한 분위기

전체적으로 페어 할 때 편하게 진행했던 것 같다.
일정도 그렇고, 페어 진행할 때도 그렇고 큰 문제가 없었던 것 같아서 좋았다.
나는 과연 다른 사람들에게 편한 사람일까?

- - + + \ No newline at end of file diff --git a/tags.html b/tags.html index eb04f1dc9..2b559dee7 100644 --- a/tags.html +++ b/tags.html @@ -13,13 +13,13 @@ - - + + - - +
+ + \ No newline at end of file diff --git a/tags/async.html b/tags/async.html index 6dc8aff10..3cf395640 100644 --- a/tags/async.html +++ b/tags/async.html @@ -3,7 +3,7 @@ -"async" 태그로 연결된 1개 게시물개의 게시물이 있습니다. | GG +"async" 태그로 연결된 2개 게시물개의 게시물이 있습니다. | GG @@ -13,39 +13,17 @@ - - + +
-

"async" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 12분

이전 글

경로 이미지 생성하기 - 기술 선택
-경로 이미지 생성하기 - 구현

개요

현재 여행을 마치는 경우, 감상을 생성하는 경우 이미지 생성 요청이 이루어진다.
-경로 이미지 생성의 경우 위치 정보의 개수에 정비례하여 생성 시간이 증가한다.
-따라서 비동기로 이미지 생성 요청을 처리하여 사용자의 경험을 개선시킬 수 있다고 생각했다.

주기능의 응답속도 개선

여행 종료와 감상 생성이 주기능이고, 이미지 생성 기능은 부기능이다.
-하지만 현재 여행 종료와 감상 생성의 응답 속도가 경로 이미지 생성 시간에 영향을 받고 있다.
-경로 이미지 생성은 비동기 처리하여도 애플리케이션 사용에 문제가 되지 않는다.
-소요 시간이 1초 이상 걸리는 경우가 존재하기에 이미지 생성을 비동기 처리하고 여행 종료와 감상 생성 기능의 응답 시간을 개선하는 것이 더 중요하다.

확장성 대비

현재 10분 간격으로 위치 정보를 서버에 저장하고 있다.
-조금 더 짧은 간격으로 위치 정보를 그리는 경우 하나의 여행에 많은 위치 정보가 저장될 수밖에 없고 따라서 경로 이미지 생성에 걸리는 시간이 더 길어질 수 있다.
-따라서 추후에 더 짧은 간격으로 위치 정보를 저장하는 경우를 대비하여 이미지 생성은 비동기로 처리하는 것이 합당하다.

비동기 처리

@Async를 사용하면 간단하게 메서드를 비동기로 동작하도록 만들 수 있다.

비동기 설정

사용하기 전에 설정 파일을 하나 만들어서 EnableAsync 설정을 해야한다.
-해당 설정을 적용하면 비동기적으로 실행하려는 메서드에 @Async 애너테이션을 붙여주기만 하면 비동기로 동작한다.

AsyncConfig
@EnableAsync
@Configuration
public class AsyncConfig {
}

스프링 부트를 사용하지 않는 경우 기본적으로 비동기 처리를 할 때 매번 새로운 스레드를 생성하기 때문에 스레드 풀 설정을 따로 해줘야 한다. 하지만 스프링 부트를 사용하는 경우 ThreadPoolTaskExecutor를 따로 설정하지 않아도 기본적으로 스프링 부트가 생성을 도와준다.

In the absence of an Executor bean in the context, Spring Boot auto-configures a ThreadPoolTaskExecutor with sensible defaults that can be automatically associated to asynchronous task execution (@EnableAsync) and Spring MVC asynchronous request processing. -7.7. Task Execution and Scheduling, Spring Boot Docs

@Async 적용

이미지 생성기에 Async 애너테이션을 붙여 비동기로 동작하도록 한다.

RouteImageGenerator
@Async
public void generate(
List<Double> latitudes,
List<Double> longitudes,
List<Double> pointedLatitudes,
List<Double> pointedLongitudes,
Long tripId
) {
// 이미지 생성
RouteImageDrawer routeImageDrawer = RouteImageDrawer.from(IMAGE_SIZE);
Coordinates coordinates = Coordinates.of(latitudes, longitudes);
Coordinates pointedCoordinates = Coordinates.of(pointedLatitudes, pointedLongitudes);
drawImage(coordinates, routeImageDrawer, pointedCoordinates);

// 이미지 저장
String imageName = routeImageUploader.upload(routeImageDrawer.bufferedImage());

// 자원 할당 해제
routeImageDrawer.dispose();

// 데이터베이스 값 변경
Trip trip = tripRepository.findById(tripId)
.orElseThrow();
trip.changeRouteImageUrl(imageUrl);
tripRepository.save(trip);
}

비동기 처리시 문제점

현재 이미지 생성을 하고 저장 후, 저장 경로를 DB에 반영해야 한다.
-따라서 패키지 간 순환 참조 형태가 되며 의존성 방향이 문제가 생긴다.

이를 해결하기 위해서는 인터페이스를 사용하는 방법과 이벤트를 사용하는 방법이 있다.
-인터페이스를 사용한다면 다음과 같은 구조가 된다.

패키지 간 의존성은 해결되었지만, 이미지 경로 저장을 위해 tripId를 받아야하는 등의 논리적인 의존성은 아직 해결되지 않았다.
-따라서 이벤트를 사용하기로 했다.

이벤트 사용

스프링의 애플리케이션 이벤트를 사용하면 비즈니스 로직의 비관심사(ex. 경로 이미지 생성)을 효율적인 방법으로 처리할 수 있다.

이벤트 발행

이벤트를 사용하려면 먼저 이벤트를 발행해야 한다.
-스프링에서는 ApplicationEventPublisher 인터페이스를 사용하여 이벤트를 발행할 수 있다.
-해당 인터페이스는 내부적으로 ApplicationContext가 구현하여 이벤트를 발행한다.

TripService & TripUpdateEvent
public void updateTripById(LoginUser loginUser, Long tripId, TripUpdateRequest tripUpdateRequest) {
...

// 이벤트 발행
applicationEventPublisher.publishEvent(new TripUpdateEvent(trip.id()));
}

public record TripUpdateEvent(Long tripId) {
}

이벤트를 발행할 때 발행하는 이벤트명이 중요하다.
-이벤트를 구독하는 도메인의 행위를 담고 있는 이벤트를 발행(ex. RouteImageGenerateEvent)한다면 논리적인 의존 관계가 남아있기에 이벤트를 적절히 사용했다고 보기 어렵다.
-발행하는 이벤트명은 주기능이 어떤 행위(ex. TripUpdateEvent)를 했는지에 대한 정보가 담겨있는 이벤트명으로 발행하는 것이 중요하다.

이벤트 구독

이벤트를 구독하여 실행하는 메서드는 비동기로 처리하기 위하여 @Async 애너테이션을 적용했다.
-이벤트의 구독은 여행이 정상적으로 종료될 때 여행에 대한 정보를 가지고 경로 이미지를 생성하기 위해 @TransactionalEventListener를 사용했다.

TransactionPhase 설정

TransactionPhase을 사용하여 트랜잭션 이벤트를 어떤 단계에서 수신하고 처리할지를 지정할 수 있다.

AFTER_COMMIT(기본값): 트랜잭션이 정상적으로 커밋 되는 경우 이벤트 실행
-AFTER_ROLLBACK: 트랜잭션이 롤백되는 경우 이벤트 실행
-AFTER_COMPLETION: 트랜잭션이 커밋 또는 롤백 되었을 경우 이벤트 실행
-BEFORE_COMMIT: 트랜잭션이 커밋 되기 전 이벤트 실행

이미지 생성의 경우 트랜잭션에서 제외하기 위해 @Transactional 애너테이션을 사용하지 않았다.

TripUpdateEventHandler
@Component
public class TripUpdateEventHandler {

private final RouteImageGenerator routeImageGenerator;
private final TripRepository tripRepository;

public TripUpdateEventHandler(RouteImageGenerator routeImageGenerator, TripRepository tripRepository) {
this.routeImageGenerator = routeImageGenerator;
this.tripRepository = tripRepository;
}

@Async
@TransactionalEventListener(phase = AFTER_COMMIT)
public void handle(TripUpdateEvent tripUpdateEvent) {
Trip trip = tripRepository.getTripWithPoints(tripUpdateEvent.tripId());

String imageUrl = routeImageGenerator.generate(
trip.getLatitudes(),
trip.getLongitudes(),
trip.getPointedLatitudes(),
trip.getPointedLongitudes()
);

trip.changeRouteImageUrl(imageUrl);
tripRepository.save(trip);
}
}

이벤트를 사용함으로써 패키지 간 순환 참조 문제가 다음과 같이 해결되었다.
-또한 주기능과 부기능을 분리함으로써 경로 이미지 생성 기능에 대한 전체적인 결합도를 낮추었다.

테스트

비동기로 동작하는 메서드를 테스트하기 위해서는 아래와 같은 방법이 있다.

@ContextConfiguration(classes = TestSyncConfig.class)
@SpringBootTest
public class TripUpdateEventHandlerIntegrationTest {

...

@Test
void 여행수정_이벤트를_발생시키면_이미지를_생성_요청을_한다() {
// given
TripUpdateEvent tripUpdateEvent = new TripUpdateEvent(1L);
given(tripRepository.getTripWithPoints(tripUpdateEvent.tripId()))
.willReturn(여행());

// when
transactionTemplate.executeWithoutResult(action -> applicationEventPublisher.publishEvent(tripUpdateEvent));

// then
then(routeImageGenerator)
.should(times(1))
.generate(any(), any(), any(), any());
}
}

처음에는 테스트에서만 동기로 설정 후 검증하려고 했다.
-통합 테스트에선 트랜잭션이 정상 종료되었을 때 비동기로 이벤트를 구독하여 이미지 생성 메서드를 호출하는지 검증이 필요했기 때문에 최종적으로 Mockito.timeout 메서드를 사용하여 비동기 메서드가 통과될 때까지 대기하는 방향으로 변경했다.

결과

./time.png

위 응답 시간은 위치 정보 1000개를 기준으로 테스트한 값이다.
-응답 시간에 이미지 생성 시간이 포함되지 않아서 성능이 개선된 것을 볼 수 있다.

참고 자료

7.7. Task Execution and Scheduling, Spring Boot Docs
-Spring Events, Baeldung
-회원시스템 이벤트기반 아키텍처 구축하기

- - +

"async" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

개요

현재 트립드로우의 경로 이미지 생성 기능은 비동기로 처리되고 있다. 로그를 확인하는 도중 @Async가 적용된 메서드에서 예외가 발생하는 경우 로그가 정상적으로 출력되지 않는 문제가 발생했다.

확인해 보니 Spring의 @ControllerAdvice + @ExceptionHandler의 경우 동기 예외만 처리하고, 비동기 예외를 처리하지 않았다. 따라서 Spring에서 지원해 주는 AsyncUncaughtExceptionHandler 인터페이스를 구현해서 예외를 처리하는 클래스를 생성했다.

비동기 예외처리

AsyncExceptionHandler
@Slf4j
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

private static final String LOG_FORMAT = "[%s] %s";

@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
log.info(String.format(LOG_FORMAT, MDC.get(REQUEST_ID.key()), throwable.getMessage()), throwable);
}
}

해당 AsyncExceptionHandler의 경우 AsyncConfigurer를 구현한 Configuration 클래스를 사용하여 등록할 수 있다. getAsyncUncaughtExceptionHandler 메서드를 오버라이딩하여 이전에 생성해 준 AsyncExceptionHandler를 반환하도록 설정했다.
+이렇게 설정한다면 예외가 발생하는 경우 AsyncUncaughtExceptionHandler의 구현체인 AsyncExceptionHandler가 예외를 잡아서 처리를 해준다.

AsyncConfig
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}

MDC 정보 연동 문제

./mdc-null.png

기존 예외가 발생할 때 실행 흐름을 추적하기 위해 MDC(Mapped Diagnostic Context)를 사용한다.
+비동기 처리의 경우 별도의 스레드에서 동작하기 때문에 ThreadLocal 기반으로 동작하는 MDC의 정보를 얻어올 수 없었다.

이를 적절하게 Decorator 클래스를 설정하여 MDC의 정보를 복사해서 넘겨줄 수 있다.

다음과 같이 TaskDecorator를 구현한 클래스를 하나 생성하고, Task가 실행되기 전 MDC의 정보를 복사하도록 설정했다.

MdcTaskDecorator
public class MdcTaskDecorator implements TaskDecorator {

@Override
public Runnable decorate(final Runnable runnable) {
Map<String, String> threadContext = MDC.getCopyOfContextMap();
return () -> {
MDC.setContextMap(threadContext);
runnable.run();
};
}
}

해당 Decorator 클래스를 설정 파일에 등록해 준다.

AsyncConfig
@RequiredArgsConstructor
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

private final AsyncConfigurationProperties properties;

@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(properties.coreSize());
executor.setMaxPoolSize(properties.maxSize());
executor.setQueueCapacity(properties.queueCapacity());

executor.setTaskDecorator(new MdcTaskDecorator());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}

설정 후에는 정상적으로 MDC에 들어가 있는 UUID가 출력되는 것을 볼 수 있다.

./mdc-not-null.png

참고 자료

spring async, baeldung
+@Async will not call by @ControllerAdvice for global exception
+Spring 의 동기, 비동기, 배치 처리시 항상 context 를 유지하고 로깅하기, 강남언니

+ + \ No newline at end of file diff --git a/tags/async/page/2.html b/tags/async/page/2.html new file mode 100644 index 000000000..c05f1ccad --- /dev/null +++ b/tags/async/page/2.html @@ -0,0 +1,51 @@ + + + + + +"async" 태그로 연결된 2개 게시물개의 게시물이 있습니다. | GG + + + + + + + + + + + + + +
+

"async" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 12분

이전 글

경로 이미지 생성하기 - 기술 선택
+경로 이미지 생성하기 - 구현

개요

현재 여행을 마치는 경우, 감상을 생성하는 경우 이미지 생성 요청이 이루어진다.
+경로 이미지 생성의 경우 위치 정보의 개수에 정비례하여 생성 시간이 증가한다.
+따라서 비동기로 이미지 생성 요청을 처리하여 사용자의 경험을 개선시킬 수 있다고 생각했다.

주기능의 응답속도 개선

여행 종료와 감상 생성이 주기능이고, 이미지 생성 기능은 부기능이다.
+하지만 현재 여행 종료와 감상 생성의 응답 속도가 경로 이미지 생성 시간에 영향을 받고 있다.
+경로 이미지 생성은 비동기 처리하여도 애플리케이션 사용에 문제가 되지 않는다.
+소요 시간이 1초 이상 걸리는 경우가 존재하기에 이미지 생성을 비동기 처리하고 여행 종료와 감상 생성 기능의 응답 시간을 개선하는 것이 더 중요하다.

확장성 대비

현재 10분 간격으로 위치 정보를 서버에 저장하고 있다.
+조금 더 짧은 간격으로 위치 정보를 그리는 경우 하나의 여행에 많은 위치 정보가 저장될 수밖에 없고 따라서 경로 이미지 생성에 걸리는 시간이 더 길어질 수 있다.
+따라서 추후에 더 짧은 간격으로 위치 정보를 저장하는 경우를 대비하여 이미지 생성은 비동기로 처리하는 것이 합당하다.

비동기 처리

@Async를 사용하면 간단하게 메서드를 비동기로 동작하도록 만들 수 있다.

비동기 설정

사용하기 전에 설정 파일을 하나 만들어서 EnableAsync 설정을 해야한다.
+해당 설정을 적용하면 비동기적으로 실행하려는 메서드에 @Async 애너테이션을 붙여주기만 하면 비동기로 동작한다.

AsyncConfig
@EnableAsync
@Configuration
public class AsyncConfig {
}

스프링 부트를 사용하지 않는 경우 기본적으로 비동기 처리를 할 때 매번 새로운 스레드를 생성하기 때문에 스레드 풀 설정을 따로 해줘야 한다. 하지만 스프링 부트를 사용하는 경우 ThreadPoolTaskExecutor를 따로 설정하지 않아도 기본적으로 스프링 부트가 생성을 도와준다.

In the absence of an Executor bean in the context, Spring Boot auto-configures a ThreadPoolTaskExecutor with sensible defaults that can be automatically associated to asynchronous task execution (@EnableAsync) and Spring MVC asynchronous request processing. +7.7. Task Execution and Scheduling, Spring Boot Docs

@Async 적용

이미지 생성기에 Async 애너테이션을 붙여 비동기로 동작하도록 한다.

RouteImageGenerator
@Async
public void generate(
List<Double> latitudes,
List<Double> longitudes,
List<Double> pointedLatitudes,
List<Double> pointedLongitudes,
Long tripId
) {
// 이미지 생성
RouteImageDrawer routeImageDrawer = RouteImageDrawer.from(IMAGE_SIZE);
Coordinates coordinates = Coordinates.of(latitudes, longitudes);
Coordinates pointedCoordinates = Coordinates.of(pointedLatitudes, pointedLongitudes);
drawImage(coordinates, routeImageDrawer, pointedCoordinates);

// 이미지 저장
String imageName = routeImageUploader.upload(routeImageDrawer.bufferedImage());

// 자원 할당 해제
routeImageDrawer.dispose();

// 데이터베이스 값 변경
Trip trip = tripRepository.findById(tripId)
.orElseThrow();
trip.changeRouteImageUrl(imageUrl);
tripRepository.save(trip);
}

비동기 처리시 문제점

현재 이미지 생성을 하고 저장 후, 저장 경로를 DB에 반영해야 한다.
+따라서 패키지 간 순환 참조 형태가 되며 의존성 방향이 문제가 생긴다.

이를 해결하기 위해서는 인터페이스를 사용하는 방법과 이벤트를 사용하는 방법이 있다.
+인터페이스를 사용한다면 다음과 같은 구조가 된다.

패키지 간 의존성은 해결되었지만, 이미지 경로 저장을 위해 tripId를 받아야하는 등의 논리적인 의존성은 아직 해결되지 않았다.
+따라서 이벤트를 사용하기로 했다.

이벤트 사용

스프링의 애플리케이션 이벤트를 사용하면 비즈니스 로직의 비관심사(ex. 경로 이미지 생성)을 효율적인 방법으로 처리할 수 있다.

이벤트 발행

이벤트를 사용하려면 먼저 이벤트를 발행해야 한다.
+스프링에서는 ApplicationEventPublisher 인터페이스를 사용하여 이벤트를 발행할 수 있다.
+해당 인터페이스는 내부적으로 ApplicationContext가 구현하여 이벤트를 발행한다.

TripService & TripUpdateEvent
public void updateTripById(LoginUser loginUser, Long tripId, TripUpdateRequest tripUpdateRequest) {
...

// 이벤트 발행
applicationEventPublisher.publishEvent(new TripUpdateEvent(trip.id()));
}

public record TripUpdateEvent(Long tripId) {
}

이벤트를 발행할 때 발행하는 이벤트명이 중요하다.
+이벤트를 구독하는 도메인의 행위를 담고 있는 이벤트를 발행(ex. RouteImageGenerateEvent)한다면 논리적인 의존 관계가 남아있기에 이벤트를 적절히 사용했다고 보기 어렵다.
+발행하는 이벤트명은 주기능이 어떤 행위(ex. TripUpdateEvent)를 했는지에 대한 정보가 담겨있는 이벤트명으로 발행하는 것이 중요하다.

이벤트 구독

이벤트를 구독하여 실행하는 메서드는 비동기로 처리하기 위하여 @Async 애너테이션을 적용했다.
+이벤트의 구독은 여행이 정상적으로 종료될 때 여행에 대한 정보를 가지고 경로 이미지를 생성하기 위해 @TransactionalEventListener를 사용했다.

TransactionPhase 설정

TransactionPhase을 사용하여 트랜잭션 이벤트를 어떤 단계에서 수신하고 처리할지를 지정할 수 있다.

AFTER_COMMIT(기본값): 트랜잭션이 정상적으로 커밋 되는 경우 이벤트 실행
+AFTER_ROLLBACK: 트랜잭션이 롤백되는 경우 이벤트 실행
+AFTER_COMPLETION: 트랜잭션이 커밋 또는 롤백 되었을 경우 이벤트 실행
+BEFORE_COMMIT: 트랜잭션이 커밋 되기 전 이벤트 실행

이미지 생성의 경우 트랜잭션에서 제외하기 위해 @Transactional 애너테이션을 사용하지 않았다.

TripUpdateEventHandler
@Component
public class TripUpdateEventHandler {

private final RouteImageGenerator routeImageGenerator;
private final TripRepository tripRepository;

public TripUpdateEventHandler(RouteImageGenerator routeImageGenerator, TripRepository tripRepository) {
this.routeImageGenerator = routeImageGenerator;
this.tripRepository = tripRepository;
}

@Async
@TransactionalEventListener(phase = AFTER_COMMIT)
public void handle(TripUpdateEvent tripUpdateEvent) {
Trip trip = tripRepository.getTripWithPoints(tripUpdateEvent.tripId());

String imageUrl = routeImageGenerator.generate(
trip.getLatitudes(),
trip.getLongitudes(),
trip.getPointedLatitudes(),
trip.getPointedLongitudes()
);

trip.changeRouteImageUrl(imageUrl);
tripRepository.save(trip);
}
}

이벤트를 사용함으로써 패키지 간 순환 참조 문제가 다음과 같이 해결되었다.
+또한 주기능과 부기능을 분리함으로써 경로 이미지 생성 기능에 대한 전체적인 결합도를 낮추었다.

테스트

비동기로 동작하는 메서드를 테스트하기 위해서는 아래와 같은 방법이 있다.

@ContextConfiguration(classes = TestSyncConfig.class)
@SpringBootTest
public class TripUpdateEventHandlerIntegrationTest {

...

@Test
void 여행수정_이벤트를_발생시키면_이미지를_생성_요청을_한다() {
// given
TripUpdateEvent tripUpdateEvent = new TripUpdateEvent(1L);
given(tripRepository.getTripWithPoints(tripUpdateEvent.tripId()))
.willReturn(여행());

// when
transactionTemplate.executeWithoutResult(action -> applicationEventPublisher.publishEvent(tripUpdateEvent));

// then
then(routeImageGenerator)
.should(times(1))
.generate(any(), any(), any(), any());
}
}

처음에는 테스트에서만 동기로 설정 후 검증하려고 했다.
+통합 테스트에선 트랜잭션이 정상 종료되었을 때 비동기로 이벤트를 구독하여 이미지 생성 메서드를 호출하는지 검증이 필요했기 때문에 최종적으로 Mockito.timeout 메서드를 사용하여 비동기 메서드가 통과될 때까지 대기하는 방향으로 변경했다.

결과

./time.png

위 응답 시간은 위치 정보 1000개를 기준으로 테스트한 값이다.
+응답 시간에 이미지 생성 시간이 포함되지 않아서 성능이 개선된 것을 볼 수 있다.

참고 자료

7.7. Task Execution and Scheduling, Spring Boot Docs
+Spring Events, Baeldung
+회원시스템 이벤트기반 아키텍처 구축하기

+ + + + \ No newline at end of file diff --git a/tags/awt.html b/tags/awt.html index 57063de1c..4cc420dad 100644 --- a/tags/awt.html +++ b/tags/awt.html @@ -13,12 +13,12 @@ - - + +
-

"awt" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 12분

개요

여행에 대한 경로를 보여주기 위해 경로 이미지를 생성하는 기능을 추가했다.
+

"awt" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 12분

개요

여행에 대한 경로를 보여주기 위해 경로 이미지를 생성하는 기능을 추가했다.
경로 이미지에 대한 요구사항 및 기술 선택에 대한 내용은 링크에 있다.

구현 결과

./result.png

예시 데이터는 다음과 같다.
서울역(점) → 신사역 → 노량진역 → 홍대입구역 → 종로3가역 → 옥수역 → 구로역(점) → 신림역 → 발산역

예시 데이터
List<Double> x = List.of(
126.97094933811682, 127.02154822802501, 126.94218991864345, 126.92402556641424,
126.99265358592287, 127.01779856076462, 126.88474839801178, 126.92900751277035, 126.83930056313639
);
List<Double> y = List.of(
37.55302829553499, 37.51619698970427, 37.51294119442773, 37.5565933969331,
37.57034879708931, 37.54027238225762, 37.50129417536773, 37.48258811529137, 37.557607696911184
);
List<Double> xPoints = List.of(126.97094933811682, 126.88474839801178);
List<Double> yPoints = List.of(37.55302829553499, 37.50129417536773);

IMAGE_SIZE & ROUTE_SIZE

RouteImageGenerator.java
private static final int IMAGE_SIZE = 800;
private static final int ROUTE_SIZE = 600;

코드를 보면 IMAGE_SIZE와 ROUTE_SIZE가 있다.
IMAGE_SIZE는 말 그대로 이미지의 width와 height를 의미한다.
@@ -29,7 +29,7 @@ BufferedImage를 사용할 때 왼쪽 상단의 좌표 (0, 0) 기준으로 아래로 내려갈수록 y 값이 커지고, 오른쪽으로 갈 수록 x 값이 커진다.

./800.png

따라서 최종적으로 이미지를 생성하기 위한 값을 다음과 같이 구했다.

x 값 → 계산한 offset 그대로 더한다.
y 값 → imageSize(800)에서 y + offset 값을 뺀다.

RouteImageDrawer(실제 이미지에 경로를 그려주는 클래스)

BufferedImage, Graphics2D를 필드로 가지고 있는 클래스다.
그림을 그리기 위해 설정한 상수들이 존재한다.

RouteImageDrawer.java
// RGB에 각각 8비트씩 할당한 값을 24비트 트루컬러라 부른다.
// 해당 설정은 24비트 + 8비트(alpha, 투명도)를 추가한 32비트 이미지 타입이다.
// 이를 RGBA라고 부른다.
private static final int IMAGE_TYPE = BufferedImage.TYPE_INT_ARGB;
// 배경 투명색
private static final Color TRANSPARENT = new Color(0, 0, 0, 0);
// 경로를 위한 STROKE
private static final int LINE_STROKE_WIDTH = 7;
private static final Stroke LINE_STROKE = new BasicStroke(LINE_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);
// 위치 점을 위한 STROKE
private static final int POINT_STROKE_WIDTH = 20;
private static final Stroke POINT_STROKE = new BasicStroke(POINT_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);
// 안티앨리어싱 등 화질 개선을 위한 설정
private static final Map<Object, Object> renderingHints = Map.of(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON,
RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY,
RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC
);

RouteImageDrawer 클래스에는 다음 세 개의 인터페이스가 존재한다.

  • drawLine: 선을 그린다.
  • drawPoint: 점을 찍는다.
  • dispose: 자원 할당을 해제한다.

dispose의 경우 내부에서 생성된 graphics2D에 대한 자원 할당을 해제하는 메서드인 graphics2D.dispose를 호출한다.

이미지 생성 Flow

1. 이미지 생성 준비

2. 선 그리기 요청

3. 위치 점 그리기 요청

4. 업로드 요청

전체 Flow

- - + + \ No newline at end of file diff --git a/tags/awt/page/2.html b/tags/awt/page/2.html index aa226d456..ff25e3e41 100644 --- a/tags/awt/page/2.html +++ b/tags/awt/page/2.html @@ -13,19 +13,19 @@ - - + +
-

"awt" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

./route.png

이미지 생성의 책임

위 와이어 프레임에서 여행 히스토리여행에 대한 감상을 위한 경로 이미지의 경우 네이버 지도를 사용하여 해당 기능을 구현할 수 없으니 당연히 맵 API에서 제공하는 도형 그리기 API(네이버 맵 API 기준 Polyline)를 사용할 수 없다.
+

"awt" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

./route.png

이미지 생성의 책임

위 와이어 프레임에서 여행 히스토리여행에 대한 감상을 위한 경로 이미지의 경우 네이버 지도를 사용하여 해당 기능을 구현할 수 없으니 당연히 맵 API에서 제공하는 도형 그리기 API(네이버 맵 API 기준 Polyline)를 사용할 수 없다.
따라서 이미지를 직접 생성하거나, 클라이언트에서 직접 위경도를 이용하여 그려야 한다.

해당 요구사항을 해결하기 위해서는 다음과 같은 기능을 가진 라이브러리가 필요하다.

  • 이미지 생성
  • 선과 점 표현
  • 투명한 배경색

현재 클라이언트의 바쁜 일정과 기능 구현에 있어 약간의 연산이 들어간다는 부분을 고려하여 백엔드에서 이미지를 생성하기로 결정을 내렸다.

고려한 기술

백엔드에서 이미지 생성을 하기 위해 다음과 같은 라이브러리 또는 기술들을 확인해 보았다.

  • Python의 Matplotlib
  • AWT(Abstract Window Toolkit) [최종 선택]
  • 이미지 처리 라이브러리 및 Java에서 내부적으로 Matplotlib 사용할 수 있는 라이브러리 (원하는 기능 없음)
  • Java Swing, Java FX (단순한 선 그리기 + 점 찍기라 불필요)

Python & Matplotlib

데이터 시각화 라이브러리
이미지 생성 및 로컬에 저장까지 걸리는 시간: 0.2초

  • 코드가 간단해서 유지 보수성이 좋다.
  • AWS Lambda 같은 서버리스 컴퓨팅 서비스나 FastAPI와 같은 웹 프레임워크로 추가적인 API를 구현해야 한다.
  • Spring Boot에서 추가적인 API 호출을 해야하고, 확장성과 비동기 처리 등 고려 해야 할 부분이 많다.

Java AWT 이외의 라이브러리

Python이 아닌 Java에서의 라이브러리도 고려를 해봤지만 요구사항에 적합하지 않거나, 적은 요구사항에 비해 무거운 라이브러리들이 많아서 제외했다.

라이브러리설명제외 이유
SwingAWT 이후에 나온 GUI 라이브러리, 네이티브 UI를 사용하지 않고 모든 운영체제 상에서 동일한 UI를 가지도록 함요구사항에 비해 무겁고 복잡도가 높음
JavaFXSwing 이후에 나온 GUI 라이브러리, 3차원 그래픽을 지원함요구사항에 비해 무겁고 복잡도가 높음
simple-java-plotAWT로 구현된 플로팅 라이브러리AWT 기반이긴 하지만 직접 AWT를 사용하는 것에 비해 메리트가 없음, 커스텀 설정 기능이 없음
matplotlib4jMatplotlib를 Java에서 사용할 수 있게 하는 라이브러리내부적으로 파이썬 사용하기에 무거움, 배경 투명화 기능 없음

Java & AWT(Abstract Window Toolkit)

그래픽과 이미지를 그리기 위한 도구
이미지 생성 및 로컬에 저장까지 걸리는 시간: 1.75초

  • 플로팅 라이브러리를 사용하는 것보다 구현의 난이도가 다소 존재한다.
  • 이미지 생성 시간이 다소 소요되기 때문에 빠른 응답 반환을 위해 비동기 처리를 고려할 수 있을 것 같다.
  • 추가적인 api 호출을 하지 않아도 된다.

기술 선택

AWT의 경우 Matplotlib에 비해 구현의 난이도가 다소 있고, 이미지 생성 시간이 더 많이 걸리는 단점이 있다.
하지만 추가적인 api 호출을 하지 않아도 되는 부분, Python을 사용하는 경우 추가적인 웹 프레임워크의 학습 비용을 고려하여 AWT를 사용하기로 결정했다.

유지 보수

AWT라는 생소한 기술을 사용하기 때문에 유지 보수성을 위해 팀원들과 공유하는 것이 중요하다고 생각했다.
따라서 다음과 같은 방법으로 공유하기로 했다.

  1. 코드 리뷰와 PR을 통해 작성한 AWT 코드에 대한 설명 및 리뷰 받는다.
  2. AWT를 사용한 부분을 문서화하여 공유한다.

레벨 3를 마무리하며 내용 추가

기술 선택을 하기 위한 실행 시간 측정에 오류가 있었다.
AWT를 사용하는 부분에서 애플리케이션 실행 시간을 제외하면 파이썬과 비슷한 시간안에 이미지를 생성할 수 있었다.

- - + + \ No newline at end of file diff --git a/tags/book.html b/tags/book.html index f1aa3d0e6..6c0e599d8 100644 --- a/tags/book.html +++ b/tags/book.html @@ -13,12 +13,12 @@ - - + +
-

"Book" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

책 정보

상자 밖에 있는 사람
+

"Book" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

책 정보

상자 밖에 있는 사람
아빈저연구소

자기기만과 자기배반

책에서는 자기기만과 자기배반에 대한 내용을 다룬다.

  • 자기기만: 자신의 문제를 인정하지 않는 것
  • 자기배반: 다른 사람을 위해 무언가 해야만 한다는 생각을 반하는 행위

자기배반을 한다면 자기기만 상태가 된다.
자기기만 상태에 빠지는 것을 책에서는 상자 안에 들어간다고 표현한다.

읽고 나서

최근에 읽은 책 중 가장 마음이 불편했다.
그렇기에 더더욱 나에게 필요한 내용이 담겨있었다.

살면서 많은 선택의 순간이 존재했고, 그 순간마다 자기배반을 택하는 경우가 많았다.
@@ -48,7 +48,7 @@ 우리가 그들과 진정으로 함께 소통하기 전까지는 우리는 그들의 가치를 잘 모릅니다.
우리의 위대함이란 다른 사람들의 위대한 점을 발견해 주는 것에 있습니다.
p.280

- - + + \ No newline at end of file diff --git a/tags/book/page/2.html b/tags/book/page/2.html index 3b96f26e8..499fb92fd 100644 --- a/tags/book/page/2.html +++ b/tags/book/page/2.html @@ -13,12 +13,12 @@ - - + +
-

"Book" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

책 정보

객체지향의 사실과 오해
+

"Book" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

책 정보

객체지향의 사실과 오해
조영호

읽고 나서

조영호님의 오브젝트를 읽고 나서 다시 한 번 읽어보았다.
아직 이해가 안되는 부분이 많지만, 그래도 항상 새로움을 느낀다.
더할 나위 없이 휼륭한 객체지향 책이고, 조금 더 공부하고 다시 읽어봐야될 것 같다.

커피 전문점, 지하철 노선도, 이상한 나라의 엘리스를 예시로 든 설명이 너무 좋았고
@@ -38,7 +38,7 @@ ’어떤 행위(What)’를 수행할 것인지 결정한 후 ‘누가(who)’ 그 행위를 수행할 것인지 결정해야 한다. 여기서 ‘어떤 행위’가 바로 메시지다. p.158

- - + + \ No newline at end of file diff --git a/tags/book/page/3.html b/tags/book/page/3.html index eed21a713..e0bb45407 100644 --- a/tags/book/page/3.html +++ b/tags/book/page/3.html @@ -13,18 +13,18 @@ - - + +
-

"Book" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

책 정보

글, 우리도 잘 쓸 수 있습니다.
+

"Book" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

책 정보

글, 우리도 잘 쓸 수 있습니다.
박솔미

읽고 나서

저자의 경험과 함께 글쓰기에 대한 가벼운 조언이 담겨있어 가볍게 읽기 좋았다.
글을 잘 작성해 보고 싶을 때 적용해 볼 수 있는 정보가 많아서 도움이 되었다.

우아한 테크코스의 프리코스를 진행할 때 후기를 작성하고 나면 항상 글이 딱딱하다는 느낌을 받았다.
다른 지원자들의 읽기 편하고, 밝은 느낌을 주는 글을 보면 부러운 마음을 가지기도 했다.
이 책을 읽었으니 2023년에는 조금 더 글을 잘 적어보려고 한다.

밑줄 친 문장들

문장이 심심하고 지루하다면 내용을 일목요연하게 정리했고, 글의 의도도 삐뚤지 않고, 단어도 적절한 것으로 골랐는데… 그런데도 어딘가가 심심하고 지루하다면? 축축 처지고 따분하다면? 말꼬리를 모조리 ‘~다’로 통일한 건 아닌지 점검해 보세요.

말꼬리를 잘 갖고 놀아야 합니다. 문장의 마지막 글자를 매번 다르게 고쳐쓰는 것만으로도 글에 활기를 더할 수 있죠. 때론 문장을 다 마치지 않고, 단어로만 끝맺는 것도 방법. 문장과 문장 사이에 쉼표가 들어서며 글 전체에 활기가 돌게 돼요. 문장의 길이도 다채로워지는 덕분에 덤으로 얻게 되는 것도 있습니다. 바로, 글의 리듬.

이전 문장에서 끝난 글자로, 다음 문장을 끝맺지 않기. 한두 문단마다 단어 수준의 아주 짧은 문장 배치하기.

글의 진짜 이유, 글의 진짜 목적, 글의 진짜 대상을 찾으려고 애썼습니다. 지금처럼 틀을 떠올린다거나, 눈치를 본다거나, 정치적인 셈도 하지 않았어요.

제목은 짧게, 보기 쉽게, 읽기 쉽게, 발음이 비슷하게, 순서를 바꿔서

글을 마지막으로 다듬을 때, 노래에 가까워질 방법은 없을지 고민해봅니다. 감히 가 닿을 수 없는 목표이겠지만, 할 수 있는 최소한의 리듬이라도 붙여주고 싶어요.

여는 말과 마지막 말에 작정하고 마음을 담는 연습을 해봅시다. 글의 어느 구석이라도 뻔한 글자는 남기지 않겠노라 다짐하며 써보는 겁니다. 나만이 가진 유일한 메시지에 집중하면서요. 그럼 생각이 달라지고, 고르는 단어도 달라지고, 남긴 문장도 달라져요. 결국에는 글을 쓴 사람인 나 자신도 남달라질 겁니다.

맞춤법은 중요합니다. 하지만 맞춤법보다 더 중요한 건 거기에 담긴 마음입니다. 내 마음을 글에 담아 실어 보내기 전, 맞춤법을 점검하는 이유 역시 그겁니다. 오직 내 마음이 남에게 읽히는 동안 방해가 되지 않기를 바라기 때문이죠. 내가 쓴 글도, 남이 쓴 글도. 언제나 그 안에 담긴 마음이 먼저입니다.

글을 쓴다고 글이 완성되는 게 아니에요. 글과 닮은 모습으로 살 때, 글은 비로소 완성됩니다.

- - + + \ No newline at end of file diff --git a/tags/class.html b/tags/class.html index 1c9a30a2d..66bf753c8 100644 --- a/tags/class.html +++ b/tags/class.html @@ -13,12 +13,12 @@ - - + +
-

"Class" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

클래스 파일

자바 소스코드가 실행이 되려면 자바 컴파일러(javac)를 통해 소스코드를 클래스파일로 변환해야 한다.
+

"Class" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

클래스 파일

자바 소스코드가 실행이 되려면 자바 컴파일러(javac)를 통해 소스코드를 클래스파일로 변환해야 한다.
컴파일된 클래스파일은 어떤 구조로 되어있을까?

클래스 파일의 데이터 형식

8비트 바이트의 스트림으로 구성된다.
16비트 및 32비트의 데이터는 각각 2개, 4개의 연속된 8비트를 읽어서 구성된다.
멀티바이트의 경우 항상 big endian 순서로 저장된다.

u1 → unsigned 1byte
@@ -36,7 +36,7 @@ Class file in Java, File Format
java se11 Class 파일 형식, Oracle
java se17 Class 파일 형식, Oracle

- - + + \ No newline at end of file diff --git a/tags/cloudwatch.html b/tags/cloudwatch.html index 367e288f7..78ddfe394 100644 --- a/tags/cloudwatch.html +++ b/tags/cloudwatch.html @@ -13,12 +13,12 @@ - - + +
-

"cloudwatch" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

CloudWatch

AWS 리소스와 애플리케이션의 지표와 로그에 대한 모니터링을 제공하는 서비스다.
+

"cloudwatch" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

CloudWatch

AWS 리소스와 애플리케이션의 지표와 로그에 대한 모니터링을 제공하는 서비스다.
지표를 감시하여 알림을 보내는 기능도 제공한다.
프리티어를 사용하지 않는 경우 대시보드당 3$/M 의 비용이 청구되고, 지표나 로그의 양에 따라 비용이 추가적으로 청구된다.
요금 정보에 대한 자세한 정보는 다음 링크에서 확인할 수 있다.

CloudWatch Metrics

기본적으로 5분마다 지표에 대한 정보가 수집된다.
@@ -38,7 +38,7 @@ 서버에 CloudWatch 에이전트 설치 및 실행
CloudWatch Agent를 Parameter Store에서 관리해 보기
CloudWatch에이전트 구성 파일

- - + + \ No newline at end of file diff --git a/tags/composite.html b/tags/composite.html index 70b720e39..c6cb013d5 100644 --- a/tags/composite.html +++ b/tags/composite.html @@ -13,12 +13,12 @@ - - + +
-

"Composite" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

요구사항

지하철 미션에는 다음과 같은 요구사항이 있었다.

  • 거리별 추가 요금 정책
  • 노선별 추가 요금 정책
  • 연령별 요금 할인 정책

인터페이스 사용

요금 정책은 다음과 같이 인터페이스로 표현할 수 있다.
+

"Composite" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

요구사항

지하철 미션에는 다음과 같은 요구사항이 있었다.

  • 거리별 추가 요금 정책
  • 노선별 추가 요금 정책
  • 연령별 요금 할인 정책

인터페이스 사용

요금 정책은 다음과 같이 인터페이스로 표현할 수 있다.
요금을 계산하는 메서드는 최단 경로 계산의 결과, 사용자의 정보, 요금을 받아 요금을 계산한다.

public interface FarePolicy {
int calculate(Path path, Passenger passenger, int fare);
}

public class BaseFarePolicy implements FarePolicy { ... }
public class DistanceFarePolicy implements FarePolicy { ... }
public class AgeDiscountFarePolicy implements FarePolicy { ... }

composite1

모든 요금 정책을 포함하는 새로운 요금 정책 만들기

나머지 구현체를 모두 가지고 있는 하나의 구현체를 만들었다.
이 또한 FarePolicy를 구현한 형태가 되고, 필드로는 나머지 구현체들을 가지고 있다.

public class SubwayFarePolicy implements FarePolicy {

private final List<FarePolicy> farePolicies;

public SubwayFarePolicy(final List<FarePolicy> farePolicies) {
this.farePolicies = farePolicies;
}

@Override
public int calculate(final Path path, final Passenger passenger, final int fare) {
int calculatedFare = fare;
for (FarePolicy farePolicy : farePolicies) {
calculatedFare = farePolicy.calculate(path, passenger, calculatedFare);
}
return calculatedFare;
}
}

따라서 그림으로 본다면 다음과 같은 구조가 된다.

composite2

정책의 순서

지하철 요구사항은 순서가 중요했다.
금액의 총합을 구하고, 그 후에 할인 정책이 들어가야했다.
@@ -31,7 +31,7 @@ 패턴을 맹목적으로 사용해서는 안되고, 현재의 요구사항에 따라 패턴을 유동적으로 수정해가면서 적용하는 것이 좋다.
항상 트레이드오프를 생각하자!

참고 자료

컴포지트 패턴, GoF의 디자인 패턴
디자인 패턴과 프레임워크, 오브젝트

- - + + \ No newline at end of file diff --git a/tags/data-base.html b/tags/data-base.html index 9d7ce2e45..1ae8a48ec 100644 --- a/tags/data-base.html +++ b/tags/data-base.html @@ -13,12 +13,12 @@ - - + +
-

"DataBase" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

InnoDB 스토리지 엔진의 잠금

MySQL에서 제공하는 잠금과 별개로 스토리지 엔진 내부에서 로우 단위의 잠금을 지원한다.
+

"DataBase" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

InnoDB 스토리지 엔진의 잠금

MySQL에서 제공하는 잠금과 별개로 스토리지 엔진 내부에서 로우 단위의 잠금을 지원한다.
보통 명시적으로 잠금을 사용하는 경우는 드물고, 격리 수준에 따라 묵시적으로 잠금이 사용된다.

동시성 제어 방식에는 낙관적인 방식과 비관적인 방식이 있다.
InnoDB는 기본적으로 MVCC(다중 버전 동시성 제어)를 통해 낙관적인 방식을 사용하고 락을 통해 특정 상황에서 비관적인 방식을 사용한다.

낙관적 동시성 제어(OCC, Optimistic concurrency control)

트랜잭션이 서로 충돌하지 않는다고 가정하는 방식

비관적 동시성 제어(PCC, Pessimistic Concurrency Control)

트랜잭션이 충돌하는 가정하에 잠금을 거는 방식
일반적으로 Shared Lock, Exclusive Lock을 통해 이를 구현한다.

Shared & Exclusive Locks

InnoDB는 로우 단위의 잠금을 수행할 때 공유 잠금과 배타적 잠금을 사용한다.

공유 잠금(S, shared lock)

데이터 조회를 위한 락, 읽기 잠금(read lock)으로도 불린다.
@@ -38,7 +38,7 @@ MySQL Innodb Locks, cecil1018
MySQL 8.0 InnoDB Locks, MySQL
Locks Set by Different SQL Statements in InnoDB, MySQL

- - + + \ No newline at end of file diff --git a/tags/data-base/page/2.html b/tags/data-base/page/2.html index 93cc27f4e..fe2e875e3 100644 --- a/tags/data-base/page/2.html +++ b/tags/data-base/page/2.html @@ -13,12 +13,12 @@ - - + +
-

"DataBase" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

MySQL 엔진의 잠금

MySQL에서의 락은 스토리지 엔진 레벨과, MySQL 엔진 레벨로 나눌 수 있다.
+

"DataBase" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

MySQL 엔진의 잠금

MySQL에서의 락은 스토리지 엔진 레벨과, MySQL 엔진 레벨로 나눌 수 있다.
MySQL 엔진 레벨의 잠금은 모든 스토리지 엔진에 영향을 미친다.

글로벌 락(Global lock)

MySQL에서 제공하는 잠금 중 가장 넓은 범위를 가지고 있는 잠금이다.

  • 영향을 미치는 범위는 해당 서버 전체이다.
  • 작업 대상 테이블, 데이터베이스 상관 없이 동일하게 영향을 받는다.

한 세션에서 글로벌 락을 획득하면 해제 될 때 까지 조회를 제외한 대부분의 명령이 대기 상태가 된다.
데이터베이스에 존재하는 MyISAM이나 MEMORY 테이블에 대해 일관된 백업을 받아야할 때 사용한다.
InnoDB 스토리지 엔진에서는 백업 시 조금 더 가벼운 백업 락을 사용할 수 있다.

-- GLOBAL LOCK
FLUSH TABLES WITH READ LOCK;
-- UNLOCK
UNLOCK TABLES;

-- BACKUP LOCK
LOCK INSTANCE FOR BACKUP;
-- UNLOCK
UNLOCK INSTANCE;
- - + + \ No newline at end of file diff --git a/tags/data-base/page/3.html b/tags/data-base/page/3.html index e6775ed00..bf9b4c497 100644 --- a/tags/data-base/page/3.html +++ b/tags/data-base/page/3.html @@ -13,12 +13,12 @@ - - + +
-

"DataBase" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 10분

트랜잭션(Transaction)

데이터베이스에서 논리적 기능을 수행하기 위한 작업의 단위를 말한다.
+

"DataBase" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 10분

트랜잭션(Transaction)

데이터베이스에서 논리적 기능을 수행하기 위한 작업의 단위를 말한다.
트랜잭션은 작업의 완전성과 데이터의 정합성을 보장해 준다.
논리적인 작업 셋을 완벽하게 처리하거나, 오류 시 작업의 일부만 적용되는 현상을 막아준다.

트랜잭션의 속성(ACID)

원자성(Atomicity): 트랜잭션 내에서 실행된 작업들은 모두 성공하거나, 실패해야 한다.
일관성(Consistency): 트랜잭션이 수행되기 전과 후에 데이터베이스가 일관된 상태를 유지해야 한다.
@@ -42,7 +42,7 @@ 예) A가 레코드를 여러 번 조회하던 중 B가 레코드를 변경하여 A가 조회한 값이 달라지는 경우

팬텀 리드(Phantom read, Phantom row)

한 트랜잭션 내에서 동일한 쿼리 수행시, 수행 결과가 다른 현상
예) A가 레코드를 조회하고 B가 레코드를 추가하여 A가 다시 조회할 때 존재하지 않은 레코드가 조회되는 경우

참고 자료

Real My SQL 8.0 - 5장 트랜잭션과 잠금, 백은빈, 이성욱
Isolation Level, MySQL

- - + + \ No newline at end of file diff --git a/tags/documentation.html b/tags/documentation.html index 158a66c17..606ce5404 100644 --- a/tags/documentation.html +++ b/tags/documentation.html @@ -13,12 +13,12 @@ - - + +
-

"Documentation" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 11분

팀 블로그 또는 문서화를 위해 Docusaurus를 사용하는 방법을 정리하려고 한다.

설치

공식 홈페이지에 들어가서 최신 버전을 설치한다.

yarn create docusaurus

배포

배포 안내 문서
+

"Documentation" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 11분

팀 블로그 또는 문서화를 위해 Docusaurus를 사용하는 방법을 정리하려고 한다.

설치

공식 홈페이지에 들어가서 최신 버전을 설치한다.

yarn create docusaurus

배포

배포 안내 문서
netlify나 vercel 같은 서버리스 플랫폼을 추천하고 있고, 간단하고, 빠른 시간 안에 배포를 할 수 있다.
이 글에서는 github pages를 이용해서 배포하는 방법을 설명한다.

레포지토리 생성

github pages를 이용하려면 예시와 같이 username.github.io 형태의 레포지토리를 생성해야 한다.
이때 organization을 사용하는 경우 organization.github.io 형태의 레포지토리를 생성해서 사용한다.

설정 파일 수정

docusaurus.config
module.exports = {
// ...
url: 'https://greeng00se.github.io',
baseUrl: '/',
projectName: 'greeng00se.github.io',
organizationName: 'greeng00se',
trailingSlash: false,
// ...
};

토큰 설정

github action을 위해 배포용 토큰을 하나 생성하여 생성한 레포지토리에 Repository secrets으로 설정한다.
@@ -39,7 +39,7 @@ jq가 설치되어 있지 않으면 mac 기준 brew를 이용해서 설치할 수 있다.

brew install jq

다음 명령어를 이용하여 .env와 config.json을 이용하여 크롤링을 한다.

docker run -it --env-file=.env -e "CONFIG=$(cat ./config.json | jq -r tostring)" algolia/docsearch-scraper

docusaurus 설정

전에 확인한 APP ID, Search-Only API KEY, IndexName을 이용하여 docusaurus.config 파일에 설정한다.

docusaurus.config
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
...
algolia: {
appId: 'MVIU5UEMOM', // Application ID
apiKey: 'b68f378013817d9a190df88cdde226a0', // Search-Only API Key
indexName: 'teco', // config.json에 설정한 인덱스명
contextualSearch: true,
},
})

부가 설정

화면 상단 Github Icon

파일 최하단에 아래 css 구문을 추가한다.

/src/css/custom.css
.header-github-link:hover {
opacity: 0.6;
}

.header-github-link:before {
content: '';
width: 24px;
height: 24px;
display: flex;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}

html[data-theme='dark'] .header-github-link:before {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}

themeconfig -> navbar에 github link를 설정한다.

docusaurus.config
navbar: {
title: 'HELLO',
items: [
{
href: 'https://github.com/greeng00se',
position: 'right',
className: 'header-github-link',
'aria-label': 'GitHub repository',
},
],
},

코드블럭

java나 kotlin의 경우 기본적으로 하이라이팅을 지원해 주지 않는다.
prism 설정을 아래와 같이 변경해 준다.

docusaurus.config
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
additionalLanguages: ['java', 'kotlin'],
}

mermaid

mermaid를 사용하려면 @docusaurus/theme-mermaid 를 설치해야 한다.

yarn add @docusaurus/theme-mermaid

설치 후 아래와 같이 설정을 추가한다.

docusaurus.config
const config = {
...
markdown: {
mermaid: true,
},
themes: [
'@docusaurus/theme-mermaid'
],
};

themeConfig에서 mermaid의 테마를 지정할 수 있다.

docusaurus.config
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
...
mermaid: {
theme: {
light: 'neutral',
dark: 'dark'
},
},
}),

국제화 설정

국제화 설정을 한다면 Older Entries 형태의 설명이 다음 페이지 로 변경된다.
설정파일에서 i18n에 있는 로케일 설정을 ko로 변경하면 된다.

docusaurus.config
i18n: {
defaultLocale: "ko",
locales: ["ko"],
},

블로그 글 author

팀원 별로 문서를 관리한다면 다음과 같이 어떤 팀원이 글을 작성했는지 설정해야 한다.

author

authors.yml 파일을 이용하여 사용자에 대한 기본 설정을 할 수 있다.

/blog/authors.yml
herb:
name: 허브
title: Backend
url: https://github.com/greeng00se
image_url: https://github.com/greeng00se.png

mallang:
name: 말랑
title: Backend
url: https://github.com/shin-mallang
image_url: https://github.com/shin-mallang.png

블로그 글을 작성할 때 다음과 같이 authors에 넣어주기만 하면 된다.

---
slug: 1
title: Hello World
authors: [herb, mallang]
tags: [hello, docusaurus]
---

첫 번째 문서 내용
- - + + \ No newline at end of file diff --git a/tags/dto.html b/tags/dto.html index cb1c005a9..c0c9a9672 100644 --- a/tags/dto.html +++ b/tags/dto.html @@ -13,12 +13,12 @@ - - + +
-

"DTO" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

장바구니 미션에서는 상품 추가와 상품 수정에 대한 요구사항이 있었다.
+

"DTO" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

장바구니 미션에서는 상품 추가와 상품 수정에 대한 요구사항이 있었다.
요청에 담긴 Body를 통해 전달받은 값을 DTO로 매핑하여 추가와 수정을 했다.

장바구니 미션에서의 상품 추가 및 수정

중복1

클래스명을 제외하고 필드와 검증로직 그 외 모든게 같은 DTO를 보며 중복이라고 생각했다.
하지만 반대로 용도가 다르기 때문에 중복이 아니라고 생각하기도 했다.
위 경우는 중복일까? 중복이 아닐까?

이 부분에 대해서 다음과 같은 리뷰를 받았다.

ProductSaveRequestProductUpdateRequest가 완전히 동일한데, 재사용할 수 없을까? 라는 리뷰를 남겼었어요. 사실 생성과 수정은 서로 달라질 개연성이 높아서 미리 분리해놓는 게 더 좋은 방법이긴 한데, 그래도 중복은 싫어서 저도 요즘 이런저런 방법들을 시도해보는 중 입니다. 허브는 이 부분에 대해 어떤 생각을 가지고 있을지 궁금하네요 ㅎㅎ

질문에 대해 아래와 같이 답변을 했다.

저장과 수정할 때 필요한 필드값이 동일하여 현재 구조에서는 하나로 사용해도 된다고 생각을 하지만, 말씀해주신대로 요구사항이 변경된다면 달라질 가능성이 높다고 판단하였습니다!

중복과 우발적 중복

로버트 마틴님이 집필하신 클린 아키텍처는 아래와 같이 중복을 여러가지 종류로 나누어 설명하고 있다.

  • 진짜 중복: 한 인스턴스가 변경되면, 동일한 변경을 그 인스턴스의 모드 복사본에 반드시 적용해야 한다.
  • 거짓된 중복, 우발적 중복: 중복으로 보이는 두 코드 영역이 각자의 경로로 발전한다면, 즉 서로 다른 속도와 다른 이유로 변경된다면 이 두 코드는 진짜 중복이 아니다.

추가와 수정은 초기에는 중복으로 보이지만 초기 생성시에만 기입하는 데이터들이 추가되거나, 시간이 지나면서 서로 달라질 가능성이 높아진다. @@ -32,7 +32,7 @@ 상황에 맞춰 적재적소에 의존 역전을 이용해보는 것도 좋을 것 같다.

참고 자료

클린 아키텍처 16장 독립성, 로버트 C. 마틴
https://techblog.woowahan.com/2647/
https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/

- - + + \ No newline at end of file diff --git a/tags/elastic-beanstalk.html b/tags/elastic-beanstalk.html index 2869dbf77..c31c2e60f 100644 --- a/tags/elastic-beanstalk.html +++ b/tags/elastic-beanstalk.html @@ -13,12 +13,12 @@ - - + +
-

"Elastic Beanstalk" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

설정 환경

소프트웨어 이미지: Amazon Linux 2023 AMI
+

"Elastic Beanstalk" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기
- - + + \ No newline at end of file diff --git a/tags/event.html b/tags/event.html index 9ede6c852..925718feb 100644 --- a/tags/event.html +++ b/tags/event.html @@ -13,12 +13,12 @@ - - + +
-

"event" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 12분

이전 글

경로 이미지 생성하기 - 기술 선택
+

"event" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 12분

이전 글

경로 이미지 생성하기 - 기술 선택
경로 이미지 생성하기 - 구현

개요

현재 여행을 마치는 경우, 감상을 생성하는 경우 이미지 생성 요청이 이루어진다.
경로 이미지 생성의 경우 위치 정보의 개수에 정비례하여 생성 시간이 증가한다.
따라서 비동기로 이미지 생성 요청을 처리하여 사용자의 경험을 개선시킬 수 있다고 생각했다.

주기능의 응답속도 개선

여행 종료와 감상 생성이 주기능이고, 이미지 생성 기능은 부기능이다.
@@ -45,7 +45,7 @@ 응답 시간에 이미지 생성 시간이 포함되지 않아서 성능이 개선된 것을 볼 수 있다.

참고 자료

7.7. Task Execution and Scheduling, Spring Boot Docs
Spring Events, Baeldung
회원시스템 이벤트기반 아키텍처 구축하기

- - + + \ No newline at end of file diff --git a/tags/exception.html b/tags/exception.html new file mode 100644 index 000000000..69895abeb --- /dev/null +++ b/tags/exception.html @@ -0,0 +1,29 @@ + + + + + +"exception" 태그로 연결된 1개 게시물개의 게시물이 있습니다. | GG + + + + + + + + + + + + + +
+

"exception" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

개요

현재 트립드로우의 경로 이미지 생성 기능은 비동기로 처리되고 있다. 로그를 확인하는 도중 @Async가 적용된 메서드에서 예외가 발생하는 경우 로그가 정상적으로 출력되지 않는 문제가 발생했다.

확인해 보니 Spring의 @ControllerAdvice + @ExceptionHandler의 경우 동기 예외만 처리하고, 비동기 예외를 처리하지 않았다. 따라서 Spring에서 지원해 주는 AsyncUncaughtExceptionHandler 인터페이스를 구현해서 예외를 처리하는 클래스를 생성했다.

비동기 예외처리

AsyncExceptionHandler
@Slf4j
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

private static final String LOG_FORMAT = "[%s] %s";

@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
log.info(String.format(LOG_FORMAT, MDC.get(REQUEST_ID.key()), throwable.getMessage()), throwable);
}
}

해당 AsyncExceptionHandler의 경우 AsyncConfigurer를 구현한 Configuration 클래스를 사용하여 등록할 수 있다. getAsyncUncaughtExceptionHandler 메서드를 오버라이딩하여 이전에 생성해 준 AsyncExceptionHandler를 반환하도록 설정했다.
+이렇게 설정한다면 예외가 발생하는 경우 AsyncUncaughtExceptionHandler의 구현체인 AsyncExceptionHandler가 예외를 잡아서 처리를 해준다.

AsyncConfig
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}

MDC 정보 연동 문제

./mdc-null.png

기존 예외가 발생할 때 실행 흐름을 추적하기 위해 MDC(Mapped Diagnostic Context)를 사용한다.
+비동기 처리의 경우 별도의 스레드에서 동작하기 때문에 ThreadLocal 기반으로 동작하는 MDC의 정보를 얻어올 수 없었다.

이를 적절하게 Decorator 클래스를 설정하여 MDC의 정보를 복사해서 넘겨줄 수 있다.

다음과 같이 TaskDecorator를 구현한 클래스를 하나 생성하고, Task가 실행되기 전 MDC의 정보를 복사하도록 설정했다.

MdcTaskDecorator
public class MdcTaskDecorator implements TaskDecorator {

@Override
public Runnable decorate(final Runnable runnable) {
Map<String, String> threadContext = MDC.getCopyOfContextMap();
return () -> {
MDC.setContextMap(threadContext);
runnable.run();
};
}
}

해당 Decorator 클래스를 설정 파일에 등록해 준다.

AsyncConfig
@RequiredArgsConstructor
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

private final AsyncConfigurationProperties properties;

@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(properties.coreSize());
executor.setMaxPoolSize(properties.maxSize());
executor.setQueueCapacity(properties.queueCapacity());

executor.setTaskDecorator(new MdcTaskDecorator());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}

설정 후에는 정상적으로 MDC에 들어가 있는 UUID가 출력되는 것을 볼 수 있다.

./mdc-not-null.png

참고 자료

spring async, baeldung
+@Async will not call by @ControllerAdvice for global exception
+Spring 의 동기, 비동기, 배치 처리시 항상 context 를 유지하고 로깅하기, 강남언니

+ + + + \ No newline at end of file diff --git a/tags/grasp.html b/tags/grasp.html index 72d5ed132..46b15bcab 100644 --- a/tags/grasp.html +++ b/tags/grasp.html @@ -13,15 +13,15 @@ - - + +
-

"GRASP" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 9분

GRASP(General Responsibility Assignment Software Pattern)

크레이그 라만의 Applying UML and Patterns이라는 책에서 나온 책임 할당을 위한 패턴

각 패턴마다 Solution과 Problem로 구성되어 있다.

정보 전문가 패턴(Information Expert)

Q: 객체에 책임을 할당하는 기본 원칙은 무엇인가?

A: 책임을 수행하는 데 필요한 정보를 가진 클래스(정보 전문가)에게 책임을 할당한다.

정보와 행동을 가까운 곳에 위치시키기 때문에 캡슐화를 유지할 수 있다.

필요한 정보를 가진 객체들로 책임이 분산된다.

창조자 패턴(Creator)

Q: 누가 객체 A를 생성하는가?

A: 다음의 조건을 최대한 많이 만족하는 객체에게 객체 생성 책임을 할당해야 한다.

  • B가 A 객체를 포함 또는 참조한다.
  • B가 A 객체를 기록한다.
  • B가 A 객체를 긴밀하게 사용한다.
  • B가 A 객체의 초기값을 가지고 있다.

생성 예정인 객체와 연관되어 있는 객체가 생성 책임을 가지고 있게 된다면, 이미 해당 객체와 결합되어있다고 생각할 수 있다. 따라서 전체적인 결합도를 낮게 유지할 수 있다.

낮은 결합도 패턴(Low Coupling)

Q: 의존성을 낮추고 변화의 영향을 줄이며 재사용성을 증가시키는 방법은?

A: 전체적인 결합이 낮게 유지되도록 책임을 할당해야 한다.

결합도(Coupling) +

"GRASP" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 9분

GRASP(General Responsibility Assignment Software Pattern)

크레이그 라만의 Applying UML and Patterns이라는 책에서 나온 책임 할당을 위한 패턴

각 패턴마다 Solution과 Problem로 구성되어 있다.

정보 전문가 패턴(Information Expert)

Q: 객체에 책임을 할당하는 기본 원칙은 무엇인가?

A: 책임을 수행하는 데 필요한 정보를 가진 클래스(정보 전문가)에게 책임을 할당한다.

정보와 행동을 가까운 곳에 위치시키기 때문에 캡슐화를 유지할 수 있다.

필요한 정보를 가진 객체들로 책임이 분산된다.

창조자 패턴(Creator)

Q: 누가 객체 A를 생성하는가?

A: 다음의 조건을 최대한 많이 만족하는 객체에게 객체 생성 책임을 할당해야 한다.

  • B가 A 객체를 포함 또는 참조한다.
  • B가 A 객체를 기록한다.
  • B가 A 객체를 긴밀하게 사용한다.
  • B가 A 객체의 초기값을 가지고 있다.

생성 예정인 객체와 연관되어 있는 객체가 생성 책임을 가지고 있게 된다면, 이미 해당 객체와 결합되어있다고 생각할 수 있다. 따라서 전체적인 결합도를 낮게 유지할 수 있다.

낮은 결합도 패턴(Low Coupling)

Q: 의존성을 낮추고 변화의 영향을 줄이며 재사용성을 증가시키는 방법은?

A: 전체적인 결합이 낮게 유지되도록 책임을 할당해야 한다.

결합도(Coupling) 객체 사이의 의존성이 과한 경우 결합도가 높다고 말한다.

  • 오브젝트 p.17

결합도를 낮춘다면 다음과 같은 이점이 있다.

  • 다른 구성 요소의 변화에 영향을 받지 않는다.
  • 재사용이 편리해진다.
  • 해당 클래스에 대한 이해가 쉬워진다. (의존하는 클래스가 적기 때문에)

높은 응집도 패턴(High Cohesion)

Q. 객체를 관리하기 쉽게 하려면 어떻게 해야 할까?

A. 높은 응집도를 유지할 수 있게 책임을 할당해야 한다.

응집도(Cohesion) 연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에게 위임하는 객체를 가리켜 응집도가 높다고 말한다.

  • 오브젝트 p.26

변경의 이유에 따라 클래스를 분리한다면 응집도를 높일 수 있고, 응집도가 높아진다면 다음과 같은 이점이 있다.

  • 해당 클래스에 대한 이해가 쉬워진다. (할당된 책임만을 수행하고 있기 때문에)
  • 유지보수가 쉬워진다.
  • 낮은 결합도 또한 지원한다.
  • 응집도가 높은 클래스는 특정한 목적에 사용할 수 있기 때문에 재사용하기 좋다.

컨트롤러 패턴(Controller)

Q. 사용자의 요청을 처리하는 것은 누가 담당해야 하는가?

A. 사용자의 요청을 처리하는 Controller 객체를 만들어서 사용해야 한다.

어떤 서브시스템이 존재한다고 가정할 때

  • 직접적으로 객체에 접근하여 프로그램을 사용한다면 결합도가 상승한다.
  • 서브 시스템에 들어오는 요청을 처리해주는 컨트롤러가 있다면 사용하는 입장에서는 해당 컨트롤러만 알면 된다.
  • 만약 서브 시스템의 변경이 생겼을 때 외부에 미치는 영향도 줄어든다.

다형성 패턴(Polymorphism)

Q. 객체의 타입에 따라 행동이 바뀐다면 책임을 어떻게 할당해야 할까?

A. OOP가 지원하는 다형성을 적극적으로 활용한다. (인터페이스를 두고 행동에 대한 부분을 구현)

객체의 종류에 따라 분기하는 조건문이 아닌 다형성을 사용하는 것이 좋은 방법이다.

새로운 타입이 추가되었을 때 조건문을 사용한다면 기존의 조건문을 수정해야 하지만 다형성을 활용하면 쉽게 확장할 수 있다.

변경 보호 패턴(Protected Variations)

Q. 어떻게 하면 변경이 다른 요소에 영향을 미치지 않도록 방지할 수 있을까?

A. 변화가 예상되는 지점을 식별하고, 주위에 안정된 인터페이스를 형성하도록 책임을 할당해야 한다.

간접 참조 패턴(Indirection)

Q. 두 객체 사이의 직접적인 연결을 피하고 싶다면 어떻게 해야 할까?

A. 두 객체 사이에 또 다른 객체를 두어 직접적인 연결을 피할 수 있다.

중재자 패턴을 사용하여 두 객체 사이에 또 하나의 객체를 추가하여 복잡한 관계를 단순화할 수 있다.

중간에 인터페이스를 둔다면 변경 보호 패턴(Protected Variations)에 해당된다.

순수한 가공물 패턴(Pure Fabrication)

Q. 책임을 할당한 도메인 객체가 Low Coupling, High Cohesion, 재사용성 등의 목적을 위반한다면 어떻게 해야 할까?

A. 도메인 개념을 포함하지 않는 클래스를 하나 만들고 매우 응집된 책임을 할당할 수 있다.

행동을 추가할 때, 해당 책임을 수행할 도메인 개념이 존재하지 않는다면 도메인과 무관한 인공적인 객체를 만든다음 해당 객체에게 책임을 할당한다.

객체가 데이터베이스에 저장해야 할 값을 가지고 있다고, 정보 전문가 패턴을 적용하여 데이터베이스에 저장하라는 책임을 가지라고 하지 않는다.

예) 상점과 고객 클래스가 있고 서로 다른 통화를 사용하고 있다고 가정

  • 서로 다른 통화를 사용하고 있기 때문에 거래를 하려면 환전을 해야한다.
  • 두 클래스 다 환전에 대한 책임을 부여하기 애매하다면 환전을 책임하는 클래스를 추가하고 사용할 수 있다.

참고 자료

오브젝트 5장. 책임 할당하기, 조영호

Applying UML and Patterns Chapter 16, Chapter 21 GRASP, Craig Larman

GRASP, 한빛 네트워크

- - + + \ No newline at end of file diff --git a/tags/image.html b/tags/image.html index 6e2c279d7..294527fe0 100644 --- a/tags/image.html +++ b/tags/image.html @@ -13,12 +13,12 @@ - - + +
-

"image" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 12분

개요

여행에 대한 경로를 보여주기 위해 경로 이미지를 생성하는 기능을 추가했다.
+

"image" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 12분

개요

여행에 대한 경로를 보여주기 위해 경로 이미지를 생성하는 기능을 추가했다.
경로 이미지에 대한 요구사항 및 기술 선택에 대한 내용은 링크에 있다.

구현 결과

./result.png

예시 데이터는 다음과 같다.
서울역(점) → 신사역 → 노량진역 → 홍대입구역 → 종로3가역 → 옥수역 → 구로역(점) → 신림역 → 발산역

예시 데이터
List<Double> x = List.of(
126.97094933811682, 127.02154822802501, 126.94218991864345, 126.92402556641424,
126.99265358592287, 127.01779856076462, 126.88474839801178, 126.92900751277035, 126.83930056313639
);
List<Double> y = List.of(
37.55302829553499, 37.51619698970427, 37.51294119442773, 37.5565933969331,
37.57034879708931, 37.54027238225762, 37.50129417536773, 37.48258811529137, 37.557607696911184
);
List<Double> xPoints = List.of(126.97094933811682, 126.88474839801178);
List<Double> yPoints = List.of(37.55302829553499, 37.50129417536773);

IMAGE_SIZE & ROUTE_SIZE

RouteImageGenerator.java
private static final int IMAGE_SIZE = 800;
private static final int ROUTE_SIZE = 600;

코드를 보면 IMAGE_SIZE와 ROUTE_SIZE가 있다.
IMAGE_SIZE는 말 그대로 이미지의 width와 height를 의미한다.
@@ -29,7 +29,7 @@ BufferedImage를 사용할 때 왼쪽 상단의 좌표 (0, 0) 기준으로 아래로 내려갈수록 y 값이 커지고, 오른쪽으로 갈 수록 x 값이 커진다.

./800.png

따라서 최종적으로 이미지를 생성하기 위한 값을 다음과 같이 구했다.

x 값 → 계산한 offset 그대로 더한다.
y 값 → imageSize(800)에서 y + offset 값을 뺀다.

RouteImageDrawer(실제 이미지에 경로를 그려주는 클래스)

BufferedImage, Graphics2D를 필드로 가지고 있는 클래스다.
그림을 그리기 위해 설정한 상수들이 존재한다.

RouteImageDrawer.java
// RGB에 각각 8비트씩 할당한 값을 24비트 트루컬러라 부른다.
// 해당 설정은 24비트 + 8비트(alpha, 투명도)를 추가한 32비트 이미지 타입이다.
// 이를 RGBA라고 부른다.
private static final int IMAGE_TYPE = BufferedImage.TYPE_INT_ARGB;
// 배경 투명색
private static final Color TRANSPARENT = new Color(0, 0, 0, 0);
// 경로를 위한 STROKE
private static final int LINE_STROKE_WIDTH = 7;
private static final Stroke LINE_STROKE = new BasicStroke(LINE_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);
// 위치 점을 위한 STROKE
private static final int POINT_STROKE_WIDTH = 20;
private static final Stroke POINT_STROKE = new BasicStroke(POINT_STROKE_WIDTH, CAP_ROUND, JOIN_ROUND);
// 안티앨리어싱 등 화질 개선을 위한 설정
private static final Map<Object, Object> renderingHints = Map.of(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON,
RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY,
RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC
);

RouteImageDrawer 클래스에는 다음 세 개의 인터페이스가 존재한다.

  • drawLine: 선을 그린다.
  • drawPoint: 점을 찍는다.
  • dispose: 자원 할당을 해제한다.

dispose의 경우 내부에서 생성된 graphics2D에 대한 자원 할당을 해제하는 메서드인 graphics2D.dispose를 호출한다.

이미지 생성 Flow

1. 이미지 생성 준비

2. 선 그리기 요청

3. 위치 점 그리기 요청

4. 업로드 요청

전체 Flow

- - + + \ No newline at end of file diff --git a/tags/image/page/2.html b/tags/image/page/2.html index 59c7bdc5c..45bb40f18 100644 --- a/tags/image/page/2.html +++ b/tags/image/page/2.html @@ -13,12 +13,12 @@ - - + +
-

"image" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 7분

개요

이전에 기술 구현 가능 여부를 조사하면서 파이썬을 사용한 내용을 정리한 내용이다.

사용 기술

언어: Python 3.10
+

"image" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 7분

개요

이전에 기술 구현 가능 여부를 조사하면서 파이썬을 사용한 내용을 정리한 내용이다.

사용 기술

언어: Python 3.10
이미지 생성: matplotlib
서비스: AWS Lambda, AWS API Gateway
이미지 저장 및 URL: AWS S3, AWS CloudFront

플로우는 다음과 같다.

요구사항

./route.png

우측 상단의 경로 이미지를 생성하려고 한다.
@@ -39,7 +39,7 @@ Python Lambda 함수에 대한 .zip 파일 아카이브 작업
No module named 'numpy.core._multiarray_umath'
사례별로 알아본 안전한 S3 사용 가이드

- - + + \ No newline at end of file diff --git a/tags/image/page/3.html b/tags/image/page/3.html index 8e8bf698d..e62b71511 100644 --- a/tags/image/page/3.html +++ b/tags/image/page/3.html @@ -13,19 +13,19 @@ - - + +
-

"image" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

./route.png

이미지 생성의 책임

위 와이어 프레임에서 여행 히스토리여행에 대한 감상을 위한 경로 이미지의 경우 네이버 지도를 사용하여 해당 기능을 구현할 수 없으니 당연히 맵 API에서 제공하는 도형 그리기 API(네이버 맵 API 기준 Polyline)를 사용할 수 없다.
+

"image" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

./route.png

이미지 생성의 책임

위 와이어 프레임에서 여행 히스토리여행에 대한 감상을 위한 경로 이미지의 경우 네이버 지도를 사용하여 해당 기능을 구현할 수 없으니 당연히 맵 API에서 제공하는 도형 그리기 API(네이버 맵 API 기준 Polyline)를 사용할 수 없다.
따라서 이미지를 직접 생성하거나, 클라이언트에서 직접 위경도를 이용하여 그려야 한다.

해당 요구사항을 해결하기 위해서는 다음과 같은 기능을 가진 라이브러리가 필요하다.

  • 이미지 생성
  • 선과 점 표현
  • 투명한 배경색

현재 클라이언트의 바쁜 일정과 기능 구현에 있어 약간의 연산이 들어간다는 부분을 고려하여 백엔드에서 이미지를 생성하기로 결정을 내렸다.

고려한 기술

백엔드에서 이미지 생성을 하기 위해 다음과 같은 라이브러리 또는 기술들을 확인해 보았다.

  • Python의 Matplotlib
  • AWT(Abstract Window Toolkit) [최종 선택]
  • 이미지 처리 라이브러리 및 Java에서 내부적으로 Matplotlib 사용할 수 있는 라이브러리 (원하는 기능 없음)
  • Java Swing, Java FX (단순한 선 그리기 + 점 찍기라 불필요)

Python & Matplotlib

데이터 시각화 라이브러리
이미지 생성 및 로컬에 저장까지 걸리는 시간: 0.2초

  • 코드가 간단해서 유지 보수성이 좋다.
  • AWS Lambda 같은 서버리스 컴퓨팅 서비스나 FastAPI와 같은 웹 프레임워크로 추가적인 API를 구현해야 한다.
  • Spring Boot에서 추가적인 API 호출을 해야하고, 확장성과 비동기 처리 등 고려 해야 할 부분이 많다.

Java AWT 이외의 라이브러리

Python이 아닌 Java에서의 라이브러리도 고려를 해봤지만 요구사항에 적합하지 않거나, 적은 요구사항에 비해 무거운 라이브러리들이 많아서 제외했다.

라이브러리설명제외 이유
SwingAWT 이후에 나온 GUI 라이브러리, 네이티브 UI를 사용하지 않고 모든 운영체제 상에서 동일한 UI를 가지도록 함요구사항에 비해 무겁고 복잡도가 높음
JavaFXSwing 이후에 나온 GUI 라이브러리, 3차원 그래픽을 지원함요구사항에 비해 무겁고 복잡도가 높음
simple-java-plotAWT로 구현된 플로팅 라이브러리AWT 기반이긴 하지만 직접 AWT를 사용하는 것에 비해 메리트가 없음, 커스텀 설정 기능이 없음
matplotlib4jMatplotlib를 Java에서 사용할 수 있게 하는 라이브러리내부적으로 파이썬 사용하기에 무거움, 배경 투명화 기능 없음

Java & AWT(Abstract Window Toolkit)

그래픽과 이미지를 그리기 위한 도구
이미지 생성 및 로컬에 저장까지 걸리는 시간: 1.75초

  • 플로팅 라이브러리를 사용하는 것보다 구현의 난이도가 다소 존재한다.
  • 이미지 생성 시간이 다소 소요되기 때문에 빠른 응답 반환을 위해 비동기 처리를 고려할 수 있을 것 같다.
  • 추가적인 api 호출을 하지 않아도 된다.

기술 선택

AWT의 경우 Matplotlib에 비해 구현의 난이도가 다소 있고, 이미지 생성 시간이 더 많이 걸리는 단점이 있다.
하지만 추가적인 api 호출을 하지 않아도 되는 부분, Python을 사용하는 경우 추가적인 웹 프레임워크의 학습 비용을 고려하여 AWT를 사용하기로 결정했다.

유지 보수

AWT라는 생소한 기술을 사용하기 때문에 유지 보수성을 위해 팀원들과 공유하는 것이 중요하다고 생각했다.
따라서 다음과 같은 방법으로 공유하기로 했다.

  1. 코드 리뷰와 PR을 통해 작성한 AWT 코드에 대한 설명 및 리뷰 받는다.
  2. AWT를 사용한 부분을 문서화하여 공유한다.

레벨 3를 마무리하며 내용 추가

기술 선택을 하기 위한 실행 시간 측정에 오류가 있었다.
AWT를 사용하는 부분에서 애플리케이션 실행 시간을 제외하면 파이썬과 비슷한 시간안에 이미지를 생성할 수 있었다.

- - + + \ No newline at end of file diff --git a/tags/inno-db.html b/tags/inno-db.html index c5bf6813c..f264956cd 100644 --- a/tags/inno-db.html +++ b/tags/inno-db.html @@ -13,12 +13,12 @@ - - + +
-

"InnoDB" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

InnoDB 스토리지 엔진의 잠금

MySQL에서 제공하는 잠금과 별개로 스토리지 엔진 내부에서 로우 단위의 잠금을 지원한다.
+

"InnoDB" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

InnoDB 스토리지 엔진의 잠금

MySQL에서 제공하는 잠금과 별개로 스토리지 엔진 내부에서 로우 단위의 잠금을 지원한다.
보통 명시적으로 잠금을 사용하는 경우는 드물고, 격리 수준에 따라 묵시적으로 잠금이 사용된다.

동시성 제어 방식에는 낙관적인 방식과 비관적인 방식이 있다.
InnoDB는 기본적으로 MVCC(다중 버전 동시성 제어)를 통해 낙관적인 방식을 사용하고 락을 통해 특정 상황에서 비관적인 방식을 사용한다.

낙관적 동시성 제어(OCC, Optimistic concurrency control)

트랜잭션이 서로 충돌하지 않는다고 가정하는 방식

비관적 동시성 제어(PCC, Pessimistic Concurrency Control)

트랜잭션이 충돌하는 가정하에 잠금을 거는 방식
일반적으로 Shared Lock, Exclusive Lock을 통해 이를 구현한다.

Shared & Exclusive Locks

InnoDB는 로우 단위의 잠금을 수행할 때 공유 잠금과 배타적 잠금을 사용한다.

공유 잠금(S, shared lock)

데이터 조회를 위한 락, 읽기 잠금(read lock)으로도 불린다.
@@ -38,7 +38,7 @@ MySQL Innodb Locks, cecil1018
MySQL 8.0 InnoDB Locks, MySQL
Locks Set by Different SQL Statements in InnoDB, MySQL

- - + + \ No newline at end of file diff --git a/tags/intelli-j.html b/tags/intelli-j.html index 123641daa..b693cc6e6 100644 --- a/tags/intelli-j.html +++ b/tags/intelli-j.html @@ -13,13 +13,13 @@ - - + +
-

"IntelliJ" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 1분

Import 자동 적용

Prefrences > Editor > General > Auto Import > Add unambiguous imports on the fly

auto-import

저장시 동작

Prefrences > Tools > Actions on Save

actions-on-save

Reformat Code: Code Reformmating

Optimize imports: 사용하지 않는 Import 제거

Rearrange: Code Style > Arrangement 설정 기반 코드 재정렬

메소드 추출, 변수 추출시 final 적용

Prefrences > Editor > Code Style > Java > Code Generation > Final Modifier

final-modifier

- - +

"IntelliJ" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 1분

Import 자동 적용

Prefrences > Editor > General > Auto Import > Add unambiguous imports on the fly

auto-import

저장시 동작

Prefrences > Tools > Actions on Save

actions-on-save

Reformat Code: Code Reformmating

Optimize imports: 사용하지 않는 Import 제거

Rearrange: Code Style > Arrangement 설정 기반 코드 재정렬

메소드 추출, 변수 추출시 final 적용

Prefrences > Editor > Code Style > Java > Code Generation > Final Modifier

final-modifier

+ + \ No newline at end of file diff --git a/tags/isolation.html b/tags/isolation.html index 760137693..34d94837d 100644 --- a/tags/isolation.html +++ b/tags/isolation.html @@ -13,12 +13,12 @@ - - + +
-

"Isolation" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 10분

트랜잭션(Transaction)

데이터베이스에서 논리적 기능을 수행하기 위한 작업의 단위를 말한다.
+

"Isolation" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 10분

트랜잭션(Transaction)

데이터베이스에서 논리적 기능을 수행하기 위한 작업의 단위를 말한다.
트랜잭션은 작업의 완전성과 데이터의 정합성을 보장해 준다.
논리적인 작업 셋을 완벽하게 처리하거나, 오류 시 작업의 일부만 적용되는 현상을 막아준다.

트랜잭션의 속성(ACID)

원자성(Atomicity): 트랜잭션 내에서 실행된 작업들은 모두 성공하거나, 실패해야 한다.
일관성(Consistency): 트랜잭션이 수행되기 전과 후에 데이터베이스가 일관된 상태를 유지해야 한다.
@@ -42,7 +42,7 @@ 예) A가 레코드를 여러 번 조회하던 중 B가 레코드를 변경하여 A가 조회한 값이 달라지는 경우

팬텀 리드(Phantom read, Phantom row)

한 트랜잭션 내에서 동일한 쿼리 수행시, 수행 결과가 다른 현상
예) A가 레코드를 조회하고 B가 레코드를 추가하여 A가 다시 조회할 때 존재하지 않은 레코드가 조회되는 경우

참고 자료

Real My SQL 8.0 - 5장 트랜잭션과 잠금, 백은빈, 이성욱
Isolation Level, MySQL

- - + + \ No newline at end of file diff --git a/tags/java.html b/tags/java.html index 83a418594..655c01929 100644 --- a/tags/java.html +++ b/tags/java.html @@ -13,12 +13,12 @@ - - + +
-

"Java" 태그로 연결된 5개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

자바 17, 스프링 6.0, 스프링 부트 3.1

팀 프로젝트를 진행하면서 스프링 부트 3.1을 사용하게 되었다.
+

"Java" 태그로 연결된 5개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

자바 17, 스프링 6.0, 스프링 부트 3.1

팀 프로젝트를 진행하면서 스프링 부트 3.1을 사용하게 되었다.
2.7 버전을 사용할 수도 있었지만 LTS 기간과 취약점 패치로 인한 버전업 등을 고려했을 때 3.1과 자바 17을 사용하는 것이 더 효율적이라고 판단했다.

자바 변경 사항

우아한테크코스 레벨 2까지는 자바 11을 사용했었다.
따라서 자바 11부터 자바 17까지의 변경사항을 정식 릴리즈 기준으로 정리해보려고 한다.

Switch Expressions(Java 14)

Java 14에서는 기존의 Switch 문을 간결하게 작성할 수 있는 Switch 식이 추가되었다.

enum RESULT {
WIN, LOSE, DRAW
}

RESULT result = RESULT.WIN;

int prize = switch (result) {
case WIN -> 10_000_000;
case LOSE, DRAW -> 5_000_000;
default -> 0;
};

주요 특징은 다음과 같다.

  • -> 연산자를 이용하여 각 case에 대한 결과를 바로 반환할 수 있다.
  • case를 콤마(,)로 연결하여 하나의 case에 여러 값을 지정할 수 있다.
  • break 문이 필요 없다.
  • default 블록을 통해 기본 값을 지정할 수 있다.

Text Block(Java 15)

Java 15에는 새로운 문자열 표현방식이 추가되었다.
긴 문자열을 + 연산자의 도움 없이 가독성있게 작성할 수 있다.

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
@Query("""
SELECT p FROM Post p
WHERE p.title LIKE %:keyword%
OR p.content LIKE %:keyword%
""")
List<Post> findPostsByTitleOrContentContainingKeyword(String keyword);
}

NPE 메시지(Java 15)

String name = null;
name.chars();

/**
# before
java.lang.NullPointerException
at com.example.DiscountPolicyTest.test(NullPointerExceptionTest.java:61)

# after
Cannot invoke "String.chars()" because "name" is null
java.lang.NullPointerException: Cannot invoke "String.chars()" because "name" is null
*/

Record(Java 16)

Lombok의 @Data, kotlin의 data 클래스와 유사한 기능을 제공한다.
@@ -34,7 +34,7 @@ What's New in Spring Framework 6.x
Spring Boot 3.0 Release Notes
Spring Boot 3.1 Release Notes

- - + + \ No newline at end of file diff --git a/tags/java/page/2.html b/tags/java/page/2.html index 8b0b9f19f..f8b62a282 100644 --- a/tags/java/page/2.html +++ b/tags/java/page/2.html @@ -13,12 +13,12 @@ - - + +
-

"Java" 태그로 연결된 5개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

클래스 파일

자바 소스코드가 실행이 되려면 자바 컴파일러(javac)를 통해 소스코드를 클래스파일로 변환해야 한다.
+

"Java" 태그로 연결된 5개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

클래스 파일

자바 소스코드가 실행이 되려면 자바 컴파일러(javac)를 통해 소스코드를 클래스파일로 변환해야 한다.
컴파일된 클래스파일은 어떤 구조로 되어있을까?

클래스 파일의 데이터 형식

8비트 바이트의 스트림으로 구성된다.
16비트 및 32비트의 데이터는 각각 2개, 4개의 연속된 8비트를 읽어서 구성된다.
멀티바이트의 경우 항상 big endian 순서로 저장된다.

u1 → unsigned 1byte
@@ -36,7 +36,7 @@ Class file in Java, File Format
java se11 Class 파일 형식, Oracle
java se17 Class 파일 형식, Oracle

- - + + \ No newline at end of file diff --git a/tags/java/page/3.html b/tags/java/page/3.html index 014e41b80..a4b49f791 100644 --- a/tags/java/page/3.html +++ b/tags/java/page/3.html @@ -13,12 +13,12 @@ - - + +
-

"Java" 태그로 연결된 5개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 10분

체스 미션에서는 데이터베이스에서 값을 가져오기 위해 DAO를 사용했다.
+

"Java" 태그로 연결된 5개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 10분

체스 미션에서는 데이터베이스에서 값을 가져오기 위해 DAO를 사용했다.
이 때 JDBC를 사용할 때 데이터베이스의 커넥션을 얻고, try-with-resource를 사용하는 부분이 반복되었다.
템플릿 콜백 패턴을 이용하여 나만의 JdbcTemplate을 만들어보았다.

기존 코드

public class User {
private final int id;
private final String name;

public User(final int id, final String name) {
this.id = id;
this.name = name;
}

public int getId() {
return id;
}

public String getName() {
return name;
}
}

SELECT, DELETE 중복 제거

변하지 않는 부분: try-with-resource, preparedStatement를 사용하는 부분, executeUpdate로 실행 등등
변하는 부분: SQL Query, 매개변수

다음과 같이 쿼리를 실행하는 부분을 분리하고 가변인수를 사용한다면 SELECT와 DELETE의 중복을 제거할 수 있다.

public void insert(final String name) {
final String query = "INSERT INTO User (name) VALUES (?)";
executeUpdate(query, name);
}

public void delete(final int userId) {
final String query = "DELETE FROM user WHERE user_id = ?";
executeUpdate(query, userId);
}

private void executeUpdate(final String query, final Object... parameters) {
final Connection connection = connectionPool.getConnection();
try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
for (int i = 1; i <= parameters.length; i++) {
preparedStatement.setObject(i, parameters[i - 1]);
}
preparedStatement.executeUpdate();
} catch (final SQLException e) {
throw new IllegalArgumentException(e.getMessage());
}
}

조회 분리하기 - 1. 콜백을 위한 인터페이스 정의

조회는 INSERT, DELETE와 달리 값을 반환받아야 하기 때문에 다른 방법을 사용해야 한다.
@@ -30,7 +30,7 @@ 아래와 같이 제네릭을 적용하여 다른 Dao에서도 사용 가능하도록 변경할 수 있다.

@FunctionalInterface
public interface RowMapper<T> {
T mapRow(final ResultSet resultSet) throws SQLException;
}

private <T> List<T> query(final String query, final RowMapper<T> rowMapper, final Object... parameters) {...}
private <T> T queryForSingleResult(final String query, final RowMapper<T> rowMapper, final Object... parameters) {...}

메서드 분리한 부분 클래스로 분리하기 + Optional 사용하기

메서드로 분리한 부분을 JdbcTemplate이라는 클래스를 만들어 옮긴다.
또한 null을 반환하기 보단 Optional로 감싸서 반환하도록 변경한다.
최종적으로 아래와 같은 코드가 완성된다.

public class UserDao {
private final RowMapper<User> rowMapper = resultSet -> {
final int id = resultSet.getInt("id");
final String name = resultSet.getString("name");
return new User(id, name);
};
private final JdbcTemplate jdbcTemplate;

public UserDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void insert(final String name) {
final String query = "INSERT INTO User (name) VALUES (?)";
jdbcTemplate.executeUpdate(query, name);
}

public void delete(final int userId) {
final String query = "DELETE FROM user WHERE user_id = ?";
jdbcTemplate.executeUpdate(query, userId);
}

public Optional<User> findById(final int userId) {
final String query = "SELECT * FROM user WHERE id = ?";
return jdbcTemplate.queryForSingleResult(query, rowMapper, userId);
}

public List<User> findAll() {
final String query = "SELECT * FROM user";
return jdbcTemplate.query(query, rowMapper);
}
}
- - + + \ No newline at end of file diff --git a/tags/java/page/4.html b/tags/java/page/4.html index 80d03e691..92a0468a4 100644 --- a/tags/java/page/4.html +++ b/tags/java/page/4.html @@ -13,12 +13,12 @@ - - + +
-

"Java" 태그로 연결된 5개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

테스트를 작성하다보면 매개변수에 따라 반복이 되는 테스트들이 생긴다.
+

"Java" 태그로 연결된 5개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

테스트를 작성하다보면 매개변수에 따라 반복이 되는 테스트들이 생긴다.
이 때 @ParameterizedTest를 사용하면 단일 테스트를 매개변수를 사용하여 여러 번 반복할 수 있다.

Argument Sources

@ParameterizedTest를 사용하려면 최소 하나 이상의 Source 애노테이션이 필요하다.
JUnit이 제공하는 다양한 Source가 있기 때문에, 테스트에 맞춰 다양하게 사용할 수 있다.

Value Source

값을 이용하여 제공하는 형태로, 다음과 같은 타입의 값을 매개변수로 제공할 수 있다.

  • short, int, long, float, double
  • byte, char, boolean, String, Class
@ParameterizedTest
@ValueSource(ints = {1, 100, Integer.MAX_VALUE})
void valueTest(final int value) {
Assertions.assertThat(value).isPositive();
}

Null & Empty Source

null 값, 빈 값을 제공한다.
Empty Source의 경우 다음과 같은 타입에 한해 매개변수로 제공할 수 있다.

  • String
  • java.util.List, java.util.Set, java.util.Map
  • primitive arrays — ex) int[]
  • object arrays — ex) String[]
@ParameterizedTest
@NullAndEmptySource
void nullAndEmptyTest(final String value) {
Assertions.assertThat(value).isNullOrEmpty();
}

Enum Source

EnumSource를 이용하여 Enum 또한 매개변수로 제공할 수 있다.

enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}

@ParameterizedTest
@EnumSource(Day.class)
void enumTest(final Day day) {
assertThat(day).isInstanceOf(Day.class);
}

다음과 같이 mode 값을 이용하여 특징 Enum을 제외하거나, 포함시킬 수 있다. (default: Mode.Include)

@ParameterizedTest
@EnumSource(value = Day.class, names = {"SATURDAY", "SUNDAY"}, mode = Mode.EXCLUDE)
void enumTest(final Day day) {
// MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
assertThat(day).isInstanceOf(Day.class);
}

CSV Source

csv 형식의 값을 이용하여 매개변수를 제공한다.
@@ -26,7 +26,7 @@ 개인적으로 2개 정도의 값을 매개변수로 전달하는 경우 CsvSource를 사용한다.

@ParameterizedTest
@CsvSource({"1,1", "2,4", "3,9", "4,16"})
void csvTest(final int number, final int result) {
assertThat(number * number).isEqualTo(result);
}

Method Source

복잡한 타입의 값을 전달할 때 사용한다.
메서드명을 입력하여 매개변수를 제공하는 메서드를 지정할 수 있다.
메서드명을 따로 입력하지 않으면 테스트명과 동일한 static 메서드가 지정된다.

@ParameterizedTest
@MethodSource
void methodTest(final List<Integer> numbers, final int count) {
assertThat(numbers).hasSize(count);
}

private static Stream<Arguments> methodTest() {
return Stream.of(
Arguments.of(List.of(1), 1),
Arguments.of(List.of(1, 2), 2),
Arguments.of(List.of(1, 2, 3), 3)
);
}

ETC.

위에서 언급한 방법 이외에도 다양한 방법으로 매개변수를 제공할 수 있다.

  • CSV 파일을 이용한 CsvFileSource
  • ArgumentsProvider 구현한 클래스를 이용하는 ArgumentsSource

참고 자료

- - + + \ No newline at end of file diff --git a/tags/java/page/5.html b/tags/java/page/5.html index 01c412c07..82d10cb1c 100644 --- a/tags/java/page/5.html +++ b/tags/java/page/5.html @@ -13,19 +13,19 @@ - - + +
-

"Java" 태그로 연결된 5개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 2분

이전에 많은 문제가 있던 자바의 클래스(Calendar, Date)를 대체하는 날짜와 시간 API
+

"Java" 태그로 연결된 5개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 2분

이전에 많은 문제가 있던 자바의 클래스(Calendar, Date)를 대체하는 날짜와 시간 API
ISO-8601을 기반으로 작성
설계 목표 → 불변, Fluent API, 명확하고 명시적, 확장 가능성

ISO-8601

날짜와 시간에 관련된 데이터를 다루는 국제 표준

LocalDate, LocalTime, LocalDateTime

날짜와 시간을 표현하는 클래스

Instant

유닉스 시간(1970-01-01, 00:00:00 UTC) 기준으로 특정 지점까지의 시간을 초로 표현하는 클래스
기계의 관점에서 시간 표현

Duration, Period

간격을 표현하는 클래스

TemporalAdjusters

복잡한 날짜 조정이 필요할 때 사용
필요한 경우 다음 인터페이스를 구현하여 커스텀 TemporalAdjuster를 구현 가능

@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}

DateTimeFormatter

날짜와 시간 포맷 클래스
특정 날짜 패턴이나, DateTimeFormatterBuilder를 이용해서 커스텀한 포맷을 생성 가능

ZoneId, ZoneOffset

ZoneId는 지역 ID는 ‘지역/도시’ 형식, ZoneOffset은 시차 UTC 기준 고정된 시간 차이 이용
ZoneId의 경우 IANA Time Zone Database에서 제공하는 지역 집합 정보 사용

Instant instant = Instant.now();
LocalDateTime utc = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

참고 자료

- - + + \ No newline at end of file diff --git a/tags/jdbc.html b/tags/jdbc.html index 76089aa4f..abc5817a8 100644 --- a/tags/jdbc.html +++ b/tags/jdbc.html @@ -13,12 +13,12 @@ - - + +
-

"JDBC" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 10분

체스 미션에서는 데이터베이스에서 값을 가져오기 위해 DAO를 사용했다.
+

"JDBC" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 10분

체스 미션에서는 데이터베이스에서 값을 가져오기 위해 DAO를 사용했다.
이 때 JDBC를 사용할 때 데이터베이스의 커넥션을 얻고, try-with-resource를 사용하는 부분이 반복되었다.
템플릿 콜백 패턴을 이용하여 나만의 JdbcTemplate을 만들어보았다.

기존 코드

public class User {
private final int id;
private final String name;

public User(final int id, final String name) {
this.id = id;
this.name = name;
}

public int getId() {
return id;
}

public String getName() {
return name;
}
}

SELECT, DELETE 중복 제거

변하지 않는 부분: try-with-resource, preparedStatement를 사용하는 부분, executeUpdate로 실행 등등
변하는 부분: SQL Query, 매개변수

다음과 같이 쿼리를 실행하는 부분을 분리하고 가변인수를 사용한다면 SELECT와 DELETE의 중복을 제거할 수 있다.

public void insert(final String name) {
final String query = "INSERT INTO User (name) VALUES (?)";
executeUpdate(query, name);
}

public void delete(final int userId) {
final String query = "DELETE FROM user WHERE user_id = ?";
executeUpdate(query, userId);
}

private void executeUpdate(final String query, final Object... parameters) {
final Connection connection = connectionPool.getConnection();
try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
for (int i = 1; i <= parameters.length; i++) {
preparedStatement.setObject(i, parameters[i - 1]);
}
preparedStatement.executeUpdate();
} catch (final SQLException e) {
throw new IllegalArgumentException(e.getMessage());
}
}

조회 분리하기 - 1. 콜백을 위한 인터페이스 정의

조회는 INSERT, DELETE와 달리 값을 반환받아야 하기 때문에 다른 방법을 사용해야 한다.
@@ -30,7 +30,7 @@ 아래와 같이 제네릭을 적용하여 다른 Dao에서도 사용 가능하도록 변경할 수 있다.

@FunctionalInterface
public interface RowMapper<T> {
T mapRow(final ResultSet resultSet) throws SQLException;
}

private <T> List<T> query(final String query, final RowMapper<T> rowMapper, final Object... parameters) {...}
private <T> T queryForSingleResult(final String query, final RowMapper<T> rowMapper, final Object... parameters) {...}

메서드 분리한 부분 클래스로 분리하기 + Optional 사용하기

메서드로 분리한 부분을 JdbcTemplate이라는 클래스를 만들어 옮긴다.
또한 null을 반환하기 보단 Optional로 감싸서 반환하도록 변경한다.
최종적으로 아래와 같은 코드가 완성된다.

public class UserDao {
private final RowMapper<User> rowMapper = resultSet -> {
final int id = resultSet.getInt("id");
final String name = resultSet.getString("name");
return new User(id, name);
};
private final JdbcTemplate jdbcTemplate;

public UserDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void insert(final String name) {
final String query = "INSERT INTO User (name) VALUES (?)";
jdbcTemplate.executeUpdate(query, name);
}

public void delete(final int userId) {
final String query = "DELETE FROM user WHERE user_id = ?";
jdbcTemplate.executeUpdate(query, userId);
}

public Optional<User> findById(final int userId) {
final String query = "SELECT * FROM user WHERE id = ?";
return jdbcTemplate.queryForSingleResult(query, rowMapper, userId);
}

public List<User> findAll() {
final String query = "SELECT * FROM user";
return jdbcTemplate.query(query, rowMapper);
}
}
- - + + \ No newline at end of file diff --git a/tags/jenkins.html b/tags/jenkins.html index 45be4baaa..219b530c9 100644 --- a/tags/jenkins.html +++ b/tags/jenkins.html @@ -13,12 +13,12 @@ - - + +
-

"Jenkins" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

설정 환경

소프트웨어 이미지: Amazon Linux 2023 AMI
+

"Jenkins" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기
- - + + \ No newline at end of file diff --git a/tags/kotlin.html b/tags/kotlin.html index c477e2699..c93037a01 100644 --- a/tags/kotlin.html +++ b/tags/kotlin.html @@ -13,12 +13,12 @@ - - + +
-

"Kotlin" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

nullable 타입

코틀린은 NullPointerException 예외를 최대한 발생시키지 않기 위해 타입 시스템이 설계되어 있다.
+

"Kotlin" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

nullable 타입

코틀린은 NullPointerException 예외를 최대한 발생시키지 않기 위해 타입 시스템이 설계되어 있다.
이는 실행 시점이 아닌 컴파일 시 미리 오류가 발생할 가능성이 있는 부분을 미리 감지하여 NPE 발생의 가능성을 줄여준다.

코틀린의 경우 nullable 타입을 다음과 같이 표현한다.

val number: Int?

타입 뒤에 ?를 붙여 해당 값이 null이 될 수 있다는 것을 의미한다.
만약 ?를 붙이지 않을 때 null을 받는 경우 컴파일 시 오류가 발생한다.

?. Safe Calls 연산자

자바에서 NPE를 발생시키지 않기 위해 null을 처리하는 가장 간단한 방법으로는 분기를 사용하는 방법이 있다.

코틀린은 안전한 호출 연산자인 ?. 연산자를 지원한다.
따라서 참조 값이 null이 아닐 경우에만 메서드 호출을 할 수 있다.
@@ -30,7 +30,7 @@ 사용하기 쉽지만, 리스크가 크고 혹시나 해당 값이 추후에는 null이 될 수 있기 때문에 지양해야 된다고 생각한다.

val length: Int = word!!.length

as? 안전한 캐스팅

타입 변환을 할 때 지정한 타입으로 변경할 수 없다면 ClassCastException이 발생한다.
코틀린에서는 as 뒤에 ?를 붙여 안전하게 타입 변환을 할 수 있다.
따라서 미리 변환 가능한 타입인지 확인하지 않고, 안전하게 타입을 변환 할 수 있다.

타입 변환이 불가능 할 경우 예외를 발생시키지 않고 null을 반환한다.

val value: Int? = something as? Int

List에서의 null 처리

List에는 null이 아닌 값만 반환하는 filterNotNull 유틸리티 메서드를 제공한다.

val foodsWithNull: List<String?> = listOf("Pizza", "Cheese", null, "Potato")
val foods = foodsWithNull.filterNotNull()

참고 자료

- - + + \ No newline at end of file diff --git a/tags/lock.html b/tags/lock.html index b565e1bc5..2c05d6258 100644 --- a/tags/lock.html +++ b/tags/lock.html @@ -13,12 +13,12 @@ - - + +
-

"Lock" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

InnoDB 스토리지 엔진의 잠금

MySQL에서 제공하는 잠금과 별개로 스토리지 엔진 내부에서 로우 단위의 잠금을 지원한다.
+

"Lock" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

InnoDB 스토리지 엔진의 잠금

MySQL에서 제공하는 잠금과 별개로 스토리지 엔진 내부에서 로우 단위의 잠금을 지원한다.
보통 명시적으로 잠금을 사용하는 경우는 드물고, 격리 수준에 따라 묵시적으로 잠금이 사용된다.

동시성 제어 방식에는 낙관적인 방식과 비관적인 방식이 있다.
InnoDB는 기본적으로 MVCC(다중 버전 동시성 제어)를 통해 낙관적인 방식을 사용하고 락을 통해 특정 상황에서 비관적인 방식을 사용한다.

낙관적 동시성 제어(OCC, Optimistic concurrency control)

트랜잭션이 서로 충돌하지 않는다고 가정하는 방식

비관적 동시성 제어(PCC, Pessimistic Concurrency Control)

트랜잭션이 충돌하는 가정하에 잠금을 거는 방식
일반적으로 Shared Lock, Exclusive Lock을 통해 이를 구현한다.

Shared & Exclusive Locks

InnoDB는 로우 단위의 잠금을 수행할 때 공유 잠금과 배타적 잠금을 사용한다.

공유 잠금(S, shared lock)

데이터 조회를 위한 락, 읽기 잠금(read lock)으로도 불린다.
@@ -38,7 +38,7 @@ MySQL Innodb Locks, cecil1018
MySQL 8.0 InnoDB Locks, MySQL
Locks Set by Different SQL Statements in InnoDB, MySQL

- - + + \ No newline at end of file diff --git a/tags/lock/page/2.html b/tags/lock/page/2.html index e74114389..bb8c41fb7 100644 --- a/tags/lock/page/2.html +++ b/tags/lock/page/2.html @@ -13,12 +13,12 @@ - - + +
-

"Lock" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

MySQL 엔진의 잠금

MySQL에서의 락은 스토리지 엔진 레벨과, MySQL 엔진 레벨로 나눌 수 있다.
+

"Lock" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

MySQL 엔진의 잠금

MySQL에서의 락은 스토리지 엔진 레벨과, MySQL 엔진 레벨로 나눌 수 있다.
MySQL 엔진 레벨의 잠금은 모든 스토리지 엔진에 영향을 미친다.

글로벌 락(Global lock)

MySQL에서 제공하는 잠금 중 가장 넓은 범위를 가지고 있는 잠금이다.

  • 영향을 미치는 범위는 해당 서버 전체이다.
  • 작업 대상 테이블, 데이터베이스 상관 없이 동일하게 영향을 받는다.

한 세션에서 글로벌 락을 획득하면 해제 될 때 까지 조회를 제외한 대부분의 명령이 대기 상태가 된다.
데이터베이스에 존재하는 MyISAM이나 MEMORY 테이블에 대해 일관된 백업을 받아야할 때 사용한다.
InnoDB 스토리지 엔진에서는 백업 시 조금 더 가벼운 백업 락을 사용할 수 있다.

-- GLOBAL LOCK
FLUSH TABLES WITH READ LOCK;
-- UNLOCK
UNLOCK TABLES;

-- BACKUP LOCK
LOCK INSTANCE FOR BACKUP;
-- UNLOCK
UNLOCK INSTANCE;
- - + + \ No newline at end of file diff --git a/tags/log.html b/tags/log.html index a1787383d..85ee019e6 100644 --- a/tags/log.html +++ b/tags/log.html @@ -13,12 +13,12 @@ - - + +
-

"log" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

CloudWatch

AWS 리소스와 애플리케이션의 지표와 로그에 대한 모니터링을 제공하는 서비스다.
+

"log" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

CloudWatch

AWS 리소스와 애플리케이션의 지표와 로그에 대한 모니터링을 제공하는 서비스다.
지표를 감시하여 알림을 보내는 기능도 제공한다.
프리티어를 사용하지 않는 경우 대시보드당 3$/M 의 비용이 청구되고, 지표나 로그의 양에 따라 비용이 추가적으로 청구된다.
요금 정보에 대한 자세한 정보는 다음 링크에서 확인할 수 있다.

CloudWatch Metrics

기본적으로 5분마다 지표에 대한 정보가 수집된다.
@@ -38,7 +38,7 @@ 서버에 CloudWatch 에이전트 설치 및 실행
CloudWatch Agent를 Parameter Store에서 관리해 보기
CloudWatch에이전트 구성 파일

- - + + \ No newline at end of file diff --git a/tags/mock.html b/tags/mock.html index fec46632d..a05ef86a4 100644 --- a/tags/mock.html +++ b/tags/mock.html @@ -13,12 +13,12 @@ - - + +
-

"Mock" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

테스트 대역이란?

모든 유형의 테스트를 위한 가짜 의존성을 의미하고, 테스트가 실행될 때 다른 객체를 대신한다.
+

"Mock" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

테스트 대역이란?

모든 유형의 테스트를 위한 가짜 의존성을 의미하고, 테스트가 실행될 때 다른 객체를 대신한다.
Gerard Meszaros의 xUnit Test Patterns라는 책에서는 테스트 대역을 다섯 가지(더미, 스텁, 스파이, 목, 페이크)로 구분한다.

테스트 대역의 기본 메커니즘은 다형성을 이용하는 방법이다.
외부 서비스를 사용하는 코드를 테스트 하는 경우, 인터페이스를 정의하고 외부 서비스 대신 테스트 용도의 구현체를 생성하는 것이다.

테스트 대역의 타입 계층 구조

더미(Dummy)

가장 단순하고, 원시적인 유형의 테스트 대역이다.
기본적으로 아무 일도 하지 않는 구현체로 인스턴스화가 필요한 경우 사용한다.
@@ -36,7 +36,7 @@ 테스트 더블, Martin Fowler
테스트 관련 용어 정리, Johngrib
Test Double, Gerard Meszaros

- - + + \ No newline at end of file diff --git a/tags/mockito.html b/tags/mockito.html index b3a4d7d47..e77acf401 100644 --- a/tags/mockito.html +++ b/tags/mockito.html @@ -13,12 +13,12 @@ - - + +
-

"Mockito" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 3분

개요

정적 팩터리 메서드를 모킹한다는 것은 객체지향적인 관점에서 볼 때 안티패턴이다.
+

"Mockito" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 3분

개요

정적 팩터리 메서드를 모킹한다는 것은 객체지향적인 관점에서 볼 때 안티패턴이다.
하지만 특수한 경우에는 정적 메서드를 모킹하는 것이 필요할 수 있다고 생각한다.

예를 들어 레거시 코드를 테스트 한다던지, IO 관련한 부분을 테스트 할 때 정말 필요한 부분에만 적용할 수 있을 것이다.

프로젝트를 진행하며 ImageIo.write 메서드가 호출되는 지 검증이 필요했다.
해당 static 메서드를 호출하는 부분을 따로 RouteImageUploader 클래스로 최대한 분리했다.
이미지 저장 기능 자체가 외부로 나가는 상호작용이고, 호출 횟수를 검사하는데는 mock을 사용하는게 적절하다고 판단했다.

public void upload(BufferedImage bufferedImage) {
File file = new File(파일경로);
try {
ImageIO.write(bufferedImage, ROUTE_IMAGE_FORMAT, file);
} catch (IOException e) {
throw new DrawException(IMAGE_SAVE_FAIL);
}
}

Mocking static methods

Mockito 3.4.0 이후에는 static method를 모킹할 수 있는 Mockito.mockStatic 메서드를 지원한다.
@@ -27,7 +27,7 @@ 항상 상황을 고려하고 간결함을 포기할 만큼 중요한 부분인지 적절한 트레이드오프를 고려하자.

참고 자료

Mocking static methods
Mockito mock static methods
Enable mocking static methods in Mockito

- - + + \ No newline at end of file diff --git a/tags/monitoring.html b/tags/monitoring.html index 9fe374d7d..260d94191 100644 --- a/tags/monitoring.html +++ b/tags/monitoring.html @@ -13,12 +13,12 @@ - - + +
-

"monitoring" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

CloudWatch

AWS 리소스와 애플리케이션의 지표와 로그에 대한 모니터링을 제공하는 서비스다.
+

"monitoring" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

CloudWatch

AWS 리소스와 애플리케이션의 지표와 로그에 대한 모니터링을 제공하는 서비스다.
지표를 감시하여 알림을 보내는 기능도 제공한다.
프리티어를 사용하지 않는 경우 대시보드당 3$/M 의 비용이 청구되고, 지표나 로그의 양에 따라 비용이 추가적으로 청구된다.
요금 정보에 대한 자세한 정보는 다음 링크에서 확인할 수 있다.

CloudWatch Metrics

기본적으로 5분마다 지표에 대한 정보가 수집된다.
@@ -38,7 +38,7 @@ 서버에 CloudWatch 에이전트 설치 및 실행
CloudWatch Agent를 Parameter Store에서 관리해 보기
CloudWatch에이전트 구성 파일

- - + + \ No newline at end of file diff --git a/tags/my-sql.html b/tags/my-sql.html index bc3709393..d035c8d62 100644 --- a/tags/my-sql.html +++ b/tags/my-sql.html @@ -13,12 +13,12 @@ - - + +
-

"MySQL" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

MySQL 엔진의 잠금

MySQL에서의 락은 스토리지 엔진 레벨과, MySQL 엔진 레벨로 나눌 수 있다.
+

"MySQL" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

MySQL 엔진의 잠금

MySQL에서의 락은 스토리지 엔진 레벨과, MySQL 엔진 레벨로 나눌 수 있다.
MySQL 엔진 레벨의 잠금은 모든 스토리지 엔진에 영향을 미친다.

글로벌 락(Global lock)

MySQL에서 제공하는 잠금 중 가장 넓은 범위를 가지고 있는 잠금이다.

  • 영향을 미치는 범위는 해당 서버 전체이다.
  • 작업 대상 테이블, 데이터베이스 상관 없이 동일하게 영향을 받는다.

한 세션에서 글로벌 락을 획득하면 해제 될 때 까지 조회를 제외한 대부분의 명령이 대기 상태가 된다.
데이터베이스에 존재하는 MyISAM이나 MEMORY 테이블에 대해 일관된 백업을 받아야할 때 사용한다.
InnoDB 스토리지 엔진에서는 백업 시 조금 더 가벼운 백업 락을 사용할 수 있다.

-- GLOBAL LOCK
FLUSH TABLES WITH READ LOCK;
-- UNLOCK
UNLOCK TABLES;

-- BACKUP LOCK
LOCK INSTANCE FOR BACKUP;
-- UNLOCK
UNLOCK INSTANCE;
- - + + \ No newline at end of file diff --git a/tags/mysql.html b/tags/mysql.html index c98121892..047a95d9e 100644 --- a/tags/mysql.html +++ b/tags/mysql.html @@ -13,12 +13,12 @@ - - + +
-

"mysql" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 21분

복제(Replication)

한 서버에서 다른 서버로 데이터를 동기화하는 것을 의미한다.
+

"mysql" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 21분

복제(Replication)

한 서버에서 다른 서버로 데이터를 동기화하는 것을 의미한다.
원본 데이터를 가지는 서버를 Primary 또는 Source 라고 부르고, 복제된 데이터를 가지는 서버를 Secondary 또는 Replica 라고 부른다.

복제를 하는 이유

1. 스케일 아웃

사용자의 트래픽이 증가하는 경우, 데이터베이스에 가해지는 부하도 자연스럽게 증가한다.
이를 처리하기 위해 복제를 통한 스케일 아웃을 적용하여 애플리케이션에서 사용하는 쿼리들을 각각의 데이터베이스로 분산 시킬 수 있다.

2. 데이터 백업

실제 운영되는 서비스가 사용하고 있는 DB에서 백업을 진행하는 경우, 서비스에 영향을 미칠 수 있다.
따라서 실제 서비스에 영향이 가지 않도록 복제를 통해 Replica 서버를 구축하여, Replica 서버에서 복제를 진행하는 방법으로 영향을 최소화 할 수 있다.

3. 데이터 분석

백업과 마찬가지로 복잡하고 무거운 분석용 쿼리의 서비스에 영향을 미칠 수 있다.
@@ -71,7 +71,7 @@ 데이터베이스 레플리케이션을 통한 쿼리 성능 개선 (feat. Mysql, SpringBoot)
부하 분산을 위한 MySQL Replication 구성 및 쿼리 요청 분기
Use Docker Compose, Docker

- - + + \ No newline at end of file diff --git a/tags/oop.html b/tags/oop.html index be9d61acc..9fd112141 100644 --- a/tags/oop.html +++ b/tags/oop.html @@ -13,15 +13,15 @@ - - + +
-

"OOP" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 9분

GRASP(General Responsibility Assignment Software Pattern)

크레이그 라만의 Applying UML and Patterns이라는 책에서 나온 책임 할당을 위한 패턴

각 패턴마다 Solution과 Problem로 구성되어 있다.

정보 전문가 패턴(Information Expert)

Q: 객체에 책임을 할당하는 기본 원칙은 무엇인가?

A: 책임을 수행하는 데 필요한 정보를 가진 클래스(정보 전문가)에게 책임을 할당한다.

정보와 행동을 가까운 곳에 위치시키기 때문에 캡슐화를 유지할 수 있다.

필요한 정보를 가진 객체들로 책임이 분산된다.

창조자 패턴(Creator)

Q: 누가 객체 A를 생성하는가?

A: 다음의 조건을 최대한 많이 만족하는 객체에게 객체 생성 책임을 할당해야 한다.

  • B가 A 객체를 포함 또는 참조한다.
  • B가 A 객체를 기록한다.
  • B가 A 객체를 긴밀하게 사용한다.
  • B가 A 객체의 초기값을 가지고 있다.

생성 예정인 객체와 연관되어 있는 객체가 생성 책임을 가지고 있게 된다면, 이미 해당 객체와 결합되어있다고 생각할 수 있다. 따라서 전체적인 결합도를 낮게 유지할 수 있다.

낮은 결합도 패턴(Low Coupling)

Q: 의존성을 낮추고 변화의 영향을 줄이며 재사용성을 증가시키는 방법은?

A: 전체적인 결합이 낮게 유지되도록 책임을 할당해야 한다.

결합도(Coupling) +

"OOP" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 9분

GRASP(General Responsibility Assignment Software Pattern)

크레이그 라만의 Applying UML and Patterns이라는 책에서 나온 책임 할당을 위한 패턴

각 패턴마다 Solution과 Problem로 구성되어 있다.

정보 전문가 패턴(Information Expert)

Q: 객체에 책임을 할당하는 기본 원칙은 무엇인가?

A: 책임을 수행하는 데 필요한 정보를 가진 클래스(정보 전문가)에게 책임을 할당한다.

정보와 행동을 가까운 곳에 위치시키기 때문에 캡슐화를 유지할 수 있다.

필요한 정보를 가진 객체들로 책임이 분산된다.

창조자 패턴(Creator)

Q: 누가 객체 A를 생성하는가?

A: 다음의 조건을 최대한 많이 만족하는 객체에게 객체 생성 책임을 할당해야 한다.

  • B가 A 객체를 포함 또는 참조한다.
  • B가 A 객체를 기록한다.
  • B가 A 객체를 긴밀하게 사용한다.
  • B가 A 객체의 초기값을 가지고 있다.

생성 예정인 객체와 연관되어 있는 객체가 생성 책임을 가지고 있게 된다면, 이미 해당 객체와 결합되어있다고 생각할 수 있다. 따라서 전체적인 결합도를 낮게 유지할 수 있다.

낮은 결합도 패턴(Low Coupling)

Q: 의존성을 낮추고 변화의 영향을 줄이며 재사용성을 증가시키는 방법은?

A: 전체적인 결합이 낮게 유지되도록 책임을 할당해야 한다.

결합도(Coupling) 객체 사이의 의존성이 과한 경우 결합도가 높다고 말한다.

  • 오브젝트 p.17

결합도를 낮춘다면 다음과 같은 이점이 있다.

  • 다른 구성 요소의 변화에 영향을 받지 않는다.
  • 재사용이 편리해진다.
  • 해당 클래스에 대한 이해가 쉬워진다. (의존하는 클래스가 적기 때문에)

높은 응집도 패턴(High Cohesion)

Q. 객체를 관리하기 쉽게 하려면 어떻게 해야 할까?

A. 높은 응집도를 유지할 수 있게 책임을 할당해야 한다.

응집도(Cohesion) 연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에게 위임하는 객체를 가리켜 응집도가 높다고 말한다.

  • 오브젝트 p.26

변경의 이유에 따라 클래스를 분리한다면 응집도를 높일 수 있고, 응집도가 높아진다면 다음과 같은 이점이 있다.

  • 해당 클래스에 대한 이해가 쉬워진다. (할당된 책임만을 수행하고 있기 때문에)
  • 유지보수가 쉬워진다.
  • 낮은 결합도 또한 지원한다.
  • 응집도가 높은 클래스는 특정한 목적에 사용할 수 있기 때문에 재사용하기 좋다.

컨트롤러 패턴(Controller)

Q. 사용자의 요청을 처리하는 것은 누가 담당해야 하는가?

A. 사용자의 요청을 처리하는 Controller 객체를 만들어서 사용해야 한다.

어떤 서브시스템이 존재한다고 가정할 때

  • 직접적으로 객체에 접근하여 프로그램을 사용한다면 결합도가 상승한다.
  • 서브 시스템에 들어오는 요청을 처리해주는 컨트롤러가 있다면 사용하는 입장에서는 해당 컨트롤러만 알면 된다.
  • 만약 서브 시스템의 변경이 생겼을 때 외부에 미치는 영향도 줄어든다.

다형성 패턴(Polymorphism)

Q. 객체의 타입에 따라 행동이 바뀐다면 책임을 어떻게 할당해야 할까?

A. OOP가 지원하는 다형성을 적극적으로 활용한다. (인터페이스를 두고 행동에 대한 부분을 구현)

객체의 종류에 따라 분기하는 조건문이 아닌 다형성을 사용하는 것이 좋은 방법이다.

새로운 타입이 추가되었을 때 조건문을 사용한다면 기존의 조건문을 수정해야 하지만 다형성을 활용하면 쉽게 확장할 수 있다.

변경 보호 패턴(Protected Variations)

Q. 어떻게 하면 변경이 다른 요소에 영향을 미치지 않도록 방지할 수 있을까?

A. 변화가 예상되는 지점을 식별하고, 주위에 안정된 인터페이스를 형성하도록 책임을 할당해야 한다.

간접 참조 패턴(Indirection)

Q. 두 객체 사이의 직접적인 연결을 피하고 싶다면 어떻게 해야 할까?

A. 두 객체 사이에 또 다른 객체를 두어 직접적인 연결을 피할 수 있다.

중재자 패턴을 사용하여 두 객체 사이에 또 하나의 객체를 추가하여 복잡한 관계를 단순화할 수 있다.

중간에 인터페이스를 둔다면 변경 보호 패턴(Protected Variations)에 해당된다.

순수한 가공물 패턴(Pure Fabrication)

Q. 책임을 할당한 도메인 객체가 Low Coupling, High Cohesion, 재사용성 등의 목적을 위반한다면 어떻게 해야 할까?

A. 도메인 개념을 포함하지 않는 클래스를 하나 만들고 매우 응집된 책임을 할당할 수 있다.

행동을 추가할 때, 해당 책임을 수행할 도메인 개념이 존재하지 않는다면 도메인과 무관한 인공적인 객체를 만든다음 해당 객체에게 책임을 할당한다.

객체가 데이터베이스에 저장해야 할 값을 가지고 있다고, 정보 전문가 패턴을 적용하여 데이터베이스에 저장하라는 책임을 가지라고 하지 않는다.

예) 상점과 고객 클래스가 있고 서로 다른 통화를 사용하고 있다고 가정

  • 서로 다른 통화를 사용하고 있기 때문에 거래를 하려면 환전을 해야한다.
  • 두 클래스 다 환전에 대한 책임을 부여하기 애매하다면 환전을 책임하는 클래스를 추가하고 사용할 수 있다.

참고 자료

오브젝트 5장. 책임 할당하기, 조영호

Applying UML and Patterns Chapter 16, Chapter 21 GRASP, Craig Larman

GRASP, 한빛 네트워크

- - + + \ No newline at end of file diff --git a/tags/pattern.html b/tags/pattern.html index 5b99968ab..d5db02b21 100644 --- a/tags/pattern.html +++ b/tags/pattern.html @@ -13,12 +13,12 @@ - - + +
-

"Pattern" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

요구사항

지하철 미션에는 다음과 같은 요구사항이 있었다.

  • 거리별 추가 요금 정책
  • 노선별 추가 요금 정책
  • 연령별 요금 할인 정책

인터페이스 사용

요금 정책은 다음과 같이 인터페이스로 표현할 수 있다.
+

"Pattern" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

요구사항

지하철 미션에는 다음과 같은 요구사항이 있었다.

  • 거리별 추가 요금 정책
  • 노선별 추가 요금 정책
  • 연령별 요금 할인 정책

인터페이스 사용

요금 정책은 다음과 같이 인터페이스로 표현할 수 있다.
요금을 계산하는 메서드는 최단 경로 계산의 결과, 사용자의 정보, 요금을 받아 요금을 계산한다.

public interface FarePolicy {
int calculate(Path path, Passenger passenger, int fare);
}

public class BaseFarePolicy implements FarePolicy { ... }
public class DistanceFarePolicy implements FarePolicy { ... }
public class AgeDiscountFarePolicy implements FarePolicy { ... }

composite1

모든 요금 정책을 포함하는 새로운 요금 정책 만들기

나머지 구현체를 모두 가지고 있는 하나의 구현체를 만들었다.
이 또한 FarePolicy를 구현한 형태가 되고, 필드로는 나머지 구현체들을 가지고 있다.

public class SubwayFarePolicy implements FarePolicy {

private final List<FarePolicy> farePolicies;

public SubwayFarePolicy(final List<FarePolicy> farePolicies) {
this.farePolicies = farePolicies;
}

@Override
public int calculate(final Path path, final Passenger passenger, final int fare) {
int calculatedFare = fare;
for (FarePolicy farePolicy : farePolicies) {
calculatedFare = farePolicy.calculate(path, passenger, calculatedFare);
}
return calculatedFare;
}
}

따라서 그림으로 본다면 다음과 같은 구조가 된다.

composite2

정책의 순서

지하철 요구사항은 순서가 중요했다.
금액의 총합을 구하고, 그 후에 할인 정책이 들어가야했다.
@@ -31,7 +31,7 @@ 패턴을 맹목적으로 사용해서는 안되고, 현재의 요구사항에 따라 패턴을 유동적으로 수정해가면서 적용하는 것이 좋다.
항상 트레이드오프를 생각하자!

참고 자료

컴포지트 패턴, GoF의 디자인 패턴
디자인 패턴과 프레임워크, 오브젝트

- - + + \ No newline at end of file diff --git a/tags/performance-test.html b/tags/performance-test.html index 89b63f93d..0469f6e3b 100644 --- a/tags/performance-test.html +++ b/tags/performance-test.html @@ -13,12 +13,12 @@ - - + +
-

"performance test" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

성능 테스트

API의 요청이 많은 상황에서 서버가 어떻게 동작하는지 확인하는 테스트

시스템에 부하가 걸리면 문제 상황이 발생할 수 있다.
+

"performance test" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

성능 테스트

API의 요청이 많은 상황에서 서버가 어떻게 동작하는지 확인하는 테스트

시스템에 부하가 걸리면 문제 상황이 발생할 수 있다.
다양한 상황에 대비해서 성능 테스트를 해야한다.

./test.png

스모크 테스트(Smoke Test)

최소한의 부하를 주어 시스템이 정상적으로 동작하는지 확인하는 테스트

VU를 최소한으로 두고, 짧은 시간을 가지고 테스트한다.
다른 테스트를 시작하기 전에 스모크 테스트를 함으로써 테스트 스크립트에 오류가 없는지 확인할 수 있고, 성능 지표가 정상적으로 수집, 모니터링 되고 있는지 확인할 수 있다.

가상 사용자(VU)

가상 사용자는 서버 애플리케이션에 대해 특정 테스트를 실행한다.
이는 다른 가상 사용자와 독립적으로 실행되며, 여러 가상 사용자를 사용하여 동시 연결을 할 수 있다.
@@ -33,7 +33,7 @@ 다만 Auto Scaling이 적용된 클라우드 환경에서는 진행하지 않아야 한다.

참고 자료

Load test types, k6
자바 최적화 - 벤저민 J. 에번스, 제임스 고프, 크리스 뉴랜드
아마존 웹 서비스 부하 테스트 입문 - 나카가와 타루하치, 모리시타 켄

- - + + \ No newline at end of file diff --git a/tags/python.html b/tags/python.html index d84fd1eef..c04e29929 100644 --- a/tags/python.html +++ b/tags/python.html @@ -13,12 +13,12 @@ - - + +
-

"Python" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 7분

개요

이전에 기술 구현 가능 여부를 조사하면서 파이썬을 사용한 내용을 정리한 내용이다.

사용 기술

언어: Python 3.10
+

"Python" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 7분

개요

이전에 기술 구현 가능 여부를 조사하면서 파이썬을 사용한 내용을 정리한 내용이다.

사용 기술

언어: Python 3.10
이미지 생성: matplotlib
서비스: AWS Lambda, AWS API Gateway
이미지 저장 및 URL: AWS S3, AWS CloudFront

플로우는 다음과 같다.

요구사항

./route.png

우측 상단의 경로 이미지를 생성하려고 한다.
@@ -39,7 +39,7 @@ Python Lambda 함수에 대한 .zip 파일 아카이브 작업
No module named 'numpy.core._multiarray_umath'
사례별로 알아본 안전한 S3 사용 가이드

- - + + \ No newline at end of file diff --git a/tags/replication.html b/tags/replication.html index 7bf57926a..6149f073e 100644 --- a/tags/replication.html +++ b/tags/replication.html @@ -13,12 +13,12 @@ - - + +
-

"replication" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 21분

복제(Replication)

한 서버에서 다른 서버로 데이터를 동기화하는 것을 의미한다.
+

"replication" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 21분

복제(Replication)

한 서버에서 다른 서버로 데이터를 동기화하는 것을 의미한다.
원본 데이터를 가지는 서버를 Primary 또는 Source 라고 부르고, 복제된 데이터를 가지는 서버를 Secondary 또는 Replica 라고 부른다.

복제를 하는 이유

1. 스케일 아웃

사용자의 트래픽이 증가하는 경우, 데이터베이스에 가해지는 부하도 자연스럽게 증가한다.
이를 처리하기 위해 복제를 통한 스케일 아웃을 적용하여 애플리케이션에서 사용하는 쿼리들을 각각의 데이터베이스로 분산 시킬 수 있다.

2. 데이터 백업

실제 운영되는 서비스가 사용하고 있는 DB에서 백업을 진행하는 경우, 서비스에 영향을 미칠 수 있다.
따라서 실제 서비스에 영향이 가지 않도록 복제를 통해 Replica 서버를 구축하여, Replica 서버에서 복제를 진행하는 방법으로 영향을 최소화 할 수 있다.

3. 데이터 분석

백업과 마찬가지로 복잡하고 무거운 분석용 쿼리의 서비스에 영향을 미칠 수 있다.
@@ -71,7 +71,7 @@ 데이터베이스 레플리케이션을 통한 쿼리 성능 개선 (feat. Mysql, SpringBoot)
부하 분산을 위한 MySQL Replication 구성 및 쿼리 요청 분기
Use Docker Compose, Docker

- - + + \ No newline at end of file diff --git a/tags/retrospective.html b/tags/retrospective.html index 06c118581..702297b1a 100644 --- a/tags/retrospective.html +++ b/tags/retrospective.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 13분

톰캣 구현

우아한테크코스를 지원할 때 객체지향과 관련된 미션도 기대를 많이 했지만 레벨 4에 진행하는 미션이 정말 하고 싶었다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 13분

톰캣 구현

우아한테크코스를 지원할 때 객체지향과 관련된 미션도 기대를 많이 했지만 레벨 4에 진행하는 미션이 정말 하고 싶었다.
그래서 미션을 할 수 있을까라는 걱정 반, 미션에 대한 기대 반으로 부푼 마음을 가지고 미션을 시작했던 것 같다.

이번 미션에서는 적절하게 추상화하고, 미션의 본질을 이해하려고 노력했다.
톰캣 구현 미션은 RFC 2616에 명시된 스펙(완벽하지 않지만 미션에서 주어진 요구사항만 만족하도록)으로 요청을 받아 처리 후 반환하는데 집중했다.

다이어그램

Catalina는 Tomcat의 서블릿 컨테이너, Coyote는 HTTP 1.1 웹 서버를 지원하는 구성 요소라고 생각하고 아래와 같이 구성했다.
사실 내부 구조를 깊게 공부할 시간을 가지지 못해서 각 구성 요소가 왜 해당 위치에 있는지 완벽하게 설명하지는 못하지만 미션을 진행하면서 이건 여기에 있으면 좋을 것 같은데? 라는 생각이 들면 적절한 패키지에 위치시키는 방향으로 진행을 했다.
@@ -47,7 +47,7 @@ Apache Tomcat 8 Configuration Reference
Apache Tomcat Tuning, Terry Cho
maxThreads, maxConnections, acceptCount로 Tomcat 튜닝하기

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/10.html b/tags/retrospective/page/10.html index 74d0348d6..c58bfc1f9 100644 --- a/tags/retrospective/page/10.html +++ b/tags/retrospective/page/10.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

프론트엔트

닉네임을 입력하여 간단히 로그인하는 화면, 채팅 목록을 보여주는 화면도 만들었고 단일 채팅을 확인할 수 있는 화면도 만들었다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

프론트엔트

닉네임을 입력하여 간단히 로그인하는 화면, 채팅 목록을 보여주는 화면도 만들었고 단일 채팅을 확인할 수 있는 화면도 만들었다.
추가로 채팅을 이어나갈 수 있게 하는 기능도 추가했다.
자잘하게 신경 쓸 부분이 많아서, 프론트엔드 하는 사람들이 대단하다고 생각되었다.
여유가 된다면 자신의 채팅을 볼 수 있는 기능이나, 채팅을 이어서 할 수 있는 기능, 댓글 기능도 추가할 예정이다.

백엔드

최대한 빨리 서비스를 크루들에게 제공하기로 정해서, 백엔드는 말랑이 일단 다 만들고 있다.
@@ -37,7 +37,7 @@ 오늘 적용해 보니 램이 부족하여 중간에 잘 안되기도 하고 그래서 그냥 "Pipeline만 사용할 걸 그랬나?" 라는 생각이 든다.

참고 자료

Elastic Beanstalk, AWS
EC2 AWS Graviton, AWS
Default Memory Settings, AWS

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/11.html b/tags/retrospective/page/11.html index 0613cb5b3..2867387f7 100644 --- a/tags/retrospective/page/11.html +++ b/tags/retrospective/page/11.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

4월 21일 금요일

레벨 2를 시작한 뒤 내가 학습에 대한 방향을 잃어버렸다는 생각이 들었다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

4월 21일 금요일

레벨 2를 시작한 뒤 내가 학습에 대한 방향을 잃어버렸다는 생각이 들었다.
레벨 3, 4에서 나만의 강점을 가지고 싶어 고민을 많이 했다.
단순히 스프링을 깊게 공부하는 건 효율이 많이 떨어진다고 생각했다.
글쓰기 수상으로 받은 쿠폰을 사용해 브라운에게 커피챗을 신청했고, 사이드 프로젝트를 해보라는 답을 받았다.

나는 아이디어를 못내는 편인데 브라운이 아이디어까지 던져주셨다.
@@ -41,7 +41,7 @@ 추가로 띄어쓰기도 적용되지 않아서 \n<br>태그로 변환했다.
변환하는 로직은 GPT의 도움을 많이 받았다.

const replaceCodeFences = (input: String) => {
const codeFencesRegex = /```([\w-]*)\n([\s\S]*?)\n```/g;
return input
.replace(codeFencesRegex, (match, p1, p2) => {
const languageClass = p1 ? ` class="language-${p1}"` : "";
return `<pre><code${languageClass}>${p2}</code></pre>`;
})
.replace(/\n/g, "<br>");
};

Tiptap을 적용하니 다음과 같이 깔끔한 코드 블록을 볼 수 있었다.

tecochat

폰트 및 favicon 적용

타이틀은 배달의민족 도현체, 내용은 IBM Plex Sans를 사용했다.
추가로 favicon도 간단하게 적용해서 만족스러웠다.

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/12.html b/tags/retrospective/page/12.html index e6df7bb9b..caa52a678 100644 --- a/tags/retrospective/page/12.html +++ b/tags/retrospective/page/12.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

레벨 1이 끝났다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

레벨 1이 끝났다.
우테코를 시작하기 전 내가 정해두었던 목표 이상으로 달성했기 때문에 매우 만족스럽다.
혼자 독학을 할 땐 이 방향으로 공부하는 게 맞는지 계속 반추하다 결국 무기력함에 빠져들었다.
하지만 이제는 같이 공부할 사람도 있고, 이야기할 사람도 있기 때문에 즐기는 일만 남은 것 같다.

Keep

나만의 루틴 만들기

스스로가 외부의 영향을 많이 받는다고 생각한다.
@@ -50,7 +50,7 @@ 블로그에 기술적인 부분을 많이 정리하지 않았는데, 조금 더 깊게 공부하고 정리하는 시간도 가져야겠다.

레벨 1을 마무리하며

시간이 빠르게 흘러갔다.
타인에게 좋은 영향을 주기위해, 방학동안 나를 챙기는 시간을 가져야겠다.
또한 함께 일하고 싶은 사람을 목표로 앞으로도 꾸준히 의식적 노력을 해야겠다.

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/13.html b/tags/retrospective/page/13.html index ddb118470..cfd13be5b 100644 --- a/tags/retrospective/page/13.html +++ b/tags/retrospective/page/13.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

체스

체스 미션에는 가비와 페어가 매칭되었다!
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

체스

체스 미션에는 가비와 페어가 매칭되었다!
체스는 이전 미션들보다 훨씬 복잡한 도메인이었다.
하지만 가비와 나는 체스 도메인이 익숙해서 더 편한 마음으로 시작할 수 있었다.
미션을 진행하면서 어려웠던 부분은 기물의 이동, 이동시 경로에 기물이 존재하는지 확인하는 부분이었다.

가비가 집에가서도 기물의 이동 관련해 생각 정리한 글을 보내줘서 더욱 빨리 진행할 수 있었다.
@@ -53,7 +53,7 @@ 모르는게 있으면 솔직하게 말해주는 부분
나의 의견을 정리하지 못한 상태로 전달할 때 이해가 안되었다고 정확히 전달해주는 부분
솔직함은 페어할 때 중요한 부분인 것 같다.

마지막으로 찰리🍫 체스 미션때 꼼꼼하게 리뷰 남겨주셔서 감사합니다!

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/14.html b/tags/retrospective/page/14.html index 622d4bafd..749c0e6cc 100644 --- a/tags/retrospective/page/14.html +++ b/tags/retrospective/page/14.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

블랙잭

블랙잭 미션에서는 후추와 페어(조미료 듀오?)가 매칭되었다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

블랙잭

블랙잭 미션에서는 후추와 페어(조미료 듀오?)가 매칭되었다.
이번에는 실수하지 않고, 바로 미션을 진행하지 않고 친해지기 부터 시작했다.

블랙잭은 구현해야 될 내용이 많아 시간이 많이 부족할 것 같았지만
후추와 함께 전략적(삼일절에 미션 이야기 나누기)으로 미션을 진행해 시간 내에 제출할 수 있었다.

미션을 끝나고 회고를 했을 때 후추가 고민거리를 하나 내줬다.
"페어를 진행할 때 압박감을 느끼는 페어가 있다면 허브가 해줄 수 있는게 뭐가 있을까?"

곰곰히 생각해봤지만 쉽게 답을 내릴 수 없었다.
@@ -48,7 +48,7 @@ 회고때도 서로 솔직하게 의견을 주고 받아서 좋았다.

도메인 언어에 신경쓰는 부분
클래스명, 변수명과 같은 언어를 세심하게 신경쓴다.
요구사항 정리도 깔끔하게 잘하는 것 같다.

후추 최고 👍

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/15.html b/tags/retrospective/page/15.html index ad76dd03a..a2f6f90e8 100644 --- a/tags/retrospective/page/15.html +++ b/tags/retrospective/page/15.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 11분

사다리 타기

사다리 타기 미션에서는 우가와 페어가 매칭되었다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 11분

사다리 타기

사다리 타기 미션에서는 우가와 페어가 매칭되었다.
이전 미션과 달리 TDD로 진행하는 것이 필수였기 때문에 익숙하지 않았지만, 우가와 미션에 관한 소통이 잘 되어서 큰 문제 없이 미션을 마무리할 수 있었다.

우가와 이야기가 잘 통해서 그런지 1단계는 크게 어렵지 않게 진행할 수 있었는데, 2단계에서 많이 고전한 것 같다.

2단계에서는 2가지 방법으로 구현해봤다.

  1. LadderGame에서 Position 기준으로 사다리 게임을 진행하는 방법
  2. Player에게 Ladder를 넘겨서 Ladder에게 Position을 넘겨주며 메시지를 보내는 방법

Position 기준으로 사다리 게임을 진행하는 방법

사실상 index를 Ladder에게 넘겨주고, 해당 index에 대한 결과를 받는 방법과 유사했다.
구현하고 나니 다른 클래스들이 Position에 대한 의존도가 너무 높은 것 같았다.
또한 Players가 별다른 책임을 가지고 있지 않다고 느꼈다.

public LadderGameResult play() {
final Map<Player, Item> result = new LinkedHashMap<>();
// 사용자 수만큼 Position을 가져와서 사다리 게임을 진행한다.
for (Position position : Position.range(players.count())) {
final Position resultPosition = ladder.play(position);
result.put(players.get(position), items.get(resultPosition));
}
return new LadderGameResult(result);
}

Player에게 Ladder를 전달하여 게임을 진행하는 방법

Position에 대한 값을 가지고 있는 Player에게 Ladder를 넘겨서, Player가 Ladder에게 메시지를 보내도록 구현하였다.
@@ -50,7 +50,7 @@ 또한 페어 진행이 느린 것 같다고 말해줘서 안정적으로 시간 안에 미션을 완료할 수 있었다.
페어프로그래밍 진행 속도에 대해 조금 더 생각을 해봐야겠다!

항상 지나갈 때마다 웃어주는데, 나도 자주 웃어야겠다고 생각했다.
웃는 것만으로도 사람이 밝아 보여서 너무 좋은 것 같다!

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/16.html b/tags/retrospective/page/16.html index 9bcfb8d54..ba7100afa 100644 --- a/tags/retrospective/page/16.html +++ b/tags/retrospective/page/16.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

자동차 경주

자동차 경주 미션에서는 다즐과 페어가 매칭되었다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

자동차 경주

자동차 경주 미션에서는 다즐과 페어가 매칭되었다.
우테코 들어와서 첫 페어프로그래밍이라 많이 떨렸지만, 다즐이 대화를 잘 이끌어줘 너무 즐거웠다.

첫날은 간단히 컨벤션과 환경을 설정하는 시간을 가졌고 다음 날부터 자동차 경주를 시작했다.
시작은 간단하게 요구사항을 정리하고, 어떻게 코드를 작성할지 같이 고민했다.

시작하기 전 아래와 같이 mermaid를 이용하여 의존성 방향에 대해서 간단한 다이어그램을 만들고 시작했다.
mermaid는 코드로 다이어그램을 생성 해주는 도구로 다음과 같은 장점이 있다고 생각한다.

  • 코드 기반이라 빠른 시간 안에 생각한 것을 시각화할 수 있다.
  • github에서 mermaid를 지원하기 때문에 리뷰어에게 코드를 이해할 수 있는 부가적인 정보를 제공할 수 있다.

미션을 진행하는 데 큰 어려움이 있지는 않았고, 페어를 마치기 전 서로 고민되는 부분을 정리했을 때 좋았다.

페어하면서 잘했다고 생각했던 점은 서로의 생각과 리뷰 받은 것을 공유한 것이다.
@@ -44,7 +44,7 @@ 그래서 즐거운 마음으로 페어 프로그래밍을 했었던 것 같다.

어떤 이유 때문인지 모르겠지만 같이 페어하는데 편한 마음이 들었다.
이건 바로 배울 수 없지만.
나도 같이 일할 때 편한 사람, 같이 일하고 싶은 사람이 되기 위해 깊이 고민해봐야겠다.

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/17.html b/tags/retrospective/page/17.html index ff6f94f6e..6fdc89422 100644 --- a/tags/retrospective/page/17.html +++ b/tags/retrospective/page/17.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

적당한 전환점, 2022년을 돌아보며

전역

약 1년 6개월간의 공군 정보보호병 생활을 마치고 전역을 했다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

적당한 전환점, 2022년을 돌아보며

전역

약 1년 6개월간의 공군 정보보호병 생활을 마치고 전역을 했다.
조기 전역 때문에 2021년 12월에 나왔지만, 실제 전역 날짜는 2022년이니 회고에 적어도 상관없겠지.

조금 더 미래에 대한 생각을 해볼걸 그랬다.
전역을 했지만 뭐 하나 제대로 할 줄 아는 것도 없으니 넓은 바닷속에 덩그러니 놓아진 기분이 괜히 들었었다.
일찍 생각을 정리하여 방향을 잡지 못했기에 아쉬움이 많이 남았다.

자바

전역을 하고 진로를 고민하다 향로님의 자바 공화국 포스팅을 읽고 나서 자바 공부를 시작했다.
@@ -33,7 +33,7 @@ 적지 않은 시간을 투자해 준비를 했고, 감사하게도 이번에는 최종 합격을 했다.

난 사람들과 소통하고, 협업하는 능력이 부족하다고 생각을 많이 했다.
우아한 테크코스를 통해 그 빈 부분을 채우도록 노력해야겠다.

2023년에는

마음의 여유가 없었던 2022년이었던 것 같다.
하고 싶은 건 많지만, 이번에는 여유를 가지고 할 수 있는 것에 최선을 다해야겠다.

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/2.html b/tags/retrospective/page/2.html index c2845994b..dfb05275b 100644 --- a/tags/retrospective/page/2.html +++ b/tags/retrospective/page/2.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

회고

지난 8주는 레벨 1, 2 때보다 5배 정도 빠르게 지나간 것 같은 느낌이 들었다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

회고

지난 8주는 레벨 1, 2 때보다 5배 정도 빠르게 지나간 것 같은 느낌이 들었다.
레벨 3에는 기술적인 부분에서도, 기술 외적인 부분에서도 부족함이 많이 보였던 것 같다.
부족한 부분을 알았기에, 앞으로 더욱 성장할 수 있을 것 같다.
내가 부족했던 부분을 팀원들이 잘 보충해 줘서 든든했다.

아쉬운 점

문서화

개인적으로는 기술 외적으로 학습한 부분을 잘 정리하지 못했다.
@@ -33,7 +33,7 @@ 아래의 D 부분은 유튜브 강의 들으면서 직접 만들어서 뿌듯하다.

기술 선택의 이유

기술의 학습 비용, 현재 구조에 적합한지, 실제 가지고 있는 리소스를 고려해서 기술 선택을 하고, 도입했던 부분이 좋았다.
100% 좋은 선택일 순 없지만, 그래도 선택에 대한 근거가 존재한다면 확률을 높혀주는 것 같다.

마치며

플레이스토어에 앱이 올라가 있는 거 너무 신기하다.
안드로이드 브레멘 음악대(멧돼지, 수달, 핑구), 그리고 백엔드 팀원들(체인저, 후추, 리오) 너무 고생이 많았다.

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/3.html b/tags/retrospective/page/3.html index b892b6b3e..df79785de 100644 --- a/tags/retrospective/page/3.html +++ b/tags/retrospective/page/3.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 3분

23년의 6월이 오고, 레벨 2가 끝났다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 3분

23년의 6월이 오고, 레벨 2가 끝났다.
빠르게 지나가서 조금 아쉽다.

학습

회고를 작성하기 전에 레벨 2 동안 보냈던 PR과 회고를 쭉 읽어봤다.
항상 아쉬운 곳은 있기 마련이지만, 잘 학습한 것 같다.
미션을 하면서 기술을 어떻게 선택하고, 적용할 것인지 고민하는 과정에서 꽤나 많은 성장을 한 것 같다.

고민은 깊었지만 이론적인 학습이 부족한 레벨 2였다.
@@ -29,7 +29,7 @@ 이번에는 프런트엔드 크루와 협업을 했다. 소통은 잘 된 것 같지만 API 명세를 정하는 부분이 아직 미숙한 것 같다.

레벨 3 때부터 본격적으로 프로젝트가 시작된다.
팀을 위해 어떤 것을 할 수 있을지 고민을 많이 해봐야겠다.

레벨 2를 마무리하며

회고 작성하면서 레벨 2에서 했던 것들을 반추해 봤는데 부족한 점은 많았어도 좋은 방향으로 가고 있는 것 같다. 읽고 싶은 책도 읽고, 부족한 부분 채우면서 쉬어야겠다.

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/4.html b/tags/retrospective/page/4.html index 58838cf7d..26d5665d4 100644 --- a/tags/retrospective/page/4.html +++ b/tags/retrospective/page/4.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

레벨 인터뷰

레벨 1 때는 준비해둔 내용으로 인터뷰를 진행해서 그렇게 특별한 부분이 없었다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

레벨 인터뷰

레벨 1 때는 준비해둔 내용으로 인터뷰를 진행해서 그렇게 특별한 부분이 없었다.
따라서 레벨 1 레벨 인터뷰 회고는 레벨 1 회고를 작성할 때 끼워넣었다.
이번에는 범위도 제한되어 있어 어떻게 준비해야 할지 당황했고, 답변에도 부족한 부분이 많았었다.
기억이 사라지기 전에 큰 문제 없이 답변한 내용을 제외하고, 기억 남는 것 위주로 작성해 보려고 한다.

API 문서 도구 선택

큰 문제 없이 답변을 했는데 앞으로도 팀 프로젝트를 하면서 도움 될 것 같은 내용이 있어서 남겨두려고 한다.
@@ -31,7 +31,7 @@ 생각할 시간을 가졌을 때 "다시 말씀드려도 될까요?"라고 말하고 답변을 이어나가기
기술적으로 깊이가 부족하다고 생각이 많이 들어서 조금 더 깊게 공부하고 정리하기
이전에 공부했던거 되돌아 보는 시간 가지기

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/5.html b/tags/retrospective/page/5.html index 5a7bbd86a..dedec3bee 100644 --- a/tags/retrospective/page/5.html +++ b/tags/retrospective/page/5.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

장바구니 주문 미션

배포 및 협업을 할 수 있는 미션이었다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

장바구니 주문 미션

배포 및 협업을 할 수 있는 미션이었다.
마코, 우가, 우코, 우스 그리고 나까지 합쳐서 5명이 한 팀이 되었다.

배포

이전 미션들과 달리 AWS를 이용해 배포를 해야 했다.
각자 하나의 EC2 인스턴스를 제공받을 수 있었고, 팀 별로 DB를 위한 추가 인스턴스를 제공받았다.
배포 스크립트를 작성하는 경험을 해볼 수 있었다.
@@ -35,7 +35,7 @@ 추가로 현업에서는 고가용성 내결함성 등을 위하여 클러스터를 구성하여 사용하는 경우가 많고, 이 경우 readOnly 설정이 되어있다면 읽기 전용 DB로 질의가 들어가서 부하 분산의 효과가 있다고 한다.

DAO에 @Transactional 적용

DAO에 트랜잭션을 보장해 보는 건 어떻겠냐고 리뷰가 달려서 고민을 많이 했다.
Service 계층에 이미 트랜잭션을 보장해 주고 있기에 필요 없지 않을까 생각했었다.
DAO를 다른 곳에서 사용하더라도 트랜잭션을 보장하기 위해(확장성 고려) @Transactional을 적용하는 것도 괜찮은 것 같다.

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/6.html b/tags/retrospective/page/6.html index 4ed62189f..95301e2ef 100644 --- a/tags/retrospective/page/6.html +++ b/tags/retrospective/page/6.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

개요

원래 목적인 크루들의 학습에 도움을 주기 위해 어떤 기능을 추가해야 할지 고민을 많이 했다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

개요

원래 목적인 크루들의 학습에 도움을 주기 위해 어떤 기능을 추가해야 할지 고민을 많이 했다.
레벨 2가 거의 끝나가는 시점, 그동안 했던 것을 정리해 보려고 한다.

나의 채팅 확인하고 이어하는 기능

GPT에도 있는 기능인데, 내가 이전에 했던 채팅을 이어할 수 있는 기능을 추가했다.
예전에 어떤 질문을 남겼는지, 또한 해당 채팅을 이어서 할 수 있다.

chat1

좋아요와 댓글 기능

다른 사람들이 질문한 내용에 반응할 수 있는 무언가가 있었으면 좋겠다는 의견들이 많았다.
누가 좋아요를 눌렀는지, 어떤 채팅이 좋아요를 가장 많이 받았는지 확인할 수 있는 기능을 추가했다.
@@ -32,7 +32,7 @@ 제일 하고 싶은 것은 실제 GPT를 사용하는 것처럼 stream/text 값을 처리하고 싶은데 이 부분은 방학 때 기회가 되면 도전해 봐야겠다.

향후 계획

실제 크루들이 사용해 주는 서비스를 직접 만들어보면서 사용자의 입장에서 고민도 하게 되는 것 같다.
크루들이 직접 사용해 주니까 너무 고맙고, 한편으로는 신기하다.
일단 방학 때 stream/text 관련된 부분 동작되도록 구현해보려고 하고, 그 외의 부분은 조금 더 고민해야될 것 같다.

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/7.html b/tags/retrospective/page/7.html index fb04be417..efa858647 100644 --- a/tags/retrospective/page/7.html +++ b/tags/retrospective/page/7.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

지하철 미션

점점 일정이 많아지는 느낌이 들면서 회고가 늦어진다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

지하철 미션

점점 일정이 많아지는 느낌이 들면서 회고가 늦어진다.
지하철 미션은 밀리랑 페어를 진행했다.
간단한 CRUD만 있던 이전 미션들과 달리, 조금 복잡한 도메인 요구사항이 있었다.
이때 API, 테이블, 도메인 설계를 해야 했는데 어떤 것부터 해야 할지 고민을 많이 했다.
@@ -39,7 +39,7 @@ 또한 코딩할 때 내가 평소에 사용하는 코딩 컨벤션에 맞춰주는 것 같아서 페어 할 때 편했다!

편한 분위기

전체적으로 페어 할 때 편하게 진행했던 것 같다.
일정도 그렇고, 페어 진행할 때도 그렇고 큰 문제가 없었던 것 같아서 좋았다.
나는 과연 다른 사람들에게 편한 사람일까?

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/8.html b/tags/retrospective/page/8.html index 377e06cc1..6f32e6083 100644 --- a/tags/retrospective/page/8.html +++ b/tags/retrospective/page/8.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

웹 장바구니 미션

장바구니 미션은 블랙캣이랑 진행했다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

웹 장바구니 미션

장바구니 미션은 블랙캣이랑 진행했다.
요구사항이 엄청 복잡한 미션은 아니었고, 스프링을 사용하여 기본적인 CRUD를 구현하는 미션이었다.
2단계에서는 Basic 인증을 통해 자신의 장바구니에만 상품을 담고, 제거할 수 있도록 구현하는 요구사항이 추가되었다.
Interceptor나 Argument Resolver에 대한 이해도가 높지 않았는데, 이번 미션을 통해 조금 더 알아간 느낌이다.
@@ -32,7 +32,7 @@ 추가적으로 이모지를 적극적으로 사용하여 더욱 좋았다!

의견 일치시키기

페어 시간은 한정되어 있고, 기간 내 요구사항을 만족해야 한다.
따라서 적당히 타협을 봐서 의견을 빠르게 수용해 데드라인을 맞추는 것도 중요하다고 생각한다.
블랙캣은 내 의견을 잘 들어줬고, 덕분에 막히는 부분 없이 빠르게 미션을 진행할 수 있었다.

빨리 친해졌고, 의사소통이 잘 돼서 재밌게 코딩할 수 있었다!

- - + + \ No newline at end of file diff --git a/tags/retrospective/page/9.html b/tags/retrospective/page/9.html index 92d567685..916d5a313 100644 --- a/tags/retrospective/page/9.html +++ b/tags/retrospective/page/9.html @@ -13,12 +13,12 @@ - - + +
-

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

웹 자동차 미션

사이드 프로젝트를 한다고 시간이 많이 없어서 회고가 늦어졌다.
+

"Retrospective" 태그로 연결된 17개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

웹 자동차 미션

사이드 프로젝트를 한다고 시간이 많이 없어서 회고가 늦어졌다.
웹 자동차 미션에서는 비버와 페어가 매칭되었다.
레벨 2에서 진행하는 첫 미션이라 많이 긴장되었지만, 그래도 비버랑 초반에 맛있는 것도 많이 먹으면서 빨리 친해져서 재밌게 할 수 있었다.

스프링을 조금 사용할 줄 알아서, 비버랑 같이 학습하면서 미션을 진행했다.
첫 미션이라 그런지 특별한 부분은 없었고, 최대한 깔끔하게 작성하려고 노력했다.
@@ -37,7 +37,7 @@ 추가적으로 알고 싶은 부분을 따로 학습하는 열정이 좋다고 생각했다.
비버와 스프링에 대해 알아가는 시간을 많이 가진 부분이 매우 좋았다.
나도 5월부터 조금 더 화이팅 해야겠다.

- - + + \ No newline at end of file diff --git a/tags/spring-boot.html b/tags/spring-boot.html index c6ba0be9c..072122528 100644 --- a/tags/spring-boot.html +++ b/tags/spring-boot.html @@ -13,12 +13,12 @@ - - + +
-

"Spring Boot" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

자바 17, 스프링 6.0, 스프링 부트 3.1

팀 프로젝트를 진행하면서 스프링 부트 3.1을 사용하게 되었다.
+

"Spring Boot" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

자바 17, 스프링 6.0, 스프링 부트 3.1

팀 프로젝트를 진행하면서 스프링 부트 3.1을 사용하게 되었다.
2.7 버전을 사용할 수도 있었지만 LTS 기간과 취약점 패치로 인한 버전업 등을 고려했을 때 3.1과 자바 17을 사용하는 것이 더 효율적이라고 판단했다.

자바 변경 사항

우아한테크코스 레벨 2까지는 자바 11을 사용했었다.
따라서 자바 11부터 자바 17까지의 변경사항을 정식 릴리즈 기준으로 정리해보려고 한다.

Switch Expressions(Java 14)

Java 14에서는 기존의 Switch 문을 간결하게 작성할 수 있는 Switch 식이 추가되었다.

enum RESULT {
WIN, LOSE, DRAW
}

RESULT result = RESULT.WIN;

int prize = switch (result) {
case WIN -> 10_000_000;
case LOSE, DRAW -> 5_000_000;
default -> 0;
};

주요 특징은 다음과 같다.

  • -> 연산자를 이용하여 각 case에 대한 결과를 바로 반환할 수 있다.
  • case를 콤마(,)로 연결하여 하나의 case에 여러 값을 지정할 수 있다.
  • break 문이 필요 없다.
  • default 블록을 통해 기본 값을 지정할 수 있다.

Text Block(Java 15)

Java 15에는 새로운 문자열 표현방식이 추가되었다.
긴 문자열을 + 연산자의 도움 없이 가독성있게 작성할 수 있다.

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
@Query("""
SELECT p FROM Post p
WHERE p.title LIKE %:keyword%
OR p.content LIKE %:keyword%
""")
List<Post> findPostsByTitleOrContentContainingKeyword(String keyword);
}

NPE 메시지(Java 15)

String name = null;
name.chars();

/**
# before
java.lang.NullPointerException
at com.example.DiscountPolicyTest.test(NullPointerExceptionTest.java:61)

# after
Cannot invoke "String.chars()" because "name" is null
java.lang.NullPointerException: Cannot invoke "String.chars()" because "name" is null
*/

Record(Java 16)

Lombok의 @Data, kotlin의 data 클래스와 유사한 기능을 제공한다.
@@ -34,7 +34,7 @@ What's New in Spring Framework 6.x
Spring Boot 3.0 Release Notes
Spring Boot 3.1 Release Notes

- - + + \ No newline at end of file diff --git a/tags/spring.html b/tags/spring.html index c300a9ae6..649e4677d 100644 --- a/tags/spring.html +++ b/tags/spring.html @@ -13,12 +13,12 @@ - - + +
-

"Spring" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

자바 17, 스프링 6.0, 스프링 부트 3.1

팀 프로젝트를 진행하면서 스프링 부트 3.1을 사용하게 되었다.
+

"Spring" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

자바 17, 스프링 6.0, 스프링 부트 3.1

팀 프로젝트를 진행하면서 스프링 부트 3.1을 사용하게 되었다.
2.7 버전을 사용할 수도 있었지만 LTS 기간과 취약점 패치로 인한 버전업 등을 고려했을 때 3.1과 자바 17을 사용하는 것이 더 효율적이라고 판단했다.

자바 변경 사항

우아한테크코스 레벨 2까지는 자바 11을 사용했었다.
따라서 자바 11부터 자바 17까지의 변경사항을 정식 릴리즈 기준으로 정리해보려고 한다.

Switch Expressions(Java 14)

Java 14에서는 기존의 Switch 문을 간결하게 작성할 수 있는 Switch 식이 추가되었다.

enum RESULT {
WIN, LOSE, DRAW
}

RESULT result = RESULT.WIN;

int prize = switch (result) {
case WIN -> 10_000_000;
case LOSE, DRAW -> 5_000_000;
default -> 0;
};

주요 특징은 다음과 같다.

  • -> 연산자를 이용하여 각 case에 대한 결과를 바로 반환할 수 있다.
  • case를 콤마(,)로 연결하여 하나의 case에 여러 값을 지정할 수 있다.
  • break 문이 필요 없다.
  • default 블록을 통해 기본 값을 지정할 수 있다.

Text Block(Java 15)

Java 15에는 새로운 문자열 표현방식이 추가되었다.
긴 문자열을 + 연산자의 도움 없이 가독성있게 작성할 수 있다.

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
@Query("""
SELECT p FROM Post p
WHERE p.title LIKE %:keyword%
OR p.content LIKE %:keyword%
""")
List<Post> findPostsByTitleOrContentContainingKeyword(String keyword);
}

NPE 메시지(Java 15)

String name = null;
name.chars();

/**
# before
java.lang.NullPointerException
at com.example.DiscountPolicyTest.test(NullPointerExceptionTest.java:61)

# after
Cannot invoke "String.chars()" because "name" is null
java.lang.NullPointerException: Cannot invoke "String.chars()" because "name" is null
*/

Record(Java 16)

Lombok의 @Data, kotlin의 data 클래스와 유사한 기능을 제공한다.
@@ -34,7 +34,7 @@ What's New in Spring Framework 6.x
Spring Boot 3.0 Release Notes
Spring Boot 3.1 Release Notes

- - + + \ No newline at end of file diff --git a/tags/static.html b/tags/static.html index f07e8bf9e..baf675788 100644 --- a/tags/static.html +++ b/tags/static.html @@ -13,12 +13,12 @@ - - + +
-

"static" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 3분

개요

정적 팩터리 메서드를 모킹한다는 것은 객체지향적인 관점에서 볼 때 안티패턴이다.
+

"static" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 3분

개요

정적 팩터리 메서드를 모킹한다는 것은 객체지향적인 관점에서 볼 때 안티패턴이다.
하지만 특수한 경우에는 정적 메서드를 모킹하는 것이 필요할 수 있다고 생각한다.

예를 들어 레거시 코드를 테스트 한다던지, IO 관련한 부분을 테스트 할 때 정말 필요한 부분에만 적용할 수 있을 것이다.

프로젝트를 진행하며 ImageIo.write 메서드가 호출되는 지 검증이 필요했다.
해당 static 메서드를 호출하는 부분을 따로 RouteImageUploader 클래스로 최대한 분리했다.
이미지 저장 기능 자체가 외부로 나가는 상호작용이고, 호출 횟수를 검사하는데는 mock을 사용하는게 적절하다고 판단했다.

public void upload(BufferedImage bufferedImage) {
File file = new File(파일경로);
try {
ImageIO.write(bufferedImage, ROUTE_IMAGE_FORMAT, file);
} catch (IOException e) {
throw new DrawException(IMAGE_SAVE_FAIL);
}
}

Mocking static methods

Mockito 3.4.0 이후에는 static method를 모킹할 수 있는 Mockito.mockStatic 메서드를 지원한다.
@@ -27,7 +27,7 @@ 항상 상황을 고려하고 간결함을 포기할 만큼 중요한 부분인지 적절한 트레이드오프를 고려하자.

참고 자료

Mocking static methods
Mockito mock static methods
Enable mocking static methods in Mockito

- - + + \ No newline at end of file diff --git a/tags/teco-chat.html b/tags/teco-chat.html index feec3c247..9c1c5b608 100644 --- a/tags/teco-chat.html +++ b/tags/teco-chat.html @@ -13,12 +13,12 @@ - - + +
-

"TecoChat" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

개요

원래 목적인 크루들의 학습에 도움을 주기 위해 어떤 기능을 추가해야 할지 고민을 많이 했다.
+

"TecoChat" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

개요

원래 목적인 크루들의 학습에 도움을 주기 위해 어떤 기능을 추가해야 할지 고민을 많이 했다.
레벨 2가 거의 끝나가는 시점, 그동안 했던 것을 정리해 보려고 한다.

나의 채팅 확인하고 이어하는 기능

GPT에도 있는 기능인데, 내가 이전에 했던 채팅을 이어할 수 있는 기능을 추가했다.
예전에 어떤 질문을 남겼는지, 또한 해당 채팅을 이어서 할 수 있다.

chat1

좋아요와 댓글 기능

다른 사람들이 질문한 내용에 반응할 수 있는 무언가가 있었으면 좋겠다는 의견들이 많았다.
누가 좋아요를 눌렀는지, 어떤 채팅이 좋아요를 가장 많이 받았는지 확인할 수 있는 기능을 추가했다.
@@ -32,7 +32,7 @@ 제일 하고 싶은 것은 실제 GPT를 사용하는 것처럼 stream/text 값을 처리하고 싶은데 이 부분은 방학 때 기회가 되면 도전해 봐야겠다.

향후 계획

실제 크루들이 사용해 주는 서비스를 직접 만들어보면서 사용자의 입장에서 고민도 하게 되는 것 같다.
크루들이 직접 사용해 주니까 너무 고맙고, 한편으로는 신기하다.
일단 방학 때 stream/text 관련된 부분 동작되도록 구현해보려고 하고, 그 외의 부분은 조금 더 고민해야될 것 같다.

- - + + \ No newline at end of file diff --git a/tags/teco-chat/page/2.html b/tags/teco-chat/page/2.html index 2cc3f5732..5ede53954 100644 --- a/tags/teco-chat/page/2.html +++ b/tags/teco-chat/page/2.html @@ -13,12 +13,12 @@ - - + +
-

"TecoChat" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

프론트엔트

닉네임을 입력하여 간단히 로그인하는 화면, 채팅 목록을 보여주는 화면도 만들었고 단일 채팅을 확인할 수 있는 화면도 만들었다.
+

"TecoChat" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

프론트엔트

닉네임을 입력하여 간단히 로그인하는 화면, 채팅 목록을 보여주는 화면도 만들었고 단일 채팅을 확인할 수 있는 화면도 만들었다.
추가로 채팅을 이어나갈 수 있게 하는 기능도 추가했다.
자잘하게 신경 쓸 부분이 많아서, 프론트엔드 하는 사람들이 대단하다고 생각되었다.
여유가 된다면 자신의 채팅을 볼 수 있는 기능이나, 채팅을 이어서 할 수 있는 기능, 댓글 기능도 추가할 예정이다.

백엔드

최대한 빨리 서비스를 크루들에게 제공하기로 정해서, 백엔드는 말랑이 일단 다 만들고 있다.
@@ -37,7 +37,7 @@ 오늘 적용해 보니 램이 부족하여 중간에 잘 안되기도 하고 그래서 그냥 "Pipeline만 사용할 걸 그랬나?" 라는 생각이 든다.

참고 자료

Elastic Beanstalk, AWS
EC2 AWS Graviton, AWS
Default Memory Settings, AWS

- - + + \ No newline at end of file diff --git a/tags/teco-chat/page/3.html b/tags/teco-chat/page/3.html index b3cc4d8ed..38d03fc7d 100644 --- a/tags/teco-chat/page/3.html +++ b/tags/teco-chat/page/3.html @@ -13,12 +13,12 @@ - - + +
-

"TecoChat" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

4월 21일 금요일

레벨 2를 시작한 뒤 내가 학습에 대한 방향을 잃어버렸다는 생각이 들었다.
+

"TecoChat" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

4월 21일 금요일

레벨 2를 시작한 뒤 내가 학습에 대한 방향을 잃어버렸다는 생각이 들었다.
레벨 3, 4에서 나만의 강점을 가지고 싶어 고민을 많이 했다.
단순히 스프링을 깊게 공부하는 건 효율이 많이 떨어진다고 생각했다.
글쓰기 수상으로 받은 쿠폰을 사용해 브라운에게 커피챗을 신청했고, 사이드 프로젝트를 해보라는 답을 받았다.

나는 아이디어를 못내는 편인데 브라운이 아이디어까지 던져주셨다.
@@ -41,7 +41,7 @@ 추가로 띄어쓰기도 적용되지 않아서 \n<br>태그로 변환했다.
변환하는 로직은 GPT의 도움을 많이 받았다.

const replaceCodeFences = (input: String) => {
const codeFencesRegex = /```([\w-]*)\n([\s\S]*?)\n```/g;
return input
.replace(codeFencesRegex, (match, p1, p2) => {
const languageClass = p1 ? ` class="language-${p1}"` : "";
return `<pre><code${languageClass}>${p2}</code></pre>`;
})
.replace(/\n/g, "<br>");
};

Tiptap을 적용하니 다음과 같이 깔끔한 코드 블록을 볼 수 있었다.

tecochat

폰트 및 favicon 적용

타이틀은 배달의민족 도현체, 내용은 IBM Plex Sans를 사용했다.
추가로 favicon도 간단하게 적용해서 만족스러웠다.

- - + + \ No newline at end of file diff --git a/tags/test.html b/tags/test.html index f87650442..56590338a 100644 --- a/tags/test.html +++ b/tags/test.html @@ -13,12 +13,12 @@ - - + +
-

"Test" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

테스트 대역이란?

모든 유형의 테스트를 위한 가짜 의존성을 의미하고, 테스트가 실행될 때 다른 객체를 대신한다.
+

"Test" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

테스트 대역이란?

모든 유형의 테스트를 위한 가짜 의존성을 의미하고, 테스트가 실행될 때 다른 객체를 대신한다.
Gerard Meszaros의 xUnit Test Patterns라는 책에서는 테스트 대역을 다섯 가지(더미, 스텁, 스파이, 목, 페이크)로 구분한다.

테스트 대역의 기본 메커니즘은 다형성을 이용하는 방법이다.
외부 서비스를 사용하는 코드를 테스트 하는 경우, 인터페이스를 정의하고 외부 서비스 대신 테스트 용도의 구현체를 생성하는 것이다.

테스트 대역의 타입 계층 구조

더미(Dummy)

가장 단순하고, 원시적인 유형의 테스트 대역이다.
기본적으로 아무 일도 하지 않는 구현체로 인스턴스화가 필요한 경우 사용한다.
@@ -36,7 +36,7 @@ 테스트 더블, Martin Fowler
테스트 관련 용어 정리, Johngrib
Test Double, Gerard Meszaros

- - + + \ No newline at end of file diff --git a/tags/time.html b/tags/time.html index be1412a78..0c89cb69b 100644 --- a/tags/time.html +++ b/tags/time.html @@ -13,19 +13,19 @@ - - + +
-

"Time" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 2분

이전에 많은 문제가 있던 자바의 클래스(Calendar, Date)를 대체하는 날짜와 시간 API
+

"Time" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 2분

이전에 많은 문제가 있던 자바의 클래스(Calendar, Date)를 대체하는 날짜와 시간 API
ISO-8601을 기반으로 작성
설계 목표 → 불변, Fluent API, 명확하고 명시적, 확장 가능성

ISO-8601

날짜와 시간에 관련된 데이터를 다루는 국제 표준

LocalDate, LocalTime, LocalDateTime

날짜와 시간을 표현하는 클래스

Instant

유닉스 시간(1970-01-01, 00:00:00 UTC) 기준으로 특정 지점까지의 시간을 초로 표현하는 클래스
기계의 관점에서 시간 표현

Duration, Period

간격을 표현하는 클래스

TemporalAdjusters

복잡한 날짜 조정이 필요할 때 사용
필요한 경우 다음 인터페이스를 구현하여 커스텀 TemporalAdjuster를 구현 가능

@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}

DateTimeFormatter

날짜와 시간 포맷 클래스
특정 날짜 패턴이나, DateTimeFormatterBuilder를 이용해서 커스텀한 포맷을 생성 가능

ZoneId, ZoneOffset

ZoneId는 지역 ID는 ‘지역/도시’ 형식, ZoneOffset은 시차 UTC 기준 고정된 시간 차이 이용
ZoneId의 경우 IANA Time Zone Database에서 제공하는 지역 집합 정보 사용

Instant instant = Instant.now();
LocalDateTime utc = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

참고 자료

- - + + \ No newline at end of file diff --git a/tags/transaction.html b/tags/transaction.html index f54dc976f..6a8728d74 100644 --- a/tags/transaction.html +++ b/tags/transaction.html @@ -13,12 +13,12 @@ - - + +
-

"Transaction" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 10분

트랜잭션(Transaction)

데이터베이스에서 논리적 기능을 수행하기 위한 작업의 단위를 말한다.
+

"Transaction" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 10분

트랜잭션(Transaction)

데이터베이스에서 논리적 기능을 수행하기 위한 작업의 단위를 말한다.
트랜잭션은 작업의 완전성과 데이터의 정합성을 보장해 준다.
논리적인 작업 셋을 완벽하게 처리하거나, 오류 시 작업의 일부만 적용되는 현상을 막아준다.

트랜잭션의 속성(ACID)

원자성(Atomicity): 트랜잭션 내에서 실행된 작업들은 모두 성공하거나, 실패해야 한다.
일관성(Consistency): 트랜잭션이 수행되기 전과 후에 데이터베이스가 일관된 상태를 유지해야 한다.
@@ -42,7 +42,7 @@ 예) A가 레코드를 여러 번 조회하던 중 B가 레코드를 변경하여 A가 조회한 값이 달라지는 경우

팬텀 리드(Phantom read, Phantom row)

한 트랜잭션 내에서 동일한 쿼리 수행시, 수행 결과가 다른 현상
예) A가 레코드를 조회하고 B가 레코드를 추가하여 A가 다시 조회할 때 존재하지 않은 레코드가 조회되는 경우

참고 자료

Real My SQL 8.0 - 5장 트랜잭션과 잠금, 백은빈, 이성욱
Isolation Level, MySQL

- - + + \ No newline at end of file diff --git a/tags/web-socket.html b/tags/web-socket.html index 63aa09ac0..6b36ae563 100644 --- a/tags/web-socket.html +++ b/tags/web-socket.html @@ -13,12 +13,12 @@ - - + +
-

"WebSocket" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

웹소켓

단일 TCP 연결을 통해 클라이언트와 서버 간 전이중 양방향 통신을 지원하는 프로토콜
+

"WebSocket" 태그로 연결된 1개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

웹소켓

단일 TCP 연결을 통해 클라이언트와 서버 간 전이중 양방향 통신을 지원하는 프로토콜
웹 환경에서 연속된 데이터를 실시간으로 처리할 수 있다.

웹소켓은 HTTP의 포트를 그대로 사용하고 각각 포트 80과 포트 443을 사용하여 HTTP(ws://) 및 HTTPS(wss://)로 서버에 연결한다.

웹소켓 등장 배경

웹소켓이 등장하기 이전, 실시간성을 보장하기 위해 Polling, Long polling, Streaming 같은 기술을 사용했어야 했다.
이는 실시간성이나 양방향성을 만족시키지 못했고, HTTP를 이용하기 때문에 과도한 오버헤드가 발생했다.

polling, long polling, streaming

Polling: 주기적으로 서버에 요청을 보내 수신할 정보가 있는지 확인하는 방법

  • 서버에서 보낼 내용이 없어도 클라이언트는 알 수 없다.
  • 계속해서 요청을 보내 확인을 해야하기 때문에 서버에 불필요한 부하를 주어야 한다.

Long Polling: 클라이언트의 요청에 대해 응답을 보내지 않고 있다가 이벤트가 발생했을때 응답하는 방법

  • 폴링 방식보다 서버에 적은 부하를 줄 수 있지만, 요청의 주기가 짧으면 폴링과 차이가 없어진다.

Streaming: 클라이언트가 request를 보내면 커넥션을 맺고, 이 커넥션을 유지하면서 서버가 계속 데이터를 보내는 방법

  • 클라이언트가 서버에 요청을 하고 싶다면 새로운 커넥션을 맺어야 한다.

웹소켓의 동작

1. Upgrade 요청

WebSocket 프로토콜로 전환하는 HTTP 요청을 보낸다.
이는 HTTP와 같이 80, 443 포트를 사용한다.
@@ -32,7 +32,7 @@ https://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
https://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
https://docs.spring.io/spring-framework/reference/web/websocket.html

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse.html b/tags/woowahan-techcourse.html index e28ce05c3..d4926cc39 100644 --- a/tags/woowahan-techcourse.html +++ b/tags/woowahan-techcourse.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 13분

톰캣 구현

우아한테크코스를 지원할 때 객체지향과 관련된 미션도 기대를 많이 했지만 레벨 4에 진행하는 미션이 정말 하고 싶었다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 13분

톰캣 구현

우아한테크코스를 지원할 때 객체지향과 관련된 미션도 기대를 많이 했지만 레벨 4에 진행하는 미션이 정말 하고 싶었다.
그래서 미션을 할 수 있을까라는 걱정 반, 미션에 대한 기대 반으로 부푼 마음을 가지고 미션을 시작했던 것 같다.

이번 미션에서는 적절하게 추상화하고, 미션의 본질을 이해하려고 노력했다.
톰캣 구현 미션은 RFC 2616에 명시된 스펙(완벽하지 않지만 미션에서 주어진 요구사항만 만족하도록)으로 요청을 받아 처리 후 반환하는데 집중했다.

다이어그램

Catalina는 Tomcat의 서블릿 컨테이너, Coyote는 HTTP 1.1 웹 서버를 지원하는 구성 요소라고 생각하고 아래와 같이 구성했다.
사실 내부 구조를 깊게 공부할 시간을 가지지 못해서 각 구성 요소가 왜 해당 위치에 있는지 완벽하게 설명하지는 못하지만 미션을 진행하면서 이건 여기에 있으면 좋을 것 같은데? 라는 생각이 들면 적절한 패키지에 위치시키는 방향으로 진행을 했다.
@@ -47,7 +47,7 @@ Apache Tomcat 8 Configuration Reference
Apache Tomcat Tuning, Terry Cho
maxThreads, maxConnections, acceptCount로 Tomcat 튜닝하기

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/10.html b/tags/woowahan-techcourse/page/10.html index 3ee0ce6fd..96fcb2863 100644 --- a/tags/woowahan-techcourse/page/10.html +++ b/tags/woowahan-techcourse/page/10.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

체스

체스 미션에는 가비와 페어가 매칭되었다!
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

체스

체스 미션에는 가비와 페어가 매칭되었다!
체스는 이전 미션들보다 훨씬 복잡한 도메인이었다.
하지만 가비와 나는 체스 도메인이 익숙해서 더 편한 마음으로 시작할 수 있었다.
미션을 진행하면서 어려웠던 부분은 기물의 이동, 이동시 경로에 기물이 존재하는지 확인하는 부분이었다.

가비가 집에가서도 기물의 이동 관련해 생각 정리한 글을 보내줘서 더욱 빨리 진행할 수 있었다.
@@ -53,7 +53,7 @@ 모르는게 있으면 솔직하게 말해주는 부분
나의 의견을 정리하지 못한 상태로 전달할 때 이해가 안되었다고 정확히 전달해주는 부분
솔직함은 페어할 때 중요한 부분인 것 같다.

마지막으로 찰리🍫 체스 미션때 꼼꼼하게 리뷰 남겨주셔서 감사합니다!

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/11.html b/tags/woowahan-techcourse/page/11.html index 161411935..c675d549a 100644 --- a/tags/woowahan-techcourse/page/11.html +++ b/tags/woowahan-techcourse/page/11.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

블랙잭

블랙잭 미션에서는 후추와 페어(조미료 듀오?)가 매칭되었다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 6분

블랙잭

블랙잭 미션에서는 후추와 페어(조미료 듀오?)가 매칭되었다.
이번에는 실수하지 않고, 바로 미션을 진행하지 않고 친해지기 부터 시작했다.

블랙잭은 구현해야 될 내용이 많아 시간이 많이 부족할 것 같았지만
후추와 함께 전략적(삼일절에 미션 이야기 나누기)으로 미션을 진행해 시간 내에 제출할 수 있었다.

미션을 끝나고 회고를 했을 때 후추가 고민거리를 하나 내줬다.
"페어를 진행할 때 압박감을 느끼는 페어가 있다면 허브가 해줄 수 있는게 뭐가 있을까?"

곰곰히 생각해봤지만 쉽게 답을 내릴 수 없었다.
@@ -48,7 +48,7 @@ 회고때도 서로 솔직하게 의견을 주고 받아서 좋았다.

도메인 언어에 신경쓰는 부분
클래스명, 변수명과 같은 언어를 세심하게 신경쓴다.
요구사항 정리도 깔끔하게 잘하는 것 같다.

후추 최고 👍

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/12.html b/tags/woowahan-techcourse/page/12.html index 703f39171..519f049bd 100644 --- a/tags/woowahan-techcourse/page/12.html +++ b/tags/woowahan-techcourse/page/12.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 11분

사다리 타기

사다리 타기 미션에서는 우가와 페어가 매칭되었다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 11분

사다리 타기

사다리 타기 미션에서는 우가와 페어가 매칭되었다.
이전 미션과 달리 TDD로 진행하는 것이 필수였기 때문에 익숙하지 않았지만, 우가와 미션에 관한 소통이 잘 되어서 큰 문제 없이 미션을 마무리할 수 있었다.

우가와 이야기가 잘 통해서 그런지 1단계는 크게 어렵지 않게 진행할 수 있었는데, 2단계에서 많이 고전한 것 같다.

2단계에서는 2가지 방법으로 구현해봤다.

  1. LadderGame에서 Position 기준으로 사다리 게임을 진행하는 방법
  2. Player에게 Ladder를 넘겨서 Ladder에게 Position을 넘겨주며 메시지를 보내는 방법

Position 기준으로 사다리 게임을 진행하는 방법

사실상 index를 Ladder에게 넘겨주고, 해당 index에 대한 결과를 받는 방법과 유사했다.
구현하고 나니 다른 클래스들이 Position에 대한 의존도가 너무 높은 것 같았다.
또한 Players가 별다른 책임을 가지고 있지 않다고 느꼈다.

public LadderGameResult play() {
final Map<Player, Item> result = new LinkedHashMap<>();
// 사용자 수만큼 Position을 가져와서 사다리 게임을 진행한다.
for (Position position : Position.range(players.count())) {
final Position resultPosition = ladder.play(position);
result.put(players.get(position), items.get(resultPosition));
}
return new LadderGameResult(result);
}

Player에게 Ladder를 전달하여 게임을 진행하는 방법

Position에 대한 값을 가지고 있는 Player에게 Ladder를 넘겨서, Player가 Ladder에게 메시지를 보내도록 구현하였다.
@@ -50,7 +50,7 @@ 또한 페어 진행이 느린 것 같다고 말해줘서 안정적으로 시간 안에 미션을 완료할 수 있었다.
페어프로그래밍 진행 속도에 대해 조금 더 생각을 해봐야겠다!

항상 지나갈 때마다 웃어주는데, 나도 자주 웃어야겠다고 생각했다.
웃는 것만으로도 사람이 밝아 보여서 너무 좋은 것 같다!

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/13.html b/tags/woowahan-techcourse/page/13.html index e5c2d7800..f601a64a6 100644 --- a/tags/woowahan-techcourse/page/13.html +++ b/tags/woowahan-techcourse/page/13.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

자동차 경주

자동차 경주 미션에서는 다즐과 페어가 매칭되었다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

자동차 경주

자동차 경주 미션에서는 다즐과 페어가 매칭되었다.
우테코 들어와서 첫 페어프로그래밍이라 많이 떨렸지만, 다즐이 대화를 잘 이끌어줘 너무 즐거웠다.

첫날은 간단히 컨벤션과 환경을 설정하는 시간을 가졌고 다음 날부터 자동차 경주를 시작했다.
시작은 간단하게 요구사항을 정리하고, 어떻게 코드를 작성할지 같이 고민했다.

시작하기 전 아래와 같이 mermaid를 이용하여 의존성 방향에 대해서 간단한 다이어그램을 만들고 시작했다.
mermaid는 코드로 다이어그램을 생성 해주는 도구로 다음과 같은 장점이 있다고 생각한다.

  • 코드 기반이라 빠른 시간 안에 생각한 것을 시각화할 수 있다.
  • github에서 mermaid를 지원하기 때문에 리뷰어에게 코드를 이해할 수 있는 부가적인 정보를 제공할 수 있다.

미션을 진행하는 데 큰 어려움이 있지는 않았고, 페어를 마치기 전 서로 고민되는 부분을 정리했을 때 좋았다.

페어하면서 잘했다고 생각했던 점은 서로의 생각과 리뷰 받은 것을 공유한 것이다.
@@ -44,7 +44,7 @@ 그래서 즐거운 마음으로 페어 프로그래밍을 했었던 것 같다.

어떤 이유 때문인지 모르겠지만 같이 페어하는데 편한 마음이 들었다.
이건 바로 배울 수 없지만.
나도 같이 일할 때 편한 사람, 같이 일하고 싶은 사람이 되기 위해 깊이 고민해봐야겠다.

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/2.html b/tags/woowahan-techcourse/page/2.html index 0b9595fde..9527028e3 100644 --- a/tags/woowahan-techcourse/page/2.html +++ b/tags/woowahan-techcourse/page/2.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

회고

지난 8주는 레벨 1, 2 때보다 5배 정도 빠르게 지나간 것 같은 느낌이 들었다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

회고

지난 8주는 레벨 1, 2 때보다 5배 정도 빠르게 지나간 것 같은 느낌이 들었다.
레벨 3에는 기술적인 부분에서도, 기술 외적인 부분에서도 부족함이 많이 보였던 것 같다.
부족한 부분을 알았기에, 앞으로 더욱 성장할 수 있을 것 같다.
내가 부족했던 부분을 팀원들이 잘 보충해 줘서 든든했다.

아쉬운 점

문서화

개인적으로는 기술 외적으로 학습한 부분을 잘 정리하지 못했다.
@@ -33,7 +33,7 @@ 아래의 D 부분은 유튜브 강의 들으면서 직접 만들어서 뿌듯하다.

기술 선택의 이유

기술의 학습 비용, 현재 구조에 적합한지, 실제 가지고 있는 리소스를 고려해서 기술 선택을 하고, 도입했던 부분이 좋았다.
100% 좋은 선택일 순 없지만, 그래도 선택에 대한 근거가 존재한다면 확률을 높혀주는 것 같다.

마치며

플레이스토어에 앱이 올라가 있는 거 너무 신기하다.
안드로이드 브레멘 음악대(멧돼지, 수달, 핑구), 그리고 백엔드 팀원들(체인저, 후추, 리오) 너무 고생이 많았다.

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/3.html b/tags/woowahan-techcourse/page/3.html index f2bd72df3..1a41daf8d 100644 --- a/tags/woowahan-techcourse/page/3.html +++ b/tags/woowahan-techcourse/page/3.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 3분

23년의 6월이 오고, 레벨 2가 끝났다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 3분

23년의 6월이 오고, 레벨 2가 끝났다.
빠르게 지나가서 조금 아쉽다.

학습

회고를 작성하기 전에 레벨 2 동안 보냈던 PR과 회고를 쭉 읽어봤다.
항상 아쉬운 곳은 있기 마련이지만, 잘 학습한 것 같다.
미션을 하면서 기술을 어떻게 선택하고, 적용할 것인지 고민하는 과정에서 꽤나 많은 성장을 한 것 같다.

고민은 깊었지만 이론적인 학습이 부족한 레벨 2였다.
@@ -29,7 +29,7 @@ 이번에는 프런트엔드 크루와 협업을 했다. 소통은 잘 된 것 같지만 API 명세를 정하는 부분이 아직 미숙한 것 같다.

레벨 3 때부터 본격적으로 프로젝트가 시작된다.
팀을 위해 어떤 것을 할 수 있을지 고민을 많이 해봐야겠다.

레벨 2를 마무리하며

회고 작성하면서 레벨 2에서 했던 것들을 반추해 봤는데 부족한 점은 많았어도 좋은 방향으로 가고 있는 것 같다. 읽고 싶은 책도 읽고, 부족한 부분 채우면서 쉬어야겠다.

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/4.html b/tags/woowahan-techcourse/page/4.html index 3f1fb5c4f..4560a5ffe 100644 --- a/tags/woowahan-techcourse/page/4.html +++ b/tags/woowahan-techcourse/page/4.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

레벨 인터뷰

레벨 1 때는 준비해둔 내용으로 인터뷰를 진행해서 그렇게 특별한 부분이 없었다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

레벨 인터뷰

레벨 1 때는 준비해둔 내용으로 인터뷰를 진행해서 그렇게 특별한 부분이 없었다.
따라서 레벨 1 레벨 인터뷰 회고는 레벨 1 회고를 작성할 때 끼워넣었다.
이번에는 범위도 제한되어 있어 어떻게 준비해야 할지 당황했고, 답변에도 부족한 부분이 많았었다.
기억이 사라지기 전에 큰 문제 없이 답변한 내용을 제외하고, 기억 남는 것 위주로 작성해 보려고 한다.

API 문서 도구 선택

큰 문제 없이 답변을 했는데 앞으로도 팀 프로젝트를 하면서 도움 될 것 같은 내용이 있어서 남겨두려고 한다.
@@ -31,7 +31,7 @@ 생각할 시간을 가졌을 때 "다시 말씀드려도 될까요?"라고 말하고 답변을 이어나가기
기술적으로 깊이가 부족하다고 생각이 많이 들어서 조금 더 깊게 공부하고 정리하기
이전에 공부했던거 되돌아 보는 시간 가지기

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/5.html b/tags/woowahan-techcourse/page/5.html index 201582293..fe0338098 100644 --- a/tags/woowahan-techcourse/page/5.html +++ b/tags/woowahan-techcourse/page/5.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

장바구니 주문 미션

배포 및 협업을 할 수 있는 미션이었다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

장바구니 주문 미션

배포 및 협업을 할 수 있는 미션이었다.
마코, 우가, 우코, 우스 그리고 나까지 합쳐서 5명이 한 팀이 되었다.

배포

이전 미션들과 달리 AWS를 이용해 배포를 해야 했다.
각자 하나의 EC2 인스턴스를 제공받을 수 있었고, 팀 별로 DB를 위한 추가 인스턴스를 제공받았다.
배포 스크립트를 작성하는 경험을 해볼 수 있었다.
@@ -35,7 +35,7 @@ 추가로 현업에서는 고가용성 내결함성 등을 위하여 클러스터를 구성하여 사용하는 경우가 많고, 이 경우 readOnly 설정이 되어있다면 읽기 전용 DB로 질의가 들어가서 부하 분산의 효과가 있다고 한다.

DAO에 @Transactional 적용

DAO에 트랜잭션을 보장해 보는 건 어떻겠냐고 리뷰가 달려서 고민을 많이 했다.
Service 계층에 이미 트랜잭션을 보장해 주고 있기에 필요 없지 않을까 생각했었다.
DAO를 다른 곳에서 사용하더라도 트랜잭션을 보장하기 위해(확장성 고려) @Transactional을 적용하는 것도 괜찮은 것 같다.

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/6.html b/tags/woowahan-techcourse/page/6.html index 306759211..0bd0bdb8a 100644 --- a/tags/woowahan-techcourse/page/6.html +++ b/tags/woowahan-techcourse/page/6.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

지하철 미션

점점 일정이 많아지는 느낌이 들면서 회고가 늦어진다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

지하철 미션

점점 일정이 많아지는 느낌이 들면서 회고가 늦어진다.
지하철 미션은 밀리랑 페어를 진행했다.
간단한 CRUD만 있던 이전 미션들과 달리, 조금 복잡한 도메인 요구사항이 있었다.
이때 API, 테이블, 도메인 설계를 해야 했는데 어떤 것부터 해야 할지 고민을 많이 했다.
@@ -39,7 +39,7 @@ 또한 코딩할 때 내가 평소에 사용하는 코딩 컨벤션에 맞춰주는 것 같아서 페어 할 때 편했다!

편한 분위기

전체적으로 페어 할 때 편하게 진행했던 것 같다.
일정도 그렇고, 페어 진행할 때도 그렇고 큰 문제가 없었던 것 같아서 좋았다.
나는 과연 다른 사람들에게 편한 사람일까?

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/7.html b/tags/woowahan-techcourse/page/7.html index 7b355054b..4ef9bdebe 100644 --- a/tags/woowahan-techcourse/page/7.html +++ b/tags/woowahan-techcourse/page/7.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

웹 장바구니 미션

장바구니 미션은 블랙캣이랑 진행했다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 5분

웹 장바구니 미션

장바구니 미션은 블랙캣이랑 진행했다.
요구사항이 엄청 복잡한 미션은 아니었고, 스프링을 사용하여 기본적인 CRUD를 구현하는 미션이었다.
2단계에서는 Basic 인증을 통해 자신의 장바구니에만 상품을 담고, 제거할 수 있도록 구현하는 요구사항이 추가되었다.
Interceptor나 Argument Resolver에 대한 이해도가 높지 않았는데, 이번 미션을 통해 조금 더 알아간 느낌이다.
@@ -32,7 +32,7 @@ 추가적으로 이모지를 적극적으로 사용하여 더욱 좋았다!

의견 일치시키기

페어 시간은 한정되어 있고, 기간 내 요구사항을 만족해야 한다.
따라서 적당히 타협을 봐서 의견을 빠르게 수용해 데드라인을 맞추는 것도 중요하다고 생각한다.
블랙캣은 내 의견을 잘 들어줬고, 덕분에 막히는 부분 없이 빠르게 미션을 진행할 수 있었다.

빨리 친해졌고, 의사소통이 잘 돼서 재밌게 코딩할 수 있었다!

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/8.html b/tags/woowahan-techcourse/page/8.html index 67fca0553..758522bd1 100644 --- a/tags/woowahan-techcourse/page/8.html +++ b/tags/woowahan-techcourse/page/8.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

웹 자동차 미션

사이드 프로젝트를 한다고 시간이 많이 없어서 회고가 늦어졌다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 4분

웹 자동차 미션

사이드 프로젝트를 한다고 시간이 많이 없어서 회고가 늦어졌다.
웹 자동차 미션에서는 비버와 페어가 매칭되었다.
레벨 2에서 진행하는 첫 미션이라 많이 긴장되었지만, 그래도 비버랑 초반에 맛있는 것도 많이 먹으면서 빨리 친해져서 재밌게 할 수 있었다.

스프링을 조금 사용할 줄 알아서, 비버랑 같이 학습하면서 미션을 진행했다.
첫 미션이라 그런지 특별한 부분은 없었고, 최대한 깔끔하게 작성하려고 노력했다.
@@ -37,7 +37,7 @@ 추가적으로 알고 싶은 부분을 따로 학습하는 열정이 좋다고 생각했다.
비버와 스프링에 대해 알아가는 시간을 많이 가진 부분이 매우 좋았다.
나도 5월부터 조금 더 화이팅 해야겠다.

- - + + \ No newline at end of file diff --git a/tags/woowahan-techcourse/page/9.html b/tags/woowahan-techcourse/page/9.html index 110685dfb..71d833648 100644 --- a/tags/woowahan-techcourse/page/9.html +++ b/tags/woowahan-techcourse/page/9.html @@ -13,12 +13,12 @@ - - + +
-

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

레벨 1이 끝났다.
+

"Woowahan Techcourse" 태그로 연결된 13개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 8분

레벨 1이 끝났다.
우테코를 시작하기 전 내가 정해두었던 목표 이상으로 달성했기 때문에 매우 만족스럽다.
혼자 독학을 할 땐 이 방향으로 공부하는 게 맞는지 계속 반추하다 결국 무기력함에 빠져들었다.
하지만 이제는 같이 공부할 사람도 있고, 이야기할 사람도 있기 때문에 즐기는 일만 남은 것 같다.

Keep

나만의 루틴 만들기

스스로가 외부의 영향을 많이 받는다고 생각한다.
@@ -50,7 +50,7 @@ 블로그에 기술적인 부분을 많이 정리하지 않았는데, 조금 더 깊게 공부하고 정리하는 시간도 가져야겠다.

레벨 1을 마무리하며

시간이 빠르게 흘러갔다.
타인에게 좋은 영향을 주기위해, 방학동안 나를 챙기는 시간을 가져야겠다.
또한 함께 일하고 싶은 사람을 목표로 앞으로도 꾸준히 의식적 노력을 해야겠다.

- - + + \ No newline at end of file diff --git a/tecochat-retrospective-1.html b/tecochat-retrospective-1.html index 4c78039ed..42295a723 100644 --- a/tecochat-retrospective-1.html +++ b/tecochat-retrospective-1.html @@ -13,12 +13,12 @@ - - + +
-

[테코챗] 1. 프로토타입 만들기

· 약 6분

4월 21일 금요일

레벨 2를 시작한 뒤 내가 학습에 대한 방향을 잃어버렸다는 생각이 들었다.
+

[테코챗] 1. 프로토타입 만들기

· 약 6분

4월 21일 금요일

레벨 2를 시작한 뒤 내가 학습에 대한 방향을 잃어버렸다는 생각이 들었다.
레벨 3, 4에서 나만의 강점을 가지고 싶어 고민을 많이 했다.
단순히 스프링을 깊게 공부하는 건 효율이 많이 떨어진다고 생각했다.
글쓰기 수상으로 받은 쿠폰을 사용해 브라운에게 커피챗을 신청했고, 사이드 프로젝트를 해보라는 답을 받았다.

나는 아이디어를 못내는 편인데 브라운이 아이디어까지 던져주셨다.
@@ -41,7 +41,7 @@ 추가로 띄어쓰기도 적용되지 않아서 \n<br>태그로 변환했다.
변환하는 로직은 GPT의 도움을 많이 받았다.

const replaceCodeFences = (input: String) => {
const codeFencesRegex = /```([\w-]*)\n([\s\S]*?)\n```/g;
return input
.replace(codeFencesRegex, (match, p1, p2) => {
const languageClass = p1 ? ` class="language-${p1}"` : "";
return `<pre><code${languageClass}>${p2}</code></pre>`;
})
.replace(/\n/g, "<br>");
};

Tiptap을 적용하니 다음과 같이 깔끔한 코드 블록을 볼 수 있었다.

tecochat

폰트 및 favicon 적용

타이틀은 배달의민족 도현체, 내용은 IBM Plex Sans를 사용했다.
추가로 favicon도 간단하게 적용해서 만족스러웠다.

- - + + \ No newline at end of file diff --git a/tecochat-retrospective-2.html b/tecochat-retrospective-2.html index 7b74deef9..bd21113e2 100644 --- a/tecochat-retrospective-2.html +++ b/tecochat-retrospective-2.html @@ -13,12 +13,12 @@ - - + +
-

[테코챗] 2. 배포

· 약 5분

프론트엔트

닉네임을 입력하여 간단히 로그인하는 화면, 채팅 목록을 보여주는 화면도 만들었고 단일 채팅을 확인할 수 있는 화면도 만들었다.
+

[테코챗] 2. 배포

· 약 5분

프론트엔트

닉네임을 입력하여 간단히 로그인하는 화면, 채팅 목록을 보여주는 화면도 만들었고 단일 채팅을 확인할 수 있는 화면도 만들었다.
추가로 채팅을 이어나갈 수 있게 하는 기능도 추가했다.
자잘하게 신경 쓸 부분이 많아서, 프론트엔드 하는 사람들이 대단하다고 생각되었다.
여유가 된다면 자신의 채팅을 볼 수 있는 기능이나, 채팅을 이어서 할 수 있는 기능, 댓글 기능도 추가할 예정이다.

백엔드

최대한 빨리 서비스를 크루들에게 제공하기로 정해서, 백엔드는 말랑이 일단 다 만들고 있다.
@@ -37,7 +37,7 @@ 오늘 적용해 보니 램이 부족하여 중간에 잘 안되기도 하고 그래서 그냥 "Pipeline만 사용할 걸 그랬나?" 라는 생각이 든다.

참고 자료

Elastic Beanstalk, AWS
EC2 AWS Graviton, AWS
Default Memory Settings, AWS

- - + + \ No newline at end of file diff --git a/tecochat-retrospective-3.html b/tecochat-retrospective-3.html index fec907635..6a9c0cb76 100644 --- a/tecochat-retrospective-3.html +++ b/tecochat-retrospective-3.html @@ -13,12 +13,12 @@ - - + +
-

[테코챗] 3. 기능 구현

· 약 5분

개요

원래 목적인 크루들의 학습에 도움을 주기 위해 어떤 기능을 추가해야 할지 고민을 많이 했다.
+

[테코챗] 3. 기능 구현

· 약 5분

개요

원래 목적인 크루들의 학습에 도움을 주기 위해 어떤 기능을 추가해야 할지 고민을 많이 했다.
레벨 2가 거의 끝나가는 시점, 그동안 했던 것을 정리해 보려고 한다.

나의 채팅 확인하고 이어하는 기능

GPT에도 있는 기능인데, 내가 이전에 했던 채팅을 이어할 수 있는 기능을 추가했다.
예전에 어떤 질문을 남겼는지, 또한 해당 채팅을 이어서 할 수 있다.

chat1

좋아요와 댓글 기능

다른 사람들이 질문한 내용에 반응할 수 있는 무언가가 있었으면 좋겠다는 의견들이 많았다.
누가 좋아요를 눌렀는지, 어떤 채팅이 좋아요를 가장 많이 받았는지 확인할 수 있는 기능을 추가했다.
@@ -32,7 +32,7 @@ 제일 하고 싶은 것은 실제 GPT를 사용하는 것처럼 stream/text 값을 처리하고 싶은데 이 부분은 방학 때 기회가 되면 도전해 봐야겠다.

향후 계획

실제 크루들이 사용해 주는 서비스를 직접 만들어보면서 사용자의 입장에서 고민도 하게 되는 것 같다.
크루들이 직접 사용해 주니까 너무 고맙고, 한편으로는 신기하다.
일단 방학 때 stream/text 관련된 부분 동작되도록 구현해보려고 하고, 그 외의 부분은 조금 더 고민해야될 것 같다.

- - + + \ No newline at end of file diff --git a/test-double.html b/test-double.html index 498cdd37f..a172a23c8 100644 --- a/test-double.html +++ b/test-double.html @@ -13,12 +13,12 @@ - - + +
-

테스트 대역

· 약 5분

테스트 대역이란?

모든 유형의 테스트를 위한 가짜 의존성을 의미하고, 테스트가 실행될 때 다른 객체를 대신한다.
+

테스트 대역

· 약 5분

테스트 대역이란?

모든 유형의 테스트를 위한 가짜 의존성을 의미하고, 테스트가 실행될 때 다른 객체를 대신한다.
Gerard Meszaros의 xUnit Test Patterns라는 책에서는 테스트 대역을 다섯 가지(더미, 스텁, 스파이, 목, 페이크)로 구분한다.

테스트 대역의 기본 메커니즘은 다형성을 이용하는 방법이다.
외부 서비스를 사용하는 코드를 테스트 하는 경우, 인터페이스를 정의하고 외부 서비스 대신 테스트 용도의 구현체를 생성하는 것이다.

테스트 대역의 타입 계층 구조

더미(Dummy)

가장 단순하고, 원시적인 유형의 테스트 대역이다.
기본적으로 아무 일도 하지 않는 구현체로 인스턴스화가 필요한 경우 사용한다.
@@ -36,7 +36,7 @@ 테스트 더블, Martin Fowler
테스트 관련 용어 정리, Johngrib
Test Double, Gerard Meszaros

- - + + \ No newline at end of file diff --git a/the-essence-of-object-orientation.html b/the-essence-of-object-orientation.html index 824a2215a..7ef016a8e 100644 --- a/the-essence-of-object-orientation.html +++ b/the-essence-of-object-orientation.html @@ -13,12 +13,12 @@ - - + +
-

[책] 객체지향의 사실과 오해

· 약 6분

책 정보

객체지향의 사실과 오해
+

[책] 객체지향의 사실과 오해

· 약 6분

책 정보

객체지향의 사실과 오해
조영호

읽고 나서

조영호님의 오브젝트를 읽고 나서 다시 한 번 읽어보았다.
아직 이해가 안되는 부분이 많지만, 그래도 항상 새로움을 느낀다.
더할 나위 없이 휼륭한 객체지향 책이고, 조금 더 공부하고 다시 읽어봐야될 것 같다.

커피 전문점, 지하철 노선도, 이상한 나라의 엘리스를 예시로 든 설명이 너무 좋았고
@@ -38,7 +38,7 @@ ’어떤 행위(What)’를 수행할 것인지 결정한 후 ‘누가(who)’ 그 행위를 수행할 것인지 결정해야 한다. 여기서 ‘어떤 행위’가 바로 메시지다. p.158

- - + + \ No newline at end of file diff --git a/tomcat-retrospective.html b/tomcat-retrospective.html index c1ed51e3b..f752bab82 100644 --- a/tomcat-retrospective.html +++ b/tomcat-retrospective.html @@ -13,12 +13,12 @@ - - + +
-

톰캣 구현 미션 회고

· 약 13분

톰캣 구현

우아한테크코스를 지원할 때 객체지향과 관련된 미션도 기대를 많이 했지만 레벨 4에 진행하는 미션이 정말 하고 싶었다.
+

톰캣 구현 미션 회고

· 약 13분

톰캣 구현

우아한테크코스를 지원할 때 객체지향과 관련된 미션도 기대를 많이 했지만 레벨 4에 진행하는 미션이 정말 하고 싶었다.
그래서 미션을 할 수 있을까라는 걱정 반, 미션에 대한 기대 반으로 부푼 마음을 가지고 미션을 시작했던 것 같다.

이번 미션에서는 적절하게 추상화하고, 미션의 본질을 이해하려고 노력했다.
톰캣 구현 미션은 RFC 2616에 명시된 스펙(완벽하지 않지만 미션에서 주어진 요구사항만 만족하도록)으로 요청을 받아 처리 후 반환하는데 집중했다.

다이어그램

Catalina는 Tomcat의 서블릿 컨테이너, Coyote는 HTTP 1.1 웹 서버를 지원하는 구성 요소라고 생각하고 아래와 같이 구성했다.
사실 내부 구조를 깊게 공부할 시간을 가지지 못해서 각 구성 요소가 왜 해당 위치에 있는지 완벽하게 설명하지는 못하지만 미션을 진행하면서 이건 여기에 있으면 좋을 것 같은데? 라는 생각이 들면 적절한 패키지에 위치시키는 방향으로 진행을 했다.
@@ -46,8 +46,8 @@ ETag, mdn
Apache Tomcat 8 Configuration Reference
Apache Tomcat Tuning, Terry Cho
-maxThreads, maxConnections, acceptCount로 Tomcat 튜닝하기

- - +maxThreads, maxConnections, acceptCount로 Tomcat 튜닝하기

+ + \ No newline at end of file diff --git a/transaction-and-isolation.html b/transaction-and-isolation.html index 788d81de4..f2e681b35 100644 --- a/transaction-and-isolation.html +++ b/transaction-and-isolation.html @@ -13,12 +13,12 @@ - - + +
-

트랜잭션과 격리수준

· 약 10분

트랜잭션(Transaction)

데이터베이스에서 논리적 기능을 수행하기 위한 작업의 단위를 말한다.
+

트랜잭션과 격리수준

· 약 10분

트랜잭션(Transaction)

데이터베이스에서 논리적 기능을 수행하기 위한 작업의 단위를 말한다.
트랜잭션은 작업의 완전성과 데이터의 정합성을 보장해 준다.
논리적인 작업 셋을 완벽하게 처리하거나, 오류 시 작업의 일부만 적용되는 현상을 막아준다.

트랜잭션의 속성(ACID)

원자성(Atomicity): 트랜잭션 내에서 실행된 작업들은 모두 성공하거나, 실패해야 한다.
일관성(Consistency): 트랜잭션이 수행되기 전과 후에 데이터베이스가 일관된 상태를 유지해야 한다.
@@ -42,7 +42,7 @@ 예) A가 레코드를 여러 번 조회하던 중 B가 레코드를 변경하여 A가 조회한 값이 달라지는 경우

팬텀 리드(Phantom read, Phantom row)

한 트랜잭션 내에서 동일한 쿼리 수행시, 수행 결과가 다른 현상
예) A가 레코드를 조회하고 B가 레코드를 추가하여 A가 다시 조회할 때 존재하지 않은 레코드가 조회되는 경우

참고 자료

Real My SQL 8.0 - 5장 트랜잭션과 잠금, 백은빈, 이성욱
Isolation Level, MySQL

- - + + \ No newline at end of file diff --git a/web-racing-car-retrospective.html b/web-racing-car-retrospective.html index 09db047a7..d58b3b426 100644 --- a/web-racing-car-retrospective.html +++ b/web-racing-car-retrospective.html @@ -13,12 +13,12 @@ - - + +
-

웹 자동차 미션 회고

· 약 4분

웹 자동차 미션

사이드 프로젝트를 한다고 시간이 많이 없어서 회고가 늦어졌다.
+

웹 자동차 미션 회고

· 약 4분

웹 자동차 미션

사이드 프로젝트를 한다고 시간이 많이 없어서 회고가 늦어졌다.
웹 자동차 미션에서는 비버와 페어가 매칭되었다.
레벨 2에서 진행하는 첫 미션이라 많이 긴장되었지만, 그래도 비버랑 초반에 맛있는 것도 많이 먹으면서 빨리 친해져서 재밌게 할 수 있었다.

스프링을 조금 사용할 줄 알아서, 비버랑 같이 학습하면서 미션을 진행했다.
첫 미션이라 그런지 특별한 부분은 없었고, 최대한 깔끔하게 작성하려고 노력했다.
@@ -37,7 +37,7 @@ 추가적으로 알고 싶은 부분을 따로 학습하는 열정이 좋다고 생각했다.
비버와 스프링에 대해 알아가는 시간을 많이 가진 부분이 매우 좋았다.
나도 5월부터 조금 더 화이팅 해야겠다.

- - + + \ No newline at end of file diff --git a/websocket.html b/websocket.html index a0c54c138..c39f9c68e 100644 --- a/websocket.html +++ b/websocket.html @@ -13,12 +13,12 @@ - - + +
-

웹소켓

· 약 5분

웹소켓

단일 TCP 연결을 통해 클라이언트와 서버 간 전이중 양방향 통신을 지원하는 프로토콜
+

웹소켓

· 약 5분

웹소켓

단일 TCP 연결을 통해 클라이언트와 서버 간 전이중 양방향 통신을 지원하는 프로토콜
웹 환경에서 연속된 데이터를 실시간으로 처리할 수 있다.

웹소켓은 HTTP의 포트를 그대로 사용하고 각각 포트 80과 포트 443을 사용하여 HTTP(ws://) 및 HTTPS(wss://)로 서버에 연결한다.

웹소켓 등장 배경

웹소켓이 등장하기 이전, 실시간성을 보장하기 위해 Polling, Long polling, Streaming 같은 기술을 사용했어야 했다.
이는 실시간성이나 양방향성을 만족시키지 못했고, HTTP를 이용하기 때문에 과도한 오버헤드가 발생했다.

polling, long polling, streaming

Polling: 주기적으로 서버에 요청을 보내 수신할 정보가 있는지 확인하는 방법

  • 서버에서 보낼 내용이 없어도 클라이언트는 알 수 없다.
  • 계속해서 요청을 보내 확인을 해야하기 때문에 서버에 불필요한 부하를 주어야 한다.

Long Polling: 클라이언트의 요청에 대해 응답을 보내지 않고 있다가 이벤트가 발생했을때 응답하는 방법

  • 폴링 방식보다 서버에 적은 부하를 줄 수 있지만, 요청의 주기가 짧으면 폴링과 차이가 없어진다.

Streaming: 클라이언트가 request를 보내면 커넥션을 맺고, 이 커넥션을 유지하면서 서버가 계속 데이터를 보내는 방법

  • 클라이언트가 서버에 요청을 하고 싶다면 새로운 커넥션을 맺어야 한다.

웹소켓의 동작

1. Upgrade 요청

WebSocket 프로토콜로 전환하는 HTTP 요청을 보낸다.
이는 HTTP와 같이 80, 443 포트를 사용한다.
@@ -32,7 +32,7 @@ https://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
https://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
https://docs.spring.io/spring-framework/reference/web/websocket.html

- - + + \ No newline at end of file diff --git a/woowacourse-level1-retrospective.html b/woowacourse-level1-retrospective.html index 23085b426..38713bba8 100644 --- a/woowacourse-level1-retrospective.html +++ b/woowacourse-level1-retrospective.html @@ -13,12 +13,12 @@ - - + +
-

우아한테크코스 레벨 1 회고

· 약 8분

레벨 1이 끝났다.
+

우아한테크코스 레벨 1 회고

· 약 8분

레벨 1이 끝났다.
우테코를 시작하기 전 내가 정해두었던 목표 이상으로 달성했기 때문에 매우 만족스럽다.
혼자 독학을 할 땐 이 방향으로 공부하는 게 맞는지 계속 반추하다 결국 무기력함에 빠져들었다.
하지만 이제는 같이 공부할 사람도 있고, 이야기할 사람도 있기 때문에 즐기는 일만 남은 것 같다.

Keep

나만의 루틴 만들기

스스로가 외부의 영향을 많이 받는다고 생각한다.
@@ -50,7 +50,7 @@ 블로그에 기술적인 부분을 많이 정리하지 않았는데, 조금 더 깊게 공부하고 정리하는 시간도 가져야겠다.

레벨 1을 마무리하며

시간이 빠르게 흘러갔다.
타인에게 좋은 영향을 주기위해, 방학동안 나를 챙기는 시간을 가져야겠다.
또한 함께 일하고 싶은 사람을 목표로 앞으로도 꾸준히 의식적 노력을 해야겠다.

- - + + \ No newline at end of file diff --git a/woowacourse-level2-retrospective.html b/woowacourse-level2-retrospective.html index 89cd1f4aa..6e2316a0f 100644 --- a/woowacourse-level2-retrospective.html +++ b/woowacourse-level2-retrospective.html @@ -13,12 +13,12 @@ - - + +
-

우아한테크코스 레벨 2 회고

· 약 3분

23년의 6월이 오고, 레벨 2가 끝났다.
+

우아한테크코스 레벨 2 회고

· 약 3분

23년의 6월이 오고, 레벨 2가 끝났다.
빠르게 지나가서 조금 아쉽다.

학습

회고를 작성하기 전에 레벨 2 동안 보냈던 PR과 회고를 쭉 읽어봤다.
항상 아쉬운 곳은 있기 마련이지만, 잘 학습한 것 같다.
미션을 하면서 기술을 어떻게 선택하고, 적용할 것인지 고민하는 과정에서 꽤나 많은 성장을 한 것 같다.

고민은 깊었지만 이론적인 학습이 부족한 레벨 2였다.
@@ -29,7 +29,7 @@ 이번에는 프런트엔드 크루와 협업을 했다. 소통은 잘 된 것 같지만 API 명세를 정하는 부분이 아직 미숙한 것 같다.

레벨 3 때부터 본격적으로 프로젝트가 시작된다.
팀을 위해 어떤 것을 할 수 있을지 고민을 많이 해봐야겠다.

레벨 2를 마무리하며

회고 작성하면서 레벨 2에서 했던 것들을 반추해 봤는데 부족한 점은 많았어도 좋은 방향으로 가고 있는 것 같다. 읽고 싶은 책도 읽고, 부족한 부분 채우면서 쉬어야겠다.

- - + + \ No newline at end of file diff --git a/woowacourse-level3-retrospective.html b/woowacourse-level3-retrospective.html index 8661fb00b..5af019860 100644 --- a/woowacourse-level3-retrospective.html +++ b/woowacourse-level3-retrospective.html @@ -13,12 +13,12 @@ - - + +
-

우아한테크코스 레벨 3 회고

· 약 4분

회고

지난 8주는 레벨 1, 2 때보다 5배 정도 빠르게 지나간 것 같은 느낌이 들었다.
+

우아한테크코스 레벨 3 회고

· 약 4분

회고

지난 8주는 레벨 1, 2 때보다 5배 정도 빠르게 지나간 것 같은 느낌이 들었다.
레벨 3에는 기술적인 부분에서도, 기술 외적인 부분에서도 부족함이 많이 보였던 것 같다.
부족한 부분을 알았기에, 앞으로 더욱 성장할 수 있을 것 같다.
내가 부족했던 부분을 팀원들이 잘 보충해 줘서 든든했다.

아쉬운 점

문서화

개인적으로는 기술 외적으로 학습한 부분을 잘 정리하지 못했다.
@@ -33,7 +33,7 @@ 아래의 D 부분은 유튜브 강의 들으면서 직접 만들어서 뿌듯하다.

기술 선택의 이유

기술의 학습 비용, 현재 구조에 적합한지, 실제 가지고 있는 리소스를 고려해서 기술 선택을 하고, 도입했던 부분이 좋았다.
100% 좋은 선택일 순 없지만, 그래도 선택에 대한 근거가 존재한다면 확률을 높혀주는 것 같다.

마치며

플레이스토어에 앱이 올라가 있는 거 너무 신기하다.
안드로이드 브레멘 음악대(멧돼지, 수달, 핑구), 그리고 백엔드 팀원들(체인저, 후추, 리오) 너무 고생이 많았다.

- - + + \ No newline at end of file