포스트

2023-01-18-TIL

2023-01-18-TIL

Today I Learned

로그 보관 기준, 권한 테이블 모델링, JPA의 Found shared references to a collection 오류를 정리했다.


로그 보관 주기

로그를 얼마나 보관할지는 서비스 정책 이전에 법적 요구사항으로 정해지는 부분이 있다. ISMS-P 인증 기준 2.9.4(로그 및 접속기록 관리)는 정보시스템의 로그 유형, 보존 기간, 보존 방법을 정하고 위·변조, 도난, 분실되지 않도록 안전하게 보관하라고 요구한다. 개인정보처리시스템의 접속기록은 「개인정보의 안전성 확보조치 기준」에 따라 보존 기간이 정해진다. itwiki 정리에 따르면 5만 명 이상의 정보주체 개인정보를 처리하거나, 고유식별정보·민감정보를 처리하거나, 기간통신사업자에 해당하면 최소 2년, 그 밖에는 최소 1년 이상이다.

기준은 개정될 수 있으므로 실제 정책을 정할 때는 고시 원문을 확인해야 한다. 또 접속기록은 개인정보처리시스템이 아닌 별도 저장소에 백업해 위·변조를 막고, 로그에 불필요한 개인정보가 남지 않도록 기록 항목을 검토하는 것까지 기준에 포함된다.

  • https://itwiki.kr/w/ISMS-P_%EC%9D%B8%EC%A6%9D_%EA%B8%B0%EC%A4%802.9.4.%EB%A1%9C%EA%B7%B8%EB%B0%8F_%EC%A0%91%EC%86%8D%EA%B8%B0%EB%A1%9D_%EA%B4%80%EB%A6%AC

Permission Table Modeling

RBAC(Role-Based Access Control)를 테이블로 옮기면 보통 사용자, 역할, 권한 세 엔티티와 그 사이의 다대다 연결 테이블로 구성한다. 사용자는 역할을 갖고, 역할은 권한 묶음을 가지며, 권한 검사는 “사용자 → 역할 → 권한”을 따라가 확인한다.

1
users ─< user_roles >─ roles ─< role_permissions >─ permissions

권한을 사용자에게 직접 주는 예외가 필요하면 user_permissions 테이블을 추가하기도 하지만, 그만큼 권한의 출처를 추적하기 어려워진다. 권한은 resource + action(예: POST:CREATE) 형태로 정의하면 화면이나 API가 늘어나도 일관되게 관리할 수 있다.

  • https://stackoverflow.com/questions/3686516/db-schema-of-a-role-based-access-control
  • https://www.researchgate.net/figure/The-database-schema-for-users-roles-and-permission_fig5_352559713

Found shared reference to a collection

Hibernate는 영속 컬렉션 인스턴스 하나가 한 엔티티의 한 필드에만 속한다고 가정하고, flush 시점에 두 곳 이상에서 같은 컬렉션 인스턴스를 참조하면 HibernateException: Found shared references to a collection을 던진다. 흔한 원인은 한 엔티티의 컬렉션을 다른 엔티티에 그대로 대입하는 경우(b.setTags(a.getTags()))로, 새 컬렉션을 만들어 원소를 복사하면 해결된다.

링크된 J4J 글은 이와 다른 사례를 보고한다. 같은 엔티티를 향하는 두 @OneToMany 연관관계가 같은 데이터를 가리키는 상황에서 QueryDSL용 쿼리 리포지토리로 조회할 때 이 오류가 났고, JpaRepository로는 발생하지 않았다고 한다.

  • https://jforj.tistory.com/270
  • https://stackoverflow.com/questions/1692871/found-shared-references-to-a-collection-org-hibernate-hibernateexception
  • https://m.blog.naver.com/ndrsgrwh4/221731486601
  • https://slothink.tistory.com/92
  • https://stackoverflow.com/questions/61196298/org-hibernate-hibernateexception-found-shared-references-to-a-collection
  • https://stackoverflow.com/questions/61077311/spring-data-jpa-found-shared-references-to-a-collection-org-hibernate-hibernat
  • https://docs.oracle.com/javaee/6/api/javax/persistence/OneToMany.html

Today I Studied

Integer.parseInt() vs Integer.valueOf()

parseInt()는 기본형 int를, valueOf()는 Integer 객체를 돌려준다. Integer.valueOf(int) Javadoc은 이 메서드가 -128부터 127까지의 값을 항상 캐시하고, 그 밖의 값도 캐시할 수 있다고 명시한다. 그래서 이 범위의 값은 valueOf()로 얻은 객체끼리 == 비교가 true가 되지만, 범위를 벗어나면 false가 될 수 있다. 숫자 계산만 필요하면 parseInt(), 컬렉션에 넣을 객체가 필요하면 valueOf()를 쓰고, 객체 비교는 항상 equals()로 한다.

  • https://m.blog.naver.com/sthwin/221000179980

Stratified Design vs Layered Design

Eric Normand는 계층화 설계(stratified design)를 코드를 의미의 층으로 쌓는 방식으로 설명한다. 아래층은 일반적이고 안정적인 연산을, 위층은 아래층의 연산을 조합한 도메인 개념을 표현하며, 각 층은 바로 아래층의 어휘만으로 쓰여야 한다. 컨트롤러-서비스-리포지토리처럼 기술적 역할로 나누는 전통적인 계층형 구조와 달리, 추상화 수준에 따라 함수를 나눈다는 점이 다르다.

  • https://medium.com/clean-code-development/stratified-design-over-layered-design-125727c7e15
  • https://ericnormand.me/podcast/more-about-stratified-design
  • https://gist.github.com/ralfw/363cc2a4f4a8dd9e19f28586c8bcd39b

English Dictionary

영어 표현을 찾아볼 때 쓸 검색 엔진으로 you.com을 메모해 두었다.

  • https://you.com/

Retrofit in Android Programming

Retrofit은 Square가 만든 HTTP 클라이언트 라이브러리로, API를 인터페이스와 애너테이션(@GET, @Path 등)으로 선언하면 구현체를 만들어 준다. Spring Cloud OpenFeign과 같은 선언적 방식이다.

  • https://salix97.tistory.com/204

선언적 UI

명령형 UI는 “버튼을 찾아 텍스트를 바꾸고 색을 바꿔라”처럼 화면을 바꾸는 절차를 코드로 쓴다. 선언적 UI는 “상태가 이렇다면 화면은 이렇게 생겼다”를 기술하고, 상태가 바뀌면 프레임워크가 화면을 다시 그린다. React, Jetpack Compose, SwiftUI가 이 방식을 따른다. 상태와 화면이 하나의 함수 관계로 묶이므로, 화면 갱신을 빠뜨리는 종류의 버그가 줄어든다.

  • https://medium.com/@kimdohun0104/%EC%82%AC%EB%9E%8C%EB%93%A4%EC%9D%80-%EC%99%9C-%EC%84%A0%EC%96%B8%ED%98%95-ui%EC%97%90-%EC%97%B4%EA%B4%91%ED%95%A0%EA%B9%8C-1440d03f4e49
  • https://selfish-developer.com/entry/%EB%AA%85%EB%A0%B9%ED%98%95-UI-vs-%EC%84%A0%EC%96%B8%ED%98%95-UI
  • https://velog.io/@ricky_0_k/%EC%9D%98%EC%8B%9D%EC%9D%98-%ED%9D%90%EB%A6%84%EC%9C%BC%EB%A1%9C-%EC%84%A0%EC%96%B8%ED%98%95-%EC%84%A0%EC%96%B8%ED%98%95-UI-%EA%B0%9C%EB%85%90-%EC%9E%AC%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0

Stateless in Android Programming

선언적 UI에서 컴포넌트를 상태 없이(stateless) 만든다는 것은 컴포넌트가 상태를 직접 들고 있지 않고 값과 이벤트 콜백을 파라미터로 받는다는 뜻이다. 상태는 상위로 끌어올려(state hoisting) 한곳에서 관리하므로, 컴포넌트는 재사용과 테스트가 쉬워진다.

  • https://myseong.tistory.com/47

React Hooks

React 문서에 따르면 Hooks는 React 16.8에 추가된 기능으로, 클래스를 작성하지 않고도 state와 다른 React 기능을 쓸 수 있게 해 준다. useState로 상태를, useEffect로 부수 효과를 함수 컴포넌트 안에서 다룬다.

  • https://reactjs.org/docs/hooks-intro.html

Kotlin Sequences

Kotlin 문서는 컬렉션 연산이 단계마다 중간 컬렉션을 만들며 즉시(eagerly) 실행되는 반면, 시퀀스는 가능하면 지연(lazily) 실행되어 전체 처리 결과가 요청될 때 실제 계산이 일어난다고 설명한다. 또 컬렉션은 한 단계를 모든 원소에 적용한 뒤 다음 단계로 가지만, 시퀀스는 원소 하나씩 모든 단계를 통과시킨다. 원소가 많고 단계가 여러 개이거나 first()처럼 일부만 필요할 때 시퀀스가 유리하다. Java Stream과 비슷한 개념이다.

  • https://kotlinlang.org/docs/sequences.html

멱등성

MDN은 HTTP 메서드가 멱등하다는 것을 같은 요청을 한 번 보내든 여러 번 보내든 서버에 의도한 효과가 같은 것으로 정의한다. 안전한(읽기 전용) 메서드와 PUT, DELETE는 멱등하고, POST와 PATCH는 멱등성이 보장되지 않는다. 멱등하더라도 응답은 달라질 수 있어서, 처음 DELETE는 200을, 이후 호출은 404를 받을 수 있다. 네트워크 오류로 재시도할 때 멱등한 요청은 안전하게 다시 보낼 수 있으므로, 결제처럼 POST를 재시도해야 하는 API는 멱등성 키를 받아 중복 처리를 막는다.

  • https://developer.mozilla.org/ko/docs/Glossary/Idempotent#:~:text=%EB%8F%99%EC%9D%BC%ED%95%9C%20%EC%9A%94%EC%B2%AD%EC%9D%84%20%ED%95%9C%20%EB%B2%88,%EB%93%B1%EC%84%B1%EC%9D%84%20%EA%B0%80%EC%A1%8C%EB%8B%A4%EA%B3%A0%20%EB%A7%90%ED%95%A9%EB%8B%88%EB%8B%A4.

Map Stateless Operation

java.util.stream 패키지 문서는 중간 연산을 상태 없는(stateless) 연산과 상태 있는(stateful) 연산으로 나눈다. filter, map 같은 상태 없는 연산은 새 원소를 처리할 때 이전 원소의 상태를 기억하지 않아 원소마다 독립적으로 처리할 수 있고, distinct, sorted 같은 상태 있는 연산은 앞서 본 원소의 상태를 반영해야 한다. 상태 없는 연산만으로 이루어진 파이프라인은 병렬 처리에서도 한 번의 통과로 끝나지만, 상태 있는 연산은 전체 입력을 봐야 결과를 낼 수 있다.

  • https://velog.io/@doradora/Stream%EC%97%90%EC%84%9C-Map-Filter%EB%8A%94-stateless-operation%EC%99%80-reduce-sum-max%EB%8A%94-bounded%EC%9D%98-%EC%A0%95%ED%99%95%ED%95%9C-%EC%84%A4%EB%AA%85%EC%9D%80

MapReduce in Hadoop

Hadoop MapReduce 튜토리얼에 따르면 MapReduce 작업은 입력 데이터를 독립적인 조각으로 나눠 map 태스크가 완전히 병렬로 처리하고, 프레임워크가 map 출력을 정렬해 reduce 태스크의 입력으로 넘긴다. 앞의 Stream 이야기와 연결하면, map 단계가 상태 없는 연산이라 조각마다 독립적으로 분산 처리할 수 있고, 같은 키끼리 모으는 셔플과 reduce 단계가 집계를 맡는 구조다.

  • https://www.techtarget.com/searchcloudcomputing/definition/MapReduce
  • https://www.talend.com/resources/what-is-mapreduce/
  • https://hadoop.apache.org/docs/r1.2.1/mapred_tutorial.html
이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다