Posts

Showing posts from 2020

design pattern #2 Creational pattern - Abstract Factory

Factory Method 의 좀더 큰 버젼이다. 비슷한 유형의 product 를 하나로 묶어 이것을 interface 로 만들고,   2개 이상의 interface  를  다시 묶어 interface  로  만든다 Factory Method 의 아이디어와 방식이 비슷한데,  creator 가 product 의 abstract type 을 리턴 한다는 것이다. 아래 예시를 보면, button, checkbox 각각 Mac, Window 버젼이 있다.  그리고 이 두 UI 를 abstract 로 묶은 GUIFactory 도 각각 Mac, Window 버젼이 있다. 프로그램 시작시 state 를 구분해서 Mac, 혹은 Window 버젼의 GUIFactory 를 생성한다. 장단점은 factory method 와 같다. public interface Button { void paint(); } public class MacOSButton implements Button { @Override public void paint() { // implement } } public class WindowsButton implements Button { @Override public void paint() { // implement } } public interface Checkbox { void paint(); } public class MacOSCheckbox implements Checkbox { @Override public void paint() { // implement } } public class WindowsCheckbox implements Checkbox { @Override public void paint() { // implement ...

design pattern #1 Creational pattern - Factory Method

Factory 라고 해서, 뭔가 찍어내는 듯한 느낌 이었는데, 그것과는 좀 거리가 멀다. 이론적으로 읽어선 한번에 이해가 안가고, 예제를 통해서 보는게 역시 쉽다. 아래처럼 File  이 Product  라고 하고, Directory 를 Creator 라고 하고, Application 을 client  라고 하면.  요점은 product 의 공통 분모를 interface 로 설계 한다는것 Creator 의 공통 분모는 abstract class 로 설계 한다는것 그리고 Creator 에 여기서 말하는 Factory Method 인 createFile 이 abstract  로 들어가 있는 게 중점 같다. 그러면 creator 와 product  가 tight 하게 coupling 되지 않고 Single Responsibility Principle  을 지킬 수 있으며(factory method 는 createFile 하나뿐이다) 기존 프로그램을 건드리지 않고 new product type 을 추가 할 수 있다. 그리고 각각 공통된 기능은 interface 나 abstract  로 뺄 수 있다. 대신 물론, 더 복잡해진다. *Product   public interface File { void size(); void onClick(); } public class MusicFile implements File { public void size() { //implement } public void onClick() { //implement } } public class PhotoFile implements File { public void size() { //implement } public void onClick() { //implement } }...

21. Merge Two Sorted Lists

해결은 했는데, 공간 복잡도 측면에서 매우 비효율적이었다. 주어진 두 list  의 pointer 들만 잘 바꿔줘도 되는걸, 복잡해서 새로운 node 를 매번 만들었기 때문이다.  아래의 iterative 가 원래 내가 풀고싶던 방식이고, recursive 도 풀기 전 접근 단계에서 생각은 했으나 구현은 못한 방식이다. LinkedList  유형의 문제를 풀때는,  sentinel node 를 하나 만들어서 return 을 쉽게 하는 것 node.next.next  식의 접근 방식도 유용하게 쓰이니 기억할 것 null 체크를 항상 주의 할것 *Iterative public ListNode mergeTwoLists(ListNode l1, ListNode l2) { ListNode ptr1 = l1; ListNode ptr2 = l2; ListNode sentinel = new ListNode(); ListNode ptr3 = sentinel; while (ptr1 != null && ptr2 != null ) { if (ptr1. val <= ptr2. val ) { ptr3. next = ptr1; ptr1 = ptr1. next ; } else { ptr3. next = ptr2; ptr2 = ptr2. next ; } ptr3. next . next = new ListNode(); ptr3 = ptr3. next ; } ptr3. next = (ptr1 == null ) ? ptr2 : ptr1; return sentinel. next ; } *Recursive public ListNode mergeTwoLists (ListNode l1, ListNode l2) { if (l1...

Postgres SELECT WHERE 에서 EXIST / NOT EXIST 에 대해

EXIST 의 parameter 로  임의의  select 구문이나 subquery  를 받는다 판단은, param 의 결과가 최소 1개 이상의 row 를 return 하면 TRUE, 아니면 FALSE 이다 최소 하나라도 return 하는지가 중요하므로, 실제  return  결과는 관심이 없다 그래서 판별식 내부는 주로 select 1 이 쓰인다 far enough to determine 이라서,  statement 를 끝까지 완료하지 않을 수 있다(1개 발견되면 끝) NOT EXIST 는 반대로 판별식을 만족하는 row  가 하나도 없을때 TRUE 를 return 한다 *EXIST 한번이라도 100 보다 큰 amount 를 지불한 적 있는 고객 SELECT name FROM customer c WHERE EXISTS ( SELECT 1 FROM payment p WHERE p.customer_id = c.customer_id AND amount > 100 ) ORDER BY name; *NOT EXIST 한번도 100 보다 큰 amount 를 지불한 적 어는 고객 SELECT name FROM customer c WHERE NOT EXISTS ( SELECT 1 FROM payment p WHERE p.customer_id = c.customer_id AND amount > 100 ) ORDER BY name;

572. Subtree of Another Tree

 Easy  이고, 예전에 풀어봤었고, tree 문제는 잘 파악만 하면 몇줄 안에 해결되는 문제여서 좀 만만하게 생각하고 풀었는데. 대실패.  논리적으로 생각하기보다, 풀었던 기억을 되살리려 했다. Tree 문제의 경우, signature 로 주어진 함수를 자식 node 로 recursive 하게 호출하면 해결되는 문제들을 많이 봐서, 그런식으로 접근하려고 했는데 다양한 edge case  에 번번히 실패 그리고 결국 실행이 되긴 했는데 LTE 떠서 실패 recursive 하게 호출 하려면, 그 함수가 정확히 뭘 하는지 구분 해야한다. 이번 문제에선 recursive 하게 호출하되,  두 TreeNode 가 정확히 matching 되는지 체크하는 함수는 또 따로 recursive 로 돌리는것이 관건이었다. 말하자면 2개의 recursiv 함수가 있는 것이다.  null 체크를 제외한 주요 호출은 아래처럼 된다.  public boolean isSubtree (TreeNode s, TreeNode t) { return exactSame (s, t) || isSubtree (s. left , t) || isSubtree (s. right , t); } private boolean exactSame (TreeNode s, TreeNode t){ if (s. val ==t. val ){ return exactSame (s. left , t. left ) && exactSame (s. right , t. right ); } return false ; } Complexity tree s  의 node 개수를 S, tree t 의 node 개수를 T  라 한다면, 각각의 s 의 노드를 root 로 하는 subtree가 tree t 와 exactly matching 하는지 확인 하므로,  최악의 경우 O(ST) 가 되는것 같 다....

refactoring #6 Dealing with Generalization

subclass 들에 중복되고 거의 유사한 field, method 는 부모 class 로  끌어올린다 부모 class에 있으나 하나의 subclass 에서만 사용된다면, 그쪽으로 내린다 한 class 에 있으나 일부만 따로 떼어질거 같으면 subclass 를 만들고 그쪽으로 다 옮긴다 서로 다른 class 에 비슷한 method 나 비슷한 부분이 있으면 superclass 를 만들고 상속받는다 subclass 가 시간이 지나며 superclass와 거의 똑같아졌다면, 합한다. 대신 다른 subclass 는 없는지, 합쳤을때 문제는 없는지 고려해야한다 시간이 지나면서, subclass 들 사이에 비슷한, 정렬 같은 알고리즘이 거의 비슷하게 존재하게 될 수가 있는데. template 형태로 superclass 로 옮길수 있다면 옮긴다. 경우에 따라 delegation 을 inheritance 로, inheritance 를 delegation 으로 바꿀 수 있다.

refactoring #5 Simplifying Method Calls

아래 내용들은 바꿀 경우, super/sub class 에도 해당 되는지도 체크 해야한다 method  이름은 간결하되, 내용을 제대로 표현하게.  method  에  parameter 추가는 좋으나, parameter 는 늘 추가하는건 쉽고, 삭제하는건 어려우며, 대개 param 사이즈가 엄청 늘어나게 되며 이경우 다른 refactoring 이 필요하다 사용되지 않는 parameter 는 삭제한다   조회하는  query 와 수정을 가하는 modifier 는 따로 다른 method   로 분리되어야 한다.  비슷한 method 인데 parameter 만 다른 경우, 하나로 합치고 parameter 로 분기할수 있는데, 자칫하다간 매우 길고 복잡한 하나의 method 로 합쳐질 수 있어 이 경우는 지양해야한다 위와 반대로, 하나의 method  가 길고 복잡하고 불필요하게 parameter 가 많은 경우, 두개의 method  로 나누고, parameter 도 필요에 따라 쪼갠다. parameter 여러개가 한 object 에서 나올 경우, object 를 그냥 넘겨도 된다. 하지만 int, String 같은 거였다가 object 로 바꿀 경우그럴 경우, 그 object 만이 method 에 쓰일 수 있다.  method 안에서 해결 될 수 있는 계산이면, method 안에서 처리 한다. 밖에서 해서 parameter 로 넘길 필요 없이 parameter  들이 많고, 자주 method 에서 쓰이면 parameter object 로 하나의 class 로 묶고, 관련된 method 들도 거기로 옮겨버리면 깔끔해질 수 있다 생성자에서 변수 할당 이외에 복잡한 작업을 할 경우, factory method  로 대체한다. -1  같은 에러코드를 리턴하는 대신 exception 을 던진다.  0, -1 같은 코드가 아니므...

refactoring #4 simplifying conditional express

if 구문 안이 장황해지면 읽기 힘들다.  가능하면 extract 한다 nested if 를 and 로 묶거나,  동일한 결과를 return 하는 if를 or 로 묶어서 extract한다 if 와 else 모두에 중복된 코드가 있으면 if-else 밖으로 뺀다.  nested hell은 피한다. exception, 바로 return 하는 것들을 최대한 빼고 평평하게 만든다 switch-case 가 길어지고 복잡해지면, 각각의 case 를 abstract- subclass 로 뺄 수 있는지 본다. 이 경우 새로 추가되는 case 가 있다 해도 기존  class 수정 없이 subclass만 신규 추가된다는 부가적인 장점도 있다. 메소드 실행시 Assert.isTrue 로 분명히 할건 assert  걸어둔다. 꼭 JUnit  에서만 쓰이는게 아니다. assertion 을 확실히 해두면 live documentation 이 된다. 물론  overdo 해선 안된다. introduce Null object 는 처음 본다.  예를들어 if(customer==null) 같은 체크가 많으니, NullCustomer 라는  class 를 Customer class  에서 상속받아 만들어서 null check 를 더 간결하게 하는 것이다. 더 복잡하게 하는거 같은데. 어쨌든 이런 방법도 있다니.

819. Most Common Word

 String 을 sanitize 하는 부분에서 애를 먹었다. Map traverse 방법은 물론이고 그게 최적인지 잘 모르겠다.  *Trial and Error sanitize 를 하면서 특수문자를 ""로 바꿨더니 "a,b"  가 "ab" 가 되어버렸다.   trim 하는것을 까먹어,  "cat" 과 "cat " 이 각각 다른 단어가 되어버렸다. "" 가 하나의 단어가 되었다. isEmpty 로 걸렀어야했다. *Complexity Paragraph 의 length 를 l 이라고 하고,  banned 의 size 를 s  라고 하면. set 에 ban 넣는 O(s) 특수문자 sanitize 하는데 O(l) split 하는데 O(l) split  한 단어의 개수를 n 이라 하면, HashMap 에 넣으니까, average O(1), worst O(n) HashMap traverse 한번 하니까 O(n) 다 더하면  O(s)+2O(l)+O(1)/O(n)+O(n) 이다. average : O(s)+2O(l)+O(1)+O(n) = O(s)+O(l)+O(n) worst : O(s)+2O(l)+O(n)+O(n) = O(s)+O(l)+O(n) 결국 O(s+l+n) 의 linear 한 complexity 를 갖는것 같다.  처음 문제를 보고 접근법을 생각할 때, 이를 Trie 를 통해 접근하면 Paragraph 를 한번만 읽으면 되지 않을까 생각했는데, Trie 구현을 안찾아보고 하기 어려워서 그냥 Map 으로 했다. 가장 성능이 좋은 알고리즘을 보니 역시 Trie 로 접근했다. Trie 를 익숙하게 다룰 줄 알아야겠다. 

Map 다루기

 put, getOrDefault *put, getOrDefault Map<String, Integer> map = new HashMap<>(); for (String word: words){ String sanitized = word.trim(); if (!bannedSet.contains(sanitized) && !sanitized.isEmpty()){ map.put(sanitized, map.getOrDefault(sanitized, 0 )+ 1 ); } } *entry traverse for (Map.Entry<String,Integer> entry : map.entrySet()){ if (entry.getValue()>count){ Integer count= entry.getValue(); String result = entry.getKey(); } }

String 다루기

  *String sort in Arrays with comparator Arrays. sort (res, 0 , pLet, (str1, str2) -> { int index1 = str1.indexOf( " " ); int index2 = str2.indexOf( " " ); return str1.substring(index1).compareTo(str2.substring(index2)); }); *String split, indexOf String str = "cat banana dog" ; String[] splitted = str.split( " " , 2 ); int idx = str.indexOf( " " ); String substr = str.substring(idx); System. out .println(splitted[ 0 ]); //cat System. out .println(splitted[ 1 ]); //banana dog System. out .println(idx); //3 System. out .println(substr); // banana dog

TreeMap 과 HashMap

Average 로 보면 HashMap 은  put, get 모두  O(1) 이다.  하지만 collision 이 매번 발생하는 Worst 의 경우에는,  put, get 모두 O(n) 이다.   TreeMap  는 key,val  에서 key 를 중심으로 key,val entry  가 정렬이 되며,   중복된 key 로 put 을 할 경우 당연히 덮혀 씌워진다.  내부적으로 Red-Black tree (balanced tree) 구조로 저장이 되며,  Average/Worst 모두 put,get 에 대해 O(logn) 이다.

코드리뷰에 대하여

우리 개발팀에는 DoR, DoD,DoP 라는 것이 있다. 이전에는 겪어본적이 없는것들이다. 나중에 따로 정리를 하겠지만, 우선은 코드리뷰에 대해 정리를 해보려고 한다.  이전 회사에서도 코드리뷰가 있었다. 처음부터 있었던건 아니고, 나중에 생겼다. 하지만 반드시 reviewer 가 accept 해야 commit 이 되는것도 일부 모듈에 대해서만 적용되었기 때문에 모든 code commit  에 대해 approval  이 필요하지 않았다. 때로는 빨리 검증 받고 배포해야 하는데 review 안되서 이게 걸림돌이라는 느낌이 강했고, 어떤 경우엔 담당자가 나 혼자여서 review 를 해줄 사람이 없었다. 모듈별로 주담당-백업이 나뉘어 있었는데, 백업 모듈에 대해 review 를 하기엔 잘 알지 못하고, 주담당인 모듈에 대해선 누군가 review  를 해줘야하는데 오래 걸리니 걸림돌이 되고. 백업이 없는 경우는 review 가 없는 상황. code reivew  에 대한 인식이 없다가 생긴것은 좋았으나, 그것이 개발에서 꼭 필요한 일부분이라기 보단 형식적인 통과의례로서 병목처럼 여겨졌다. 여기서는 아니다. review 를 받지 못하면 merge 되지 않는다. 우리 팀은 이제 7명. 옆 팀은 더 많다. 한 9명 되나? frontenf, backend 가 한 팀에 있고,  내 모듈, 네 모듈의 개념이 아닌, 우리 팀의 모듈이다. 그래서 특별히 어려운 티켓을 제외하면 누구나 develop, review, test 를 할 수 있게 되어있다.  reviewer  를 찾는 문화도 있다. daily  에서, 혹은 팀 채널에서 티켓 개발이 끝났으니 reviewer  를 찾고 있다, 라든지, 지금 할거 없는데  도움 필요한 사람? 이런식으로 나서기도 한다.  review 도 대충하지 않는다. 철저하다. 여러번 퇴짜를 맞았다. 나도 마찬가지로 적극적으로 의견을 내려고 한다. 아무리...

refactoring #3 organizing Data

 1. getter, setter의 사용. 당연하다고 생각했지만, subclass 에서 getter, setter 를 redefine 할 수 있다는 장점. setter  를 아예 없앰으로써, construct 할때 설정된 값이 항상 사용되는 final  효과를 줄 수있다는 점은 처음 알았다. 2. business logic  dto  와 GUI 에 사용되는  dto 는 반드시 분리해야한다. 그렇지않으면 골치아파진다. 그럼 buisness 를 여러명이 같이 개발 할 수도, 여러 GUI 에 대한 dto 를 각각 개발할 수도 없어진다. gui  관련된 dto 는 만들어본적이 없었는데. 얼마전 경험을 하다가 겪었다. 반드시 분리해야한다. 혼자 하는데도 불구하고 여려번 수정해야 하는 노가다가 있었다. 여러명이 작업한다면 불가능했을것이다.  3. encapsulation 이 java  의 기본이긴 하지만. Point  라는 java 클래스의  x, y 변수는 public 이라고 한다. 성능상의 큰 영향이 있는 이런 특수한 경우에는 getter/setter 대신 public 접근을 할 수도 있다. 하긴 지금 회사도 초반에 그런 것들이 꽤 있었다. 악영향을 끼치지 않고, readable  하고, 성능적인 이득을 얻는 경우. 4. collection 의 경우, encapsulation 의 형태가 좀더 달라야한다. getter/setter 라고는 하지만,  collection 자체를 getter  로 내어주는 경우, readonly  가 되어야 하며, setter 대신 각각의 item 에 대해 add, remove item 하는 함수를 만들고. setter 자체는 collection replace 가 되기 때문에 이에 유의해야 할 필요가 있다.  change value to ref change ref to value change bidir...

refactoring #2 moving features between Objects

이번 장은 주로 여기저기 퍼져있는 method들을 다시 재배치하고, public 에서 가릴건 가리고, 하는 방법이다. 1. method 나 class  를 따로 빼거나 이동하게 되면, 작업을 마친 뒤 original method, class  를 다시한번 살펴본다. 그러면 single-responsibility 를 더 잘 표현하는 increased clarity 로 renaming 할 수 있을지도 모른다. (여러 기능을 하던 것들이 빠져나갔기 때문에) 2. 이런 경험이 많지는 않아도 한 두번 있었던것 같은데.  library  class 에 원하는 method 하나가 없는 경우가 있다. 그런 경우, 아래와 같은 방법이 가능하다.  introduce foreign method method  를 하나 만들어서,  거기에 library object  를 parameter 로 받아서 필요한 기능을 구현한다.  introduce local extension    library class 를 extend 하거나, wrapper class 를 만든다. final  keyword  등으로 extend 를 할 수 없는 경우 wrapper class 를 만들어서 감싸면 되는데, 이 경우 기존 library class 의 모든 public class 를 delegate 해서 호출해줘야 한다.: 전반적으로 이번챕터는 지난  extract 챕터와 거의 유사해보인다. 아직 이런 경우에 대한 경험이 없어서 그런것 같다. 어쨌든 요점은, 1 class  1 responsibility 를 유지하는 것이 중요하며, class 가 점점 커져가면서 여러가지 field, method  들이 추가되게 되면, 어느 순간 extract class 하는것이 필요하고, 이때 관련 field, method 들을 새로 만든 class 로 이동시키면된다. 그리고 ...

Efficient PostgreSQL Index 생성을 위한 몇가지 참고점

B-Tree MySQL 과 마찬가지로, CREATE INDEX 를 하게 되면 B-Tree 를 사용한다.  B-Tree 는 별도로 공부해야하지만,  it tries to remain balanced. 가 핵심이다. 각 tree의 branch 들에 속한 값의 양들이 거의 동일하게된다.  INDEX  Case by case  select * from foo where bar = 1 select * from foo where bar = 2 bar=2 인 경우가 굉장히 많으면, 오히려 안타게 된다. sequential scan 이 index scan 보다 훨씬 빠르기 때문이다. Partial Index Index 는 Index 인데, where 조건이 포함된 Index 이다 Index size 를 줄임으로써 Index에 대한 효율성을 높일 수 있다 아래와 같은 경우, comments 중 flag 가 true 인 것들에 대해서만 created_at에 Index 를 걸 수 있다.  CREATE INDEX idx_comments_flagged_created_at ON comments ( created_at ) WHERE flag IS TRUE ; Expression Index data 를 function 에 넣은 결과값에 대해 Index를 걸 수 있다.   예를들어, 사용자의 email 계정을 저장하는  column  이 있다고 할때,  사용자가 입력한대로 값은 저장하되 로그인등 인증시에는 아래처럼 소문자로 처리하고 싶으면,   WHERE lower(account) = '{lowercased_email}'  Index 는 아래와 같이 걸수 있다.  CREATE INDEX idx_users_account_email ON users ( lower ( account ) ) ;   혹은 Timestamp Column 에 대해 날짜에 대...

OptaPlanner 공부하기 - 1

Optaplanner 란 간단히 말해서, 최적화 문제를 인공지능적인 방법으로 접근해서 해결해주는 open source java library 이다. maven 을 통해 import  를 할 수있으며, 소스 또한 github 에 공개 되어 있다. pure 100%  java 로 짜여있고, SpringBoot 와도 compatible 하다.  문제를 해결하기 위한 수학적 방정식같은걸 짜지 않아도 된다고 한다.  Optaplanner 라는 이름에서도 유추할 수 있겠지만, 이 도구가 해결해주는 문제는 간단히는 knapsack problem 부터 시작 해서,  vehicle routing, employee rostering, maintanence scheduling, task assignment, school timetabling 같은 것들이 있다. 제한된 자원을 문제 해결을 위해 할당해서 처리해야 하는데 optimal solution  을  내주는 것이다.  인공지능 하면, 방대한 데이터를 학습시켜서 판단하는 gene 같은걸 형성하는 거라고 생각했는데, maven library 를 import 하는 것만으로도 된다니 신기하다.  knapsack 문제 해결하는 예제를 따라가면서 해봤는데, 일단은 신기하다. 일단 개발자가 해야하는 일은 문제와 제약조건들에 annotation  을 달면 된다.  하나하나 앞으로 공부해가야 할 것들이다. @PlanningEntity, @PlanningVariable, @PlanningEntityCollectionProperty, @ProblemFactProperty, @PlanningScore solution 에 제약 조건들은 예를 들면 아래처럼 나타낼 수 있다. knapsack 같은 경우, maxWeight 를 넘으면 안되면서(penalize),   private Constraint maxWeight(ConstraintF...

MySQL slow log 원인 찾기 / "commit; set timestamp" analysis using performance_schema table

Image
MySQL slow query 를 보다보면 이해가 가지 않는 경우가 있다. # Time: 2020-07-02T15:44:14.145642Z # User@Host: mydatabase[mydatabase] @ [10.1.1.5] Id: 32026 # Query_time: 0.200300 Lock_time: 0.000000 Rows_sent: 0 Rows_examined: 0 use mydatabase; SET timestamp=1575301453; commit; 위 로그는 실제로 겪은 경우인데, Lock_time 도 0이고, rows_sent, rows_examined 도 0인데 0.2초가 넘는 시간이 걸렸다. 한번이면 그냥 무시하고 넘어가려고 했는데, 이 로그가 수십번 넘게 계속 쌓여가고 있어서 찾아봤다. MySQL 에서 제공하는 테이블중 innodb_trx 라는 테이블이있다.   select * from information_schema.innodb_trx; 실행 해보면 위와 비슷한 결과가 나오는데, application server 로부터 DB 에 connection 을 맺고 현재 진행중인 transaction 을 볼 수 있다. 왠만한 쿼리가 아닌 이상 매우 짧은 시간안에 끝나기 때문에 수행 할 때마다 결과 값이 바뀐다. 하지만 더 재미있는 테이블은 performance_shcema DB에 있다. MySQL 8.0부터 제공했던것 같다. https://dev.mysql.com/doc/refman/8.0/en/performance-schema.html 여러가지 테이블이있지만 그중 가장 관심이 있는 테이블은 events_statements_history 와 threads 테이블이다. 이제 위 테이블의 trx_mysql_thread_id 컬럼의 값을 이용해서 performance_schema 를 조회해볼 수 있다. 그럼 저 trx_mysql_thread_id 에서 어떤 일들이 일어났는지를 알 수 있다. select SQL_TEXT FROM perform...

IDENTITY_INSERT가 OFF로 설정되면 테이블 'products'의 ID 열에 명시적 값을 삽입할 수 없습니다.

Springframework JPA 를 사용해왔는데,  PostgreSQL, MySQL 에선 본적이 없던 에러가 나왔다. 테이블 ID 컬럼 속성에 auto increment 옵션을 달아놨는데 JPA에서 create 를 할때 id   값을 지정하려고 하니 이런 에러가 발생했다.  영어로는 Cannot insert explicit value for identity column in table 'products' when IDENTITY_INSERT is set to OFF 이런 식이다. 그냥 이 에러로 검색하면 해결법이 테이블 속성을 바꾸거나, 아래와 같이 쿼리를 수행 할 때 이전과 이 후에 처리를 해주는 것으로 나왔다. SET IDENTITY_INSERT (table) ON INSERT  SET IDENTITY_INSERT (table) OFF 하지만 JPA 를 사용할때는 이런 쿼리를 수행하기가 까다로워서 더 찾아봤다. 해결 방법은 간단했는데, @GeneratedValue (strategy=GenerationType. IDENTITY ) 를 Entity 클래스의 column  선언에 추가해 주면 된다.  public class ProductEntity { @Id @GeneratedValue (strategy=GenerationType. IDENTITY ) @Column (name = "id" , nullable = false ) public int id ; } 이런 식으로 수정을 해주고 나니 에러 없이 데이터 추가가 잘 되었다.  

Only Black Lives Matter?

충격적이고 슬픈 사건이 미국에서 발생한지 한달. 이 사건이 계기가 되어 미국은 물론 전 세계적으로 black lives matter 라는 운동이 일어났다.  폭력 사태가 발생하기도 했으며, 한인 타운의 가게를 포함 백화점들이 약탈 당했다는 소식도 들었다.  분명 일어나선 안되는 일이고 슬픈 일이지만. 이 운동에는 반문이 생긴다. 프리미어리그가 다시 시작하면서,  모든 선수들이 유니폼의 이름 자리에 black lives matter   라는 문구가 적힌 유니폼을 입고 뛰었다. 취지는 좋다. 그런데 좀 이상했다. 손흥민도, 미나미도도 그 유니폼을 입고 뛰었다. 아시안이 black lives matter  를 외치는 것 같았다.  맨유와 토트넘의 언젠가의 경기에서. 손흥민이 넘어졌을때, 포그바는 박수치는 듯한 제스쳐를 취했다. 비꼬는 듯한 표현으로읽혔다. 손흥민이 아시안이 아니었으면, 포그바는 그런 제스쳐를 취했을까? 박지성 입단 때부터 맨유를 응원하게 되었지만, 박지성 응원가에 그들은 인종차별적인 요소를 담은 응원가를 붙였다.  중남미 어느 국가와 우리나라가 국가 대표팀 경기를 했을때도. 눈을 양 옆으로 찢는듯한 제스쳐는 물론이고, 심지어는 부상선수의 치료를 위해 들고 들어간 구급상자를 밖으로 내팽개치는 선수도 있었다. 유럽의 국가와 대항전이었다면, 아무리 지고 있는 상황이라도 그런 행동을 했을까.  백인은 그들이 흑인보다 위라고 생각한다. 흑인은 백인과 동등한 대우를 해달라고 요구한다. 그런데 그 둘에겐 공통점이 있는 느낌이다.  아시안은 둘다 i don't care 라고 생각하는 듯 보인다.  인간이라고 생각을 안하는건가.  그들이 길거리에 나와 시위를 하는 이유는 정말로 모든 인종차별에 대한 증오라기 보단, 그렇게 하면 깨어 있는 행동을 하는것 같기 때문이라고 생각한다. 결국은 힘의 논리이고, 본인에게 유리한 대로 행동하는 기저가 깔려 있겠지만. 유럽에서...

refactoring #1 composing method

composing method  refactoring 의 제일 기본. 코드가 길면 이해하기 어려워지고, 이해하기 어려우면 변경하기도 어렵다. 변경하기 어려워지면 방치될 수 있고, 잠재적인 결함의 원인이 되거나, 비즈니스 로직의 변경에 대처하는 비용이 증가하게 된다.  독일에 와서 일하기 시작하면서,  code review 엄격하게 하는 환경이 되었는데. 가장 먼저 내가 받았던 review 들은 너무 길어서 이해하기 힘들다는 것이다. 별로 안길다고 생각했는데, 다시 생각해보니 "모르는 사람, 혹은 처음보는 사람 입장에서 코드를 봤을 때 이해하기 쉬운지" 의 관점에서 생각해본적은 한번도 없는 것 같다. 여러차례 review 에서 지적을 받은 끝에, 지금은  commit 할 때마다 길이를 다시한번 확인하는 습관이 생겼다. 제일 기본적인 내용이지만 생각해봐야 할 점들이 있다. 1. refactoring 혹은 review 를 하다보면 주석으로 적혀있는 것이 그대로 함수명이나 변수명이 되는 경우, 혹은 assert 의 문구가 되는 경우가 있다. 그런 경우 함수명이나 변수명이 그 뜻을 제대로 내포하지 못했다는 뜻이거나, 주석이 더 잘 표시했다는 경우이다.  우리회사에서의 주석의 정체는, what 보다는 why 에 대해 남기자는 정책인데,  why 에 대한 입장에서 naming 하는것이 더 깔끔한 경우가 있는것 같다. Exception 을 던지는 경우에도, Exception 옆에 주석을 다는 것보다, Assert(condition, "comment"); 로 표시하는 것이 더 깔끔해보였다. 2. 쪼개는 것의 단점 if(a() || b()) 의 경우,  a() 가 true 이면 b 는 실행하지 않는다. 어차피 true 이기때문이다. 하지만 이걸 extract 해서 위로 빼면  aa= a() bb = b() if (aa || bb) 같은 식이 되므로, 이때는 항상 a(), b() 를 모두 실행한다. 따라서 re...

독일 추천 수분크림 올리브놀 (Olivenöl) 제품 비교 / 후기 - 약국에 팔아요!

Image
독일에 올때 가져왔던 Dr.Jart  크림이 다 떨어져서 새로 사려고 수분 크림을 찾아봤다. 아마존을 통해 쓰던 크림을 다시 살수도 있었지만, 독일에 왔으니 독일 추천 수분 크림으로 바꿔봐야겠다고 생각했다.  많은 독일 추천 혹은 독일 선물 필수 아이템 등으로 수분 크림이 여러개가 나왔지만, 뭔가 자연 친화적일 것 같은 디자인에 향이 강하지 않고 은은한 향 일것 같아서 올리브놀 수분크림을 선택했다.  내가 찾아봤을때는 DM  에서 판다고 해서 시내  DM 들을 여기저기 가봤는데, 모두 없다고 했다. 스크린 샷을 보여주며 직원에게 물어봐도 없다고만 했다. 그래서 안파나 싶어 약국을 가봤는데, 약국에 진열되어 있는게 아닌가. 올리브놀 수분 크림은 약국에 있다.  다른 도시에는 DM 에 있을수도, 하지만 DM 에 없다면  약국에 한번 가보라고 말하고 싶다. 나는 수분크림을 찾고 있었는데, 올리브놀 제품에도 여러가지 타입이 있는것 같았다.  이건 50ml 작은 타입이고, 큰 타입도 따로 판다. 아마 100ml 였던것 같다. 일단 50ml 두개를 한번씩 써본 뒤 맞는 제품으로 큰 용량을 다음번에 사려고 한다. Feuchtigkeitspflege [Moisturizing] 수분 크림 Gesichtspflege [Facial care] 페이셜 케어  둘다 향은 강하지 않고, 은은한 기분 좋은 향이다. 향은 비슷한 것같다. 둘다 요거트 같기도하고. 두 타입의 차이가 조금 있는데, 수분 크림은 좀 촉촉한 느낌이고, 페이셜 케어는 좀더 고체감이 있는 느낌이다. 엄청 큰 차이가 있다기보단, 두개를 비교했을때 상대적인 차이라고 보면된다.  번들거림을 피하고 싶다면 페이셜 케어가 좋을것 같고 액체감을 선호한다면 수분 크림이 좋을것 같다. 아, 가격은 똑같았던것 같고,  각각 10-11 유로 사이였던것 같다.