Title: 아이템 76 : 가능한 한 실패 원자적으로 만들라 · Study-2-Effective-Java · Discussion #176 · GitHub
Open Graph Title: 아이템 76 : 가능한 한 실패 원자적으로 만들라 · Study-2-Effective-Java · Discussion #176
X Title: 아이템 76 : 가능한 한 실패 원자적으로 만들라 · Study-2-Effective-Java · Discussion #176
Description: 아이템 76 : 가능한 한 실패 원자적으로 만들라
Open Graph Description: 0. 들어가며 이 아이템이 말하는 바는 짧고 명확해서, 아이템 이름만 봐도 명확하게 보입니다. 저는 “가능한 한 실패 원자적으로 만들라” 라고 핵심 키워드를 꼽았습니다. 가능한 : 반드시 해야한다는건 아니지만, 실패 원자적 : 실패 상황에도 정상적인 흐름으로 흘러가게 하라. 그런데 실패 원자적이라는 말이 확 와닿지는 않는데요. 이를 책에서는 다음과 같...
X Description: 0. 들어가며 이 아이템이 말하는 바는 짧고 명확해서, 아이템 이름만 봐도 명확하게 보입니다. 저는 “가능한 한 실패 원자적으로 만들라” 라고 핵심 키워드를 꼽았습니다. 가능한 : 반드시 해야한다는건 아니지만, 실패 원자적 : 실패 상황에도 정상적인 흐름으로 흘러가게 하라. 그런데 실패 원자적이라는 말이 확 와닿지는 않는데요. 이를 책에서는 다음과 같...
Opengraph URL: https://github.com/orgs/Study-2-Effective-Java/discussions/176
X: @github
Domain: patch-diff.githubusercontent.com
{"@context":"https://schema.org","@type":"QAPage","mainEntity":{"@type":"Question","name":"아이템 76 : 가능한 한 실패 원자적으로 만들라","text":"0. 들어가며
\n이 아이템이 말하는 바는 짧고 명확해서, 아이템 이름만 봐도 명확하게 보입니다.
\n저는 “가능한 한 실패 원자적으로 만들라” 라고 핵심 키워드를 꼽았습니다.
\n\n- 가능한 : 반드시 해야한다는건 아니지만,
\n- 실패 원자적 : 실패 상황에도 정상적인 흐름으로 흘러가게 하라.
\n
\n그런데 실패 원자적이라는 말이 확 와닿지는 않는데요.
\n이를 책에서는 다음과 같이 말합니다.
\n\n호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야 한다.
\n
\n어떻게 원자적으로 만들 수 있는지, 해야하는 상황과 아닌 상황은 어떻게 구분하는지 아래에서 알아보겠습니다.
\n1. 실패 원자적으로 만드는 방법
\n1.1 불변
\n사실, 불변객체인 경우에는 실패원자적으로 만들기가 쉽습니다.
\n왜냐하면, 불변 객체인 경우에는 상태를 변경할때 새 객체를 생성하기 때문입니다.
\n그래서 실패하더라도 현재 객체는 변경되지 않기 때문에, 자연스럽게 실패 원자성을 가지게 되죠.
\nrecord ImmutablePerson(String name, int age) {\n\t\n\tpublic ImmutableName changeAge(int newAge) {\n\t\tif (newAge <= 0) throw new IllegalArgumentException();\n\t\treturn new ImmutableName(this.name, newAge);\n\t}\n}
\n위와 같은 불변 객체는 상태를 변경할때 새로운 객체를 반환합니다.
\n만약 changeAge를 호출할때 0이하의 나이를 주는 경우에 예외가 발생하는데요.
\n이 경우에는 해당 메서드 자체만 실패하는 것이지, 기존 객체는 정상적인 상태가 유지됩니다.
\n1.2 가변
\n가변인 경우는 조금 더 복잡할 수 밖에 없는데요.
\n미리 검증을 통해 상태 변경을 막거나, 변경을 되돌리는 작업이 있어야 하기 때문입니다.
\n그래서 책에서는 제시하는 가변 객체 실패 원자성 유지 방법은 아래 4가지 입니다.
\n\n- 상태 변경에 앞서 매개변수 유효성을 검사하는 것\n
\n- 아이템 49와 연관있음
\n
\n \n- 실패 가능성이 있는 코드를, 상태 변경 로직보다 앞에 배치하는 것
\n- 복사본에서 작업 수행 후, 성공하면 원래 객체와 교체하는 방법
\n- 실패를 가로채는 복구코드를 작성하여, 작업 전 상태로 되돌리는 방법\n
\n- DB트랜잭션 등
\n
\n \n
\n1) 상태 변경에 앞서 매개변수 유효성을 검사하는 것
\n이 항목은 사실 위의 불변 예제와 거의 비슷할 것 입니다.
\n다만, 차이점은 새로운 객체를 반환하는것이 아니라 자기 자신의 상태를 변경한다는 점이겠죠.
\nclass MutablePerson {\n\tprivate String name;\n\tprivate int age;\n\n\tpublic MutableName(String name, int age) { \n\t\tthis.name = name;\n\t\tthis.age = age;\n\t}\n\t\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\tpublic String getAge() {\n\t\treturn this.age;\n\t}\n\n\tpublic void changeAge(int newAge) {\n\t\tif (newAge <= 0) throw new IllegalArgumentException();\n\t\t\n\t\tthis.age = newAge;\n\t}\n}
\n위 처럼, 상태 변경 전에 매개변수 유효성을 검증하고 예외를 발생시켜 원자성을 유지할 수 있습니다.
\n2) 실패 가능성이 있는 코드를, 상태 변경 로직보다 앞에 배치하는 것
\nclass Processor {\n\t// ...\n\n\tpublic void process(UserCommand command) {\n\t\tthis.status = Status.SUCCESS;\n\n\t\tlog.info(\"[{}] run command\", command.getUser()) // command 가 null 이라면??\n\t\tthis.run(command.getCommand());\n\t}\n\n\tprivate void run(Command command) {\n\t\t// ...\n\t}\n}\n\n-----\n// 이렇게 바꾸면 이전보다는 나음\n\nclass Processor {\n\t// ...\n\n\tpublic void process(UserCommand command) {\n\t\tlog.info(\"[{}] run command\", command.getUser())\n\t\tthis.run(command.getCommand());\n\n\t\tthis.status = Status.SUCCESS;\n\t}\n\n\tprivate void run(Command command) {\n\t\t// ...\n\t}\n}
\n위쪽 예제는 command가 null일때 NullPointerException이 발생합니다.
\n그러면 상태는 SUCCESS인 상태로 멈춰버리게 되는데요.
\n하지만, 상태 변경로직보다 앞에 둔다면 실패했는데 SUCCESS라고 되진 않을 것 입니다.
\n2. 실패 원자성 구현은 필수?
\n하지만, 항상 실패 원자성을 구현해야하는건 아니라고 하는데요.
\n2.1 할 수 없는 경우 & 안해도 되는 경우
\n만약 멀티 스레드에서의 경쟁 상태(Race condition)에 놓였을 경우에는 해결하기 힘들겠죠.
\n\n\n- 하나만 남기고 취소해야하나?\n
\n- 그럼 어떤 기준으로 누굴 취소하지?
\n
\n \n- 둘다 취소해야하나?\n
\n- 데이터가 어떻게 변했는지 보고 복원 해야하나?
\n
\n \n
\n여러 경우의 수를 고려해야하겠지만, 위와 같이 대상이 많지 않은 경우라면 괜찮을 것 입니다.
\n하지만 아래와 같이 복잡하고 대상이 많은 경우라면 어떨까요?
\n\n이런 상황에서 원자성 달성을 위한 로직을 개발하는건 비용이 크고 복잡할 것 입니다.
\n그리고 책에서는 문제가 무엇인지 아록 나면 실패 원자성을 공짜로 얻을 수 있는 경우가 있다고 하는데요.
\n위 상황에서의 문제는 여러 스레드가 한 데이터에 접근하면서 발생한 문제입니다.
\n이와 같은 경우, ‘임계 구역’을 설정하면 해결될 수 있는 상황이기도 하죠.
\n이렇게 하면 큰 변경없이도 실패 원자성을 얻을 수 있게 됩니다.
\n3. 실패 원자성 명세
\n실패 원자성 여부는 특정 메서드를 사용하는 입장에서 중요한 정보입니다.
\n그래서 책에서는 메서드 명세에 기술되어있는 예외는 발생하더라도 원자성을 지켜줘야한다고 합니다.
\n그리고 원자성을 지켜주지 못한다면, 예외 발생시 어떤 상태가 되는지를 명시해야한다고 하는데요.
\n하지만 이런건 이상적인 얘기일 뿐, 잘 지켜지지는 않는다고 합니다.
","upvoteCount":1,"answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"\n이런건 이상적인 얘기일 뿐, 잘 지켜지지는 않는다고 합니다.
\n
\n논리적 관점과 물리적 관점이 맞지않는 그런 상황인가 봅니다 ㅎㅎ
\n\n구현부 위 아래로 바꾸는건 위트있는 코드 같군여 😄
\n이펙자바가 이제 끝시점으로 달려가고 있다 보니, 여러 개념이 중첩되고는 하는 것 같습니다.
\n꽤 많은 아이템에서 다뤄지는 내용 중 하나를 꼽아 저도 아이템을 제안 해 보고싶네요.
\n왠만하면 불변으로 만들어라\n
","upvoteCount":1,"url":"https://github.com/orgs/Study-2-Effective-Java/discussions/176#discussioncomment-5441852"}}}
| route-pattern | /_view_fragments/Voltron::DiscussionsFragmentsController/show/orgs/:org/:discussion_number/discussion_layout(.:format) |
| route-controller | voltron_discussions_fragments |
| route-action | discussion_layout |
| fetch-nonce | v2:f4958106-b005-9d18-fbaa-cd6df16344d1 |
| current-catalog-service-hash | 9f0abe34da433c9b6db74bffa2466494a717b579a96b30a5d252e5090baea7be |
| request-id | B89A:382E6F:5B40C8:80C4F2:696E5895 |
| html-safe-nonce | 60d2715bae7c8777ac36ce0142907a071df2e2d8b71e1c149201927a3c667d81 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJCODlBOjM4MkU2Rjo1QjQwQzg6ODBDNEYyOjY5NkU1ODk1IiwidmlzaXRvcl9pZCI6IjM0ODI4NjYxMTExMTc5NDA4ODUiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 9ad931978901c82599df180b338b610bf2fb4894cb9badbeee7684d6e5946ef1 |
| hovercard-subject-tag | discussion:5002332 |
| github-keyboard-shortcuts | repository,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/Voltron::DiscussionsFragmentsController/show/orgs/Study-2-Effective-Java/176/discussion_layout |
| twitter:image | https://opengraph.githubassets.com/12df992beeec6e2df475a191e061f5701406a50c041e48714b879081b88ee40c/orgs/Study-2-Effective-Java/discussions/176 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/12df992beeec6e2df475a191e061f5701406a50c041e48714b879081b88ee40c/orgs/Study-2-Effective-Java/discussions/176 |
| og:image:alt | 0. 들어가며 이 아이템이 말하는 바는 짧고 명확해서, 아이템 이름만 봐도 명확하게 보입니다. 저는 “가능한 한 실패 원자적으로 만들라” 라고 핵심 키워드를 꼽았습니다. 가능한 : 반드시 해야한다는건 아니지만, 실패 원자적 : 실패 상황에도 정상적인 흐름으로 흘러가게 하라. 그런데 실패 원자적이라는 말이 확 와닿지는 않는데요. 이를 책에서는 다음과 같... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | f68b42d371252b0f236260d6234f4304a806fe5ac43d59faa21fb59d80df103b |
| turbo-cache-control | no-preview |
| octolytics-dimension-user_id | 120388640 |
| octolytics-dimension-user_login | Study-2-Effective-Java |
| octolytics-dimension-repository_id | 577325341 |
| octolytics-dimension-repository_nwo | Study-2-Effective-Java/Effective-Java |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 577325341 |
| octolytics-dimension-repository_network_root_nwo | Study-2-Effective-Java/Effective-Java |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 6b74bc8dbcd10b5d69fd9ee9d2cfdc8b35e18a4c |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width