zepinos
18k
2019-06-19 09:36:34
65
5199

코드 수준 한 번 봐주시겠습니까?


어떤 내용의 코드인지는 굳이 밝히지 않겠습니다.


import java.io.*;
import java.util.*;
 
class Main {
 
    public static void main(String[] args) throws Exception {
 
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String input = br.readLine();
 
        String[] inputArr = input.split(",");
        Map<String, Integer> map = new HashMap<>();
        for (int iCount = 0; iCount < inputArr.length; iCount++) {
 
            String text = inputArr[iCount].trim();
            int count = map.getOrDefault(text, 0);
            map.put(text, count + 1);
 
        }
 
        input = br.readLine();
 
        List<Map.Entry<String, Integer>> list = new LinkedList<>(map.entrySet());
 
        Collections.sort(list, (o1, o2) -> {
            int comparision = (o1.getValue() - o2.getValue()) * -1;
            return comparision == 0 ? o1.getKey().compareTo(o2.getKey()) : comparision;
        });
 
        int iCount = 0;
        for (Map.Entry<String, Integer> entry : list) {
 
            String key = entry.getKey();
            if (key.startsWith(input)) {
 
                if (iCount++ > 0) System.out.print(", ");
 
                System.out.print(key);
 
            }
 
        }
 
    }
 
}


그리고, 다음은 SQL 입니다.


SELECT U.USER_ID
      ,U.USER_NM
      ,UPI.IMG_URL
      ,IFNULL(SUM(CASE PO.ORDER_STATUS WHEN 'READY' THEN 0 WHEN 'SHIPPING' THEN 0 WHEN 'COMPLETE' THEN 1 END), 0) AS COMPLETE_CNT
      ,IFNULL(SUM(CASE PO.ORDER_STATUS WHEN 'READY' THEN 0 WHEN 'SHIPPING' THEN 1 WHEN 'COMPLETE' THEN 0 END), 0) AS SHIPPING_CNT
      ,IFNULL(SUM(CASE PO.ORDER_STATUS WHEN 'READY' THEN 1 WHEN 'SHIPPING' THEN 0 WHEN 'COMPLETE' THEN 0 END), 0) AS READY_CNT
FROM PRODUCT_ORDER PO
    ,USER U
    ,USER_PROFILE_IMG UPI
WHERE PO.USER_ID = U.USER_ID
AND U.USER_ID = UPI.USER_ID
AND U.USER_ID = 'imstillhungry@test.com'
AND PO.REGISTERED_AT <= '2018-12-31 23:59:59'
AND PO.REGISTERED_AT >= STR_TO_DATE(substring('2018-12-31 23:59:59', 0, 10), '%Y-%m-%D') + INTERVAL -90 DAY


...


제가 궁금한 건, 20년 가까이 개발밥 먹은 사람이 작성한 코드라고 하기에는..."개발연차 대비 코딩완성도가 떨어진다" 는 소리를 들을 만큼 수준이 떨어지는가...하는 점입니다. 코드에서 어떤 점을 더 개선시켜야 할지 조언해주시면 매우 고맙겠습니다. 물론, 두 코드 모두 동작 자체는 잘 되는 코드라고 생각은 합니다. 성능은 더 개선시킬 수 있겠지만요.

2
4
  • 댓글 65

  • 8k
    2019-06-19 09:43:49

    제가 경력이 차면서 알게된건....

    나의 짠 소스는 평가하지 않는다...(신입이 짠건 제외)


    1
  • Frudy
    3k
    2019-06-19 09:45:26

    저는 항상, 남의 코드를 볼 때,

    20년 경력이라고 하셨죠? 그분의 코드 한줄한줄은 20년짜리 한줄이다 라고 생각해요.


    평가...를 부탁드렸으나, 배운 내용을 적어보겠습니다.


    1.

    콘솔입력은 new Scanner(System.in);으로 작성해왔었는데,

    첫줄부터 뭔가 저랑 다르시네요. 배울점이 보입니다.


    2.

    arr이라는 네이밍이 실제로 현업에서도 쓰이는군요..

    arr --> array로 사람들이 이해해주는가? 에 대해 고민이있었는대 해결됬어요.


    3.

    나머지는 자료구조 관련 지식이 부족해서,

    왜 굳이 저렇게 하셨는지 잘 모르겠습니다. 배울점이 보이네요.


    20년짜리 코드 잘보고갑니다 감사합니다.

    2
  • jslovers
    1k
    2019-06-19 09:49:37

    sql은 괜찮으신거 같은데,

    자바 코드는 완성도가 높지 않은 느낌을 받습니다.

    0
  • mirheeoj
    7k
    2019-06-19 09:50:15

    어떤 사람이 그런 말씀을 하셨는지는 모르겠지만, 코드에 문제가 있어보이면 알려주면 될 일이지 "니 죄를 니가 알렸다" 식으로 말하는 건 좀 그렇네요. 

    0
  • 재현아빠
    1k
    2019-06-19 09:55:14

    @mirheeoj 저랑 같은 의견이시군요. 저도 다른 사람의 코딩방식을 뜯어보지 않습니다. 원하는 결과가 나오고 성능이 괜찮다면말이죠. 개선할 부분이 있다면 그분이랑 직접 이야기나눠보시고 의견을 들어보면 될 일을 굳이 여기에 올려서..공개재판이라도 하자는 의미이신가요?

    -1
  • LichKing
    14k
    2019-06-19 09:57:45

    왠지 글 분위기가 제피노스님이 누굴까려고 올린게아니라 제피노스님께서 저런말씀을 듣고 올리신게아닐까 하는데요..

    지금이 코드리뷰하는시간이면 몇줄 코멘트를 달긴했을텐데 단순히 저코드보고 '연차대비 떨어진다' 라는 말을 할정도인지는 모르겠네요. 그냥 절대적인 코드량자체가 적어서 함부로 저런말을 할정도인지는 모르겠어요.

    0
  • mirheeoj
    7k
    2019-06-19 09:58:09

    재현아빠 // 제가 보기에는 글쓴분이 저 코드 작성자이고 다른 사람에게서 그런 얘기를 들었는데 이해가 안 되어 조언을 구하시는 것 같습니다. 말씀하신 것과 반대 상황 같아요. 

    0
  • 재현아빠
    1k
    2019-06-19 10:00:36 작성 2019-06-19 10:03:00 수정됨

    아..그런가요? 제가 오해를 했나보군요. 잘 모르고 댓글을 달아서 진심으로 사과드립니다. 죄송합니다.


    도대체 20년차 소스코드는 어떤 차이를 기대한건지 참 어렵군요. 그런 말을 한 의도를 잘 모르겠네요.

    댓글중 jslovers님은 어떤 의미로 완성도가 떨어져 보인다고 평가를 하신건가요? 궁금하네요.

    -1
  • 칠역한천겁
    2k
    2019-06-19 10:01:36 작성 2019-06-19 10:03:20 수정됨

    음...


    코드를 어떤 이유에서 라도 평가를 해야 한다면...

    그 코드를 짜는데 걸린 시간도 평가 요소에 들어가야 한다고 봅니다.


    요새는 코드리뷰도 많이 하는거 같아서... 머 자신이나 남의 코드를 보고 평을 하는거에 대해서는 나쁘게 생각하지는 않습니다.


    근데.. 언뜻 쭈욱 봤지만..사용자 입력받는데 BufferedReader 를 사용했는데..<= 스캐너 안쓰고??

    그에 반해 람다를 쓰셨네요. -_- <= 람다 안쓰는 혹은 모르는 구식 개발자들 많은데..


    먼가... 구와 신을 어우르는...언발란스?? <= 이 부분에서 연차의 높음이 약간 보이네요. 


    근데 머 별 이상한거 못느끼겠는데요? IO 자원해제야.. 머 생략하신거 같긴 하지만 그거야머..

    어차피 예제수준의 코드인거 같은데..


    SQL 코드는 오랜만에 보는데... 정렬한거 넘 이쁘네요. ㅎㅎ

    0
  • 돈까스
    2k
    2019-06-19 10:05:42

    그냥 제 생각입니다.

    코드가 작성된 배경이나 히스토리는 모르고서 하는 이야기니까 감안해주세요.


    요 부분을 보면 약간 의문이 되는 부분들이 있는데요.

          ,IFNULL(SUM(CASE PO.ORDER_STATUS WHEN 'READY' THEN 0 WHEN 'SHIPPING' THEN 0 WHEN 'COMPLETE' THEN 1 END), 0) AS COMPLETE_CNT
          ,IFNULL(SUM(CASE PO.ORDER_STATUS WHEN 'READY' THEN 0 WHEN 'SHIPPING' THEN 1 WHEN 'COMPLETE' THEN 0 END), 0) AS SHIPPING_CNT
          ,IFNULL(SUM(CASE PO.ORDER_STATUS WHEN 'READY' THEN 1 WHEN 'SHIPPING' THEN 0 WHEN 'COMPLETE' THEN 0 END), 0) AS READY_CNT

    - 구조를 맞추기 위해서 불필요한 WHEN을 계속 넣을 필요가 있는가 싶습니다.

      코드 값이 3개면 3줄에 3개의 WHEN이 필요한데, 코드값이 더 많아지면?

    - CASE 구문을 잘 조정하면 IFNULL도 필요없을 것 같습니다.

    - SUM이 있는데 GROUP_BY 구문은 보이지 않네요.

      코드가 생략된 것인지 의도한 것인지 의문이 드네요.


    1
  • feel
    177
    2019-06-19 10:09:43

    구체적으로 어떤 부분이 20년차 개발자의 코드가 아닌건지 알려달라고 하지 그러셨어요.


    제 개인적인 의견으로는

    1. 요구사항에 맞게,

    2. 기능 구현이 됐고

    3. 구현시 유지보수성을 고려했으며

    4. 일정에 문제가 없다면

    1년을 했던 20년을 했던 코드 내용가지고 뭐라고 할 일은 없다고 봅니다.

    1
  • Keloper245
    861
    2019-06-19 10:10:48

    20년차면 모든걸 작성한다기보다 가이드코딩만하고 세세한건 다른분께 부탁하시겠지요.


    깔끔한데요.

    읽기쉽고, 누구라도 바로 고칠수 있을 것 같네요.


    읽기쉽다 -> 좋은코드.


    년차가 차면 찰수록 뽕에차서 어렵게 쓰고 막 외계어같이 쓰는것보다


    깔끔하고 보기좋고 군더더기 없는게 저는 더 좋습니다.

    0
  • ccc
    1k
    2019-06-19 10:14:18

    input = br.readLine(); 을 두번 하는 이유는?


    Map<String, Integer> map = new HashMap<>();

    for (int iCount = 0; iCount < inputArr.length; iCount++) {

                String text = inputArr[iCount].trim();
                int count = map.getOrDefault(text, 0);
                map.put(text, count + 1);
     
    }

    위 소스에서 count는 무조건 0인데 왜 저런식으로?

    -3
  • 돈까스
    2k
    2019-06-19 10:21:06
    음 윗분께 조언을 드리자면 저건 일종의 word count 코드입니다.
    1
  • 돈까스
    2k
    2019-06-19 10:23:49 작성 2019-06-19 11:41:46 수정됨

    ccc님의 질문과 같은 의문이 생기는 이유는 코드가 읽기 어렵기 때문인데요.

    예를 들자면 따로 객체를 정의해서 사용하지 않고, Map의 Entry<K, V>를  그냥 쓰셨기 때문에

    key가 에 무슨 내용이 들어있고, value에는 뭐가 들어있는지 바로 알기 어렵고,

    첫번째 input은 무슨 의미이고, 두번째 input은 무슨 의미인지를 전체 코드를 이해한 후에야 알 수 있습니다.

    적절한 bean을 만든다거나 getKey()를 바로 쓰지않고 적절한 이름의 변수에 넣어서 사용하면 의도가 바로 나타나겠죠.

    3
  • ㅇㅈㅇ
    3k
    2019-06-19 10:33:38 작성 2019-06-19 10:34:28 수정됨

    ccc//

    count는 중복된 text만큼 증가하겠죠.

    ====

    일단 코드 평가는 공수를 따지지 않으면 그렇게 의미 없다고봅니다.

    아무리 경력이 높아도 적은 시간에 많은걸 하면 질낮은 코드가 나옵니다.

    코딩뿐아니라 대부분이 돈쓴 만큼 퀄이 나오죠.

    그래도 널체크는 습관적으로 했으면 좋겠고..

    IO관련 해서 자원해제 하는것도 습관적으로 해줬으면 좋겠네요.

    해당 API가 내부적으로 자동 처리가 되어있든 안되어있든. 

    바로 종료되는 쓰레드든 아니든 

    열면 닫는게 가독성도 좋음. 

    0
  • 자라선
    1k
    2019-06-19 10:34:22

    실무에서 람다가 많이쓰이나요?

    람다가 1.8버전에 출시했죠?

    뭔가 자바에서 람다는 잘 안어울리는거같아요

    0
  • 돈까스
    2k
    2019-06-19 10:37:30

    람다하고 스트림 많이 씁니다.

    쓰다가 안쓰면 굉장히 불편합니다.

    2
  • 수강권
    287
    2019-06-19 10:39:11

    추측이지만 딴지거신분이 자원해제 처리 안해주면 소름돋는분이 아니셨을까요

    0
  • 바코사
    37
    2019-06-19 10:44:04

    누구를 평가할 능력은 되지 않아 조언을 드릴순 없지만

    궁금한 점 질문 드립니다.

    ArrayList 대신 LinkedList를 사용하신 이유를 알고싶습니다.

    LinkedList의 퍼포먼스가 ArrayList보다 낮다는걸 들은적이 있어서요


    0
  • LichKing
    14k
    2019-06-19 10:45:58

    자바8나온지가 이제 5~6년입니다. 신나게 씁니다.

    0
  • zepinos
    18k
    2019-06-19 10:48:02

    음...일부러 댓글이 많이 달릴 때까지 아무말 하지 않고 그냥 있었습니다.


    1. 제가 얼마 전에 치른 코딩 테스트의 3문제 중 2문제...제가 제출한 답이었습니다. 1문제는 어떤 방식으로 문제를 해결해야 하는지 너무 어렵게 생각하다가 아예 풀지도 못했고, 지금은 어떤 식으로 풀어야 하는지(사실 너무 단순하게) 깨닫게 되어서 그것도 풀긴 했습니다. 방식만 알면 코딩 자체는 어렵지 않으니까요.

    2. Scanner 을 쓰지 않은 이유는, 코딩 테스트 사이트의 기본 코드가 그렇게 되어 있기 때문입니다. 제 기억이 맞다면 Scanner 보다 BufferedReader 가 속도가 더 빠른 것으로 알고 있고, 알고리즘에 연산속도까지 테스트 한다면 BufferedReader 쓰는 쪽이 더 낫다고 알고 있습니다. 평소에 저걸 쓰질 않으니...저도 그냥 그렇게만 알고 있습니다.

    3. 유료로 제공되는 시험이기에 문제를 일부러 적지 않았습니다. SQL 은 뭐...굳이 내용을 몰라도 의도하는 바가 뭔지 이해가 되시겠지만, Java 의 경우는 입력으로 콤마로 구분된 여러 문자열을 입력받고, 두번째 줄 입력으로 짧은 문자열을 입력받아 그 문자열로 시작하는 첫번째 입력의 문자열 단어를 나열하는데, 첫번째 입력 시 중복 입력된 문자열의 갯수만큼 정렬해서 출력하는 문제입니다. 그래서 Map 에 단어를 key 로, 나오는 빈도를 value 로 해서 저장한 뒤 sort 로 value 기준으로 Collections 객체에 담았습니다.

    4. 위 코드가 20년차 기준으로 미흡하지 않다면 제출 못한 한 문제 때문에 그렇다고 생각한다면, 평소 제 지론인 "알고리즘 테스트" 는 (특히 경력자들에겐) 쓸데없는 짓이다...라고 이 업체가 증명한다고 생각합니다. 물론 그 회사를 까고 싶은건 아니지만, 특정 문제에 대한 해결책 중 개발자가 해결해야 할 것도 있지만, 사실 수학자나 기획자 등 알고리즘 해결에 대한 방안은 개발자가 아닌 다른 사람이 주어야 할 부분도 있습니다. 그 문제를 못풀었다고 개발능력이 떨어진다고 판단한다면, 오히려 제 입장에선 그 회사에 안가는걸 추천드리고 싶네요. 그런 회사가 아니길 빌면서 제 코드에 어떤 부족한 점이 있는지 찾아보고 싶었던 겁니다.


    0
  • zepinos
    18k
    2019-06-19 10:53:49 작성 2019-06-19 10:56:23 수정됨

    바코사 님 // 정렬을 하게 되면 순서가 뒤바뀌게 됩니다. 물론 Java 의 ArrayList 는 충분히 빠르고 값들을 swap 해주는게 무리는 없겠지만, 그래도 기본적으로 순서를 바꾸는건 LinkedList 가 pointer 만 바꾸면 되기 때문에 더 낫다고 생각해서...그냥 저렇게 해봤습니다.


    돈까스 님 // 이건 제가 실수한 거 같네요. 코딩 테스트 사이트에서 쿼리에 대한 테스트케이스 입력을 할 수 없어서 그냥 잘 돌아가는지만 보고 그냥 처리했네요. (group by)

    첨언하자면, case when 을 쓴 이유는 ANSI SQL 에 가깝게 작성하기 위해서였고, sum 을 하는 이유는 해당 컬럼에서 3가지 형태의 값이 들어갈 수 있고(enum), 그것이 각각 몇 번인지 count 할 때 저런 식으로 많이 해왔습니다. ifnull 이야...뭐...요식행위죠. group by 빼먹은게 크네요.


    SQL 도 잘못 작성된 거라면...뭐...실력이 부족한게 맞네요. T.T

    0
  • 바코사
    37
    2019-06-19 10:57:27

    역시 sort 때문이였군요 담변 감사합니다. 

    0
  • 돈까스
    2k
    2019-06-19 11:00:53

    자바코드를 구조 변경 없이 변수명만 바꿨습니다.

    변수명을 정하는 것 정도는 시간이 많이 들지 않으니 바로 의미를 드러내는 이름을 쓰시는 것이 좋을 것 같습니다.


    class Main {

    public static void main(String[] args) throws Exception {

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String csvText = br.readLine();

    String[] words = csvText.split(",");
    Map<String, Integer> wordCountMap = new HashMap<>();
    for (int i = 0; i < words.length; i++) {

    String text = words[i].trim();
    int count = wordCountMap.getOrDefault(text, 0);
    wordCountMap.put(text, count + 1);

    }

    String searchPrefix = br.readLine();

    List<Map.Entry<String, Integer>> wordCountEntryList = new LinkedList<>(wordCountMap.entrySet());

    Collections.sort(wordCountEntryList, (o1, o2) -> {
    int comparision = (o1.getValue() - o2.getValue()) * -1;
    return comparision == 0 ? o1.getKey().compareTo(o2.getKey()) : comparision;
    });

    int i = 0;
    for (Map.Entry<String, Integer> wordCountEntry : wordCountEntryList) {

    String word = wordCountEntry.getKey();
    if (word.startsWith(searchPrefix)) {

    if (i++ > 0) System.out.print(", ");

    System.out.print(word);

    }

    }

    }

    }
    1
  • Joehispania
    183
    2019-06-19 11:02:17 작성 2019-06-19 11:07:35 수정됨

    이건 위에 zepinos 님이 댓글을 올리시기 전에 작성해 놓았던 댓글입니다.

    위의 댓글에서 System.in을 사용하신 이유도 달아놓으셨네요.

    아래의 평은 올리신 댓글을 확인하기 전 느낌들입니다.

    알고리즘 테스트 문제였을 줄 몰랐지 뭡니까 -_-;;


    ---------------------------------------------------------------------------------------------------------

    개인적으로 코드는 심플하게 만들던지 혹은 복잡한 기교를 부리던지 그건 각자 스타일이라고 생각합니다.

    다만 에러가 없는 것으로 예측되는 노멀한 케이스의 로직은 초심자든 고급이든 비슷할 수 있지만...

    세상만사 에러 없고 예외상황 없는 케이스는 있을리가 없잖아요?? 그런 때 고급개발자는 경험론적으로

    어떻게 코드를 방어할지 알고있다는것이 초심자와의 차이라고 봅니다.


    가장 처음 언급할것은 아무래도 리소스 해재 부분이네요. BufferedReader 호출 부분을 

    try-catch-finally에서 close 해주시거나 혹은 JDK1.7 부터 지원되는 try-with-resource를 통해서 

    close()를 자동으로 호출하도록 해주셨으면 좋았겠습니다. 


    다음으로 InputStream을 System.in 콘솔입력으로 받아서 처리하셨는데 

    개인적으로는 차라리 데이터를 파일에 저장해서 처리하는것이 더 좋지않을까 생각합니다.

    JUnit 테스트 코드 만들때도 System.in으로 받는 부분 테스트할 수 없잖아요 ^^;


    다음으로 신경 쓰이는것이 br.readline()을 호출하는 부분인데....첫번째는 Comma Seperated 형식의

    데이터를 받으시고 다음 br.readline()은 단순 텍스트 입력값을 받으시려는 것 같아요. 

    그런데 들어오는 데이터에 대해서 형식을 체크하기 위한 방어코드가 전혀없는것이 살짝 마음에

    걸립니다. 혹여라도  String[] inputArr = input.split(",");  이 부분에서 아무런 데이터가 안들어오면 

    결국 아래 라인 코드들은 전부 무용지물이 되어버리니까요. 이런 상황에서 어떻게 처리할지에 대한 

    방어코드가 삽입되면 좋을것 같습니다. 


    다음으로 Collections.sort()를 구현하는 부분에서 이 부분은 람다를 사용하셨는데....

    이 부분은 죄송하지만 왠지 그냥 인터넷에서 적당한 소스를 복사해서 가져오신 것 같다는 

    느낌을 받았습니다. 람다식을 아신다면 처음 BufferedReader 부분도 람다로 처리하셨을텐데....

    이런 인상을 받았습니다.  짧은 코드지만 람다식을 써야할것 같은 부분은 안쓰시고 어떤 부분은

    쓰셨는데 만약 현장에는 Open JDK 1.7만 깔려있다면 결국 이 코드는 동작안하겠죠. 

    차라리 Comparator를 직접 Override하여 구현하셨다면  그냥 알고있으시겠거니 하고 넘어갔을

    부분이  못내 신경쓰여서 말씀드렸습니다.


    마지막으로 기능단위로 메소드를 쪼개셨으면 어땠을까 생각이 듭니다. 물론 심플하게 뭔가를 

    테스트 할려고 하셨겠지만 실제 코드를 작성할 때 이런식으로 일련의 흐름으로 쭉~~~~ 

    코드를 기술해버리면 나중에 괴로워 지더군요. 물론 실제 코드 작성하실때는 기능단위로 

    잘 쪼개실거라고 봅니다. 저같은 경우는 남이 짜놓은 if-else에 가공로직에 동일 코드 반복이 뒤섞인

    끔찍한 1200라인 코드를 리팩토링 하면서 욕이 나왔던 경험이 있는지라 말씀드렸습니다. 


    3
  • SolarGrace
    327
    2019-06-19 11:10:34

    저는 꼬꼬마라 저 코드에 대해 왈가불가할 자격은 없구요 ㅋㅋㅋ


    근데..    댓글중 

    '특정 문제에 대한 해결책 중 개발자가 해결해야 할 것도 있지만, 사실 수학자나 기획자 등 알고리즘 해결에 대한 방안은 개발자가 아닌 다른 사람이 주어야 할 부분도 있습니다'

    이부분... 개개인마다 생각이 다르겠지만..



    예를 들면 이런 느낌이랄까요 요리사와 조리사의 차이점.

    단순히 레시피대로 만들기만 하는 사람은 요리사가 아닌 조리사다 라는 말을 제가 어디서 들어본 적이 있는데... 

    그와 비슷한 느낌인거같아요.



    뭐 제꿈이 한 때는 나사에서 미사일에 탑재되는 소프트웨어를 개발하는 사람이 되는거였기에.. 

    그럴지도 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ


    0
  • e3w4
    84
    2019-06-19 11:11:41

    아무 문제 없어보이는데요.

    코딩스타일은 회사나 개인차죠.


    저도 람다 좋아하고 잘쓰지만 그런걸 논할 문제는 아닌듯

    0
  • zepinos
    18k
    2019-06-19 11:15:21 작성 2019-06-19 11:25:05 수정됨

    Joehispania 님 // 위에 언급했듯이 코딩테스트라서 그렇습니다. 방어코드는...물론 넣을 수 있겠지만 문제에 잘못된 값을 넣었을 경우 어떻게 처리해라는 그런 내용이 나오진 않습니다. 일반적인 문제풀이 등을 봐도 방어코드를 넣질 않습니다. 풀이 속도 때문인 걸로 추측되구요.

    또한, 문제를 자동으로 돌려보기 때문에 파일로 입력값을 저장할 수도 없고, 사실 close() 호출하는 것도 별 의미 없기도 하구요(코딩 내용까지 꼼꼼히 봤을까 싶기도). 그리고 BufferedReader 부분도 코딩 부분에 처음 있는 걸 그대로 써서...저걸 lambda 로 쓰는 방법은 저도 잘 모르겠네요. sort 야 가끔 쓰니까, 어디서 긁어오는게 아니라 그냥저냥 저렇게 씁니다.

    실제로 이런 코드를 쓰곤 하거든요.

    list.stream().mapToInt(TestList::getValue).sum()


    그리고, 코딩테스트를 처음 해봐서 저도 잘 모르지만, 메서드로 쪼개면 실행속도 느려져서 필요한 경우 아니면 안나누는게 낫다고 판단했습니다.


    모든게 "코딩 테스트" 사이트에 촛점이 맞춰져서 그렇습니다. T.T


    SolarGrace 님 // 지금 여기 댓글 달고 계신 분들에게 인체 관절의 역학적 어쩌구저쩌구...이런걸 풀라고 하면 풀 수 있는 분이 몇 분이나 계실지 궁금하네요. 예를 들면 다익스트라 같은 최단거리 길을 찾는 알고리즘의 경우 백엔드 개발자가 그걸 이용해본 적이 거의 제로일텐데, 그걸 문제로 낸거랑 뭐가 다르냐는 문제입니다. 한식대첩 보니 최근에 외국 특급 요리사 데려와서 한식 요리사와 한식 만드는거 하던데, 왜 한식 요리사를 스승이라고 붙여줍니까? 특급 요리사인데, 한식 못하면 조리사죠...라고 하면 얼마나 황당할까요?

    제가 말하고자 하는건, 업무에 대한 이해는 그 회사 들어가서 쌓아도 된다는 겁니다. 토익처럼 답만 달달 외우고 치르게 되는 알고리즘 테스트에 대한 회의가 자꾸 쌓이는게 문제입니다. T.T

    아...하지만 반대로 SQL 문제는 정말 도움이 되는 테스트라고 생각합니다. 테스트 사이트에서 좀 더 문제를 명확하게, 그리고 테스트 값을 가지고 테스트 할 수 있게 해줬으면 하는 바램이 있네요.


    돈까스 님 // 제가 좀 순진했었네요. 변수명은 초기에 주어진 걸 안바꾸고 그냥 써서...;;;

    0
  • ccc
    1k
    2019-06-19 11:20:22

    돈까스 좋은 지적 감사합니다.

    코드를 한줄 한줄 읽었는데 코드 전체를 보고 이게 뭐하는 코드인지를 보라는 말씀이지요?

    뭔가 영어 공부할 때 단어 하나씩 해석하지 말고 문장 전체를 보라는 느낌이네요.

    제 개발 인생에 큰 도움이 될 것 같습니다.

    0
  • fender
    14k
    2019-06-19 11:25:06 작성 2019-06-19 13:47:50 수정됨

    기존 코드가 특별히 악평을 들을 만큼 나쁘다고 생각하진 않습니다. 솔직히 저도 코드 테스트 상황에서 갑자기 맞닥드렸으면 더 나은 코드를 짤 수 있었을지 의문입니다.

    다만 좀 더 '현대적'인 자바로는 이렇게도 할 수 있다는 정도로 예시를 남깁니다:

    import java.util.Comparator;
    import java.util.Map.Entry;
    import java.util.Scanner;
    import java.util.stream.Stream;

    import static java.util.function.Function.identity;
    import static java.util.stream.Collectors.*;

    class Main {

    public static void main(String[] args) {
    var scanner = new Scanner(System.in);

    var comparator = Comparator.<Entry<String, Long>, Long>
    comparing(Entry::getValue).reversed().thenComparing(Entry::getKey);

    var words = Stream.of(scanner.nextLine().split(",")).map(String::trim);
    var grouped = words.collect(groupingBy(identity(), counting())).entrySet().stream();
    var sorted = grouped.sorted(comparator).map(Entry::getKey);

    var input = scanner.nextLine();
    var result = sorted.filter(v -> v.startsWith(input)).collect(joining(", "));

    System.out.println(result);
    }
    }
    5
  • fender
    14k
    2019-06-19 11:27:13 작성 2019-06-19 11:34:06 수정됨

    여담이지만 위의 제 예시도 '좋은' 코드라고 하긴 좀 그렇습니다. 예를들어 단어 수도 매번 size()로 확인하지 않을 수 있는데 자바를 안한지 너무 오래되다 보니 튜플 같은 개념이 있는지 알 수가 없어서 그냥 저렇게 풀었네요...

    그리고 자바로 람다를 거의 안써보다 보니 예제 작성 중 API 레퍼런스 등을 자주 참조했습니다. 아마 참조가 없이는 저도 그냥 답만 나오게 하는 수준 이상의 결과를 내긴 힘들었을 것 같습니다.

    그래서 인터넷을 참조하지 않는 코드 테스트라는 건 문제를 내는 입장에서나 푸는 입장에서나 좋아하지 않는 편입니다.

    0
  • zepinos
    18k
    2019-06-19 11:40:51

    fender 님 // 역시 함수형의 달인이시군요. 저는 함수형은 영...사실 제시해주신 코드 이해 안됩니다. T.T

    0
  • fender
    14k
    2019-06-19 11:47:24

    zepinos // 아직 함수형은 객체지향 만큼 잘못쓰는 편이라, 그냥 매핑하고 필터하고 하는 수준을 벗어나지 못하는 수준입니다.

    스칼라한지가 꽤 되었는데도 일에 치이다보니 공부할 틈이 안나네요 ㅎㅎ;;

    0
  • zepinos
    18k
    2019-06-19 12:10:23

    Joehispania 님 // 아이고...죄송합니다. 그렇게 댓글 수정 안하셔도 되는 부분이었는데...^^;;;

    0
  • 배고파서서러워요
    2k
    2019-06-19 12:40:48

    SQL은 실제 테스트를 못하셨던 환경이라고 생각하면 아차하다 빼먹을 수도 있으니 넘어가고...


    소스 자체에선 전혀 문제 없다고 봅니다.

    fender님이 제시해준 함수형 소스도 협업일 경우에는 상황과 수준에 맞춰서 오히려 배제해야 하는 경우도 생길거고요...

    오히려 동작 흐름이 이해가 빨리 되어서, 코딩 테스트에는 적절한 해답이 아닐까 싶은데요.

    0
  • rezigrene
    1k
    2019-06-19 13:16:39

    즉석에선 본문정도 수준의 코딩을 벗어나기 힘들고 검색이 가능한 환경에 충분히 시간이주어진다면 fender님 같은 코드로 서서히 고쳐나가는건 가능하겠죠.

    size() 매번확인 같이 신경쓰이는 부분도 검색하면 해결책이 보통 나오니..

    https://stackoverflow.com/questions/25441088/group-by-counting-in-java-8-stream-api

    words.collect(groupingBy(identity(),Collectors.counting()))
    1
  • rezigrene
    1k
    2019-06-19 13:26:10

    성능문제로 감점 된거라면 표시할 목록을 필터로 뽑아낸 후 정렬하는 방법으로 정렬에 걸리는 시간 O(NlogN)을 줄여볼 수는 있겠습니다.

    0
  • zepinos
    18k
    2019-06-19 13:28:08

    말씀하신대로 선 추출 후 정렬...이 더 빠를 수는 있겠네요.

    0
  • fender
    14k
    2019-06-19 13:44:23

    rezigrene // 역시 필요할만한 건 다 있군요. 감사합니다! 예제 수정했습니다.


    덧글 : 혹시 Comparator 부분의 가독성을 개선할 방법을 알고 계신 분 있으시면 조언 부탁드리겠습니다.

    0
  • 유효하지않네
    1k
    2019-06-19 14:09:18


    비교 한번 해보십시요.~~~ㅎㅎ



    0
  • zepinos
    18k
    2019-06-19 14:12:46

    유효하지않네 님 // 전 "개발연차 대비 코딩완성도가 떨어진다" 는 평을 받는 사람이라...


    class Main() {
        public static void main(String[] args) {
            System.out.println("*");
            System.out.println("**");
            System.out.println("***");
            System.out.println("****");
            System.out.println("*****");
        }
    }

    이렇게밖에...

    0
  • 뒷집할머니
    1k
    2019-06-19 14:18:39

    "," 찍는거는 언어에 join 지원해주는게 많아서

    그거 쓰는 버릇하면 편해요.

    0
  • 유효하지않네
    1k
    2019-06-19 14:21:38 작성 2019-06-19 14:22:32 수정됨

    zepinos 님

    전 남이 잘 보고 이해하면 좋은 코드라고 생각됩니다.

    알지도 못하게 복잡하게 짜는건 고수라고 경력자라고 하기엔 

    제 개인적으론 힘들어 보입니다.

    zepinos 께서 올리신 소스는 크게 복잡하지도 않아 보이고 좋은 코드라고 생각됩니다.


    질문입니다.

    근데 이 게시판 활용은 어찌해야되나요..?

    닉네임에 어찌 링크를 걸죠.?? ㅎㅎ

    0
  • hello222
    911
    2019-06-19 14:25:23
    PO.REGISTERED_AT <= '2018-12-31 23:59:59'

    이부분도 시간 비교 같은데 이거 실제로 비교가 될지 의문이네요 ㅋ 
    0
  • zepinos
    18k
    2019-06-19 14:34:08 작성 2019-06-19 14:35:49 수정됨

    hello222 님 // MySQL 에선 저게 됩니다.


    유효하지않네 님 // 그냥 마우스로 드래그해서 복사한 뒤 붙여넣기 하면 됩니다. DHTML 에디터가 HTML 속성(링크와 색)을 그대로 붙여넣어줍니다. 다른 기능은 없어요. ^^;;;


    뒷집할머니 님 // 아...Java 도 8 부터 join 이 생겼네요. 이렇게 쉽게 처리할 수 있는 방법이 있었다니...;;;

    0
  • 냉동참치
    393
    2019-06-19 14:45:47
    개인적으로 실제 운영 할 소스는 SonarQube나 FindBug등을 통해서 테스트를 진행하나, 테스트용 임시코드는 어떻게 짜도 상관없다 주의라서...
    0
  • Hyperglide
    381
    2019-06-19 15:28:26

    음... 다른 분들이 말씀하시지 않은 포인트를 하나 써보려고 합니다.

    아래에 쓰는 내용과는 별개로, 작성하신 코드가 큰 결함이 있다고 생각하지 않아요 :) 다음에 보는 테스트는 더 잘하실 수 있을거에요!


    1. 코딩테스트 문제라는 특성상, Input이 무지막지하게 많을수도 있습니다.

    그리고 그로 인하여 출력물도 길어질 수 있구요.

    답이 짧은 경우에는 System.out.print를 써도 상관 없겠지만, 저는 가급적 StringBuilder에 모아놓은 뒤에 출력하고 있습니다.

    무슨 차이가 있을까? 싶을수도 있어서 속도 비교 링크를 아래와 같이 공유드립니다.

    https://www.acmicpc.net/blog/view/57


    네이밍이나 짜잘한거 신경 안쓰고 제 방식대로 다듬어본 코드 첨부드립니다.

    import java.io.*;
    import java.util.*;
    
    public class Main {
    
        public static void main(String[] args) throws Exception {
    
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    
            StringTokenizer st = new StringTokenizer(br.readLine(), ",");
    
            Map<String, Integer> map = new HashMap<>();
            while (st.hasMoreTokens()) {
                String text = st.nextToken().trim();
                int count = map.getOrDefault(text, 0);
                map.put(text, count + 1);
            }
    
            String query = br.readLine();
    
            List<Word> words = new ArrayList<>();
    
            for (String word : map.keySet()) {
                words.add(new Word(word, map.get(word)));
            }
    
            Collections.sort(words, Comparator.comparingInt(Word::getCount).reversed());
    
            StringBuilder sb = new StringBuilder();
            int iCount = 0;
            for (Word word : words) {
                if (!word.getName().startsWith(query)) {
                    continue;
                }
                if (iCount++ > 0) sb.append(", ");
                sb.append(word.getName());
            }
            System.out.print(sb);
        }
    }
    
    class Word {
        private String name;
        private int count;
    
        public Word(String name, int count) {
            this.name = name;
            this.count = count;
        }
    
        public String getName() {
            return name;
        }
    
        public int getCount() {
            return count;
        }
    }
    


    1
  • rezigrene
    1k
    2019-06-19 21:36:43 작성 2019-06-19 21:37:06 수정됨


    참고용으로 단어수에 따른 성능 측정결과 올립니다.

    스트림API 사용이 코드가 간결해진다는 점은 있으나, 풀어쓰는것이 성능에 좀더 유리합니다.

    물론 동일하게 스트림 API이용하더라도 몇줄차이로 성능이 심하게(첫번째와 4번째 방식차이)

    갈릴수도 있습니다.


    (단위: 밀리초)

    단어수스트림필터링필터링+댓글필터링+댓글활용본문방식댓글방식
    10k576676040
    1M1526241108161620623
    10M118437446817106445093488










    import java.io.StringWriter;
    import java.util.*;
    import java.util.function.Function;
    import java.util.regex.Pattern;
    import java.util.stream.Collectors;

    public class FindWord {
    public static void main(String... args) {
    bench(1, "abc", false); // 초기화용
    bench(10000, "abc", false);
    bench(1000000, "abc", false);
    bench(10000000, "abc", true);
    }

    private static void bench(int dataLength, String search, boolean skipOutput) {
    String data = generateInput(dataLength);
    System.out.println("Test Words: " + dataLength);
    long time = System.currentTimeMillis();
    time = solve1(data, search, time, skipOutput);
    time = solve2(data, search, time, skipOutput);
    time = solve3(data, search, time, skipOutput);
    time = solve4(data, search, time, skipOutput);
    time = solve5(data, search, time, skipOutput);
    time = Solve6.solve6(data, search, time, skipOutput);
    }

    private static String generateInput(int dataLength) {
    StringWriter sw = new StringWriter();
    Random rand = new Random(123);
    for (int i = 0; i < dataLength; i++) {
    if (i > 0) {
    sw.write(", ");
    }
    String uuid = UUID.randomUUID().toString().replaceAll("-","").substring(0, rand.nextInt(5) + 1);
    sw.write(uuid);
    }
    return sw.toString();
    }

    static long showTime(long startTime) {
    long end = System.currentTimeMillis();
    System.out.println("Time: " + (end - startTime));
    return end;
    }

    private static long solve1(String input, String search, long startTime, boolean skipOutput) {
    System.out.println("solve1");
    String result = Arrays.stream(input.split(","))
    .map(String::trim)
    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
    .entrySet().stream()
    .sorted(Map.Entry.<String, Long>comparingByValue().reversed().thenComparing(Map.Entry.comparingByKey()))
    .map(Map.Entry::getKey)
    .filter(v -> v.startsWith(search))
    .collect(Collectors.joining(", "));
    if (!skipOutput) {
    System.out.println(result);
    }
    return showTime(startTime);
    }

    private static long solve2(String input, String search, long startTime, boolean skipOutput) {
    System.out.println("solve2");
    String result = Arrays.stream(input.split(","))
    .map(String::trim)
    .filter(v -> v.startsWith(search))
    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
    .entrySet().stream()
    .sorted(Map.Entry.<String, Long>comparingByValue().reversed().thenComparing(Map.Entry.comparingByKey()))
    .map(Map.Entry::getKey)
    .collect(Collectors.joining(", "));
    if (!skipOutput) {
    System.out.println(result);
    }
    return showTime(startTime);
    }

    private static long solve3(String input, String search, long startTime, boolean skipOutput) {
    System.out.println("solve3");
    StringTokenizer st = new StringTokenizer(input, ",");

    Map<String, Long> map = new HashMap<>();
    while (st.hasMoreTokens()) {
    String text = st.nextToken().trim();
    if (text.startsWith(search)) {
    long count = map.getOrDefault(text, 0L);
    map.put(text, count + 1);
    }
    }
    String result = map.entrySet().stream()
    .sorted(Map.Entry.<String, Long>comparingByValue().reversed().thenComparing(Map.Entry.comparingByKey()))
    .map(Map.Entry::getKey)
    .collect(Collectors.joining(", "));
    if (!skipOutput) {
    System.out.println(result);
    }
    return showTime(startTime);
    }
    private static long solve4(String input, String search, long startTime, boolean skipOutput) {
    System.out.println("solve4");
    String result = Pattern.compile(",").splitAsStream(input)
    .map(String::trim)
    .filter(v -> v.startsWith(search))
    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
    .entrySet().stream()
    .sorted(Map.Entry.<String, Long>comparingByValue().reversed().thenComparing(Map.Entry.comparingByKey()))
    .map(Map.Entry::getKey)
    .collect(Collectors.joining(", "));
    if (!skipOutput) {
    System.out.println(result);
    }
    return showTime(startTime);
    }


    private static long solve5(String input, String search, long startTime, boolean skipOutput) {
    System.out.println("solve5");
    String[] inputArr = input.split(",");
    Map<String, Integer> map = new HashMap<>();
    for (int iCount = 0; iCount < inputArr.length; iCount++) {

    String text = inputArr[iCount].trim();
    int count = map.getOrDefault(text, 0);
    map.put(text, count + 1);

    }

    List<Map.Entry<String, Integer>> list = new LinkedList<>(map.entrySet());

    Collections.sort(list, (o1, o2) -> {
    int comparision = (o1.getValue() - o2.getValue()) * -1;
    return comparision == 0 ? o1.getKey().compareTo(o2.getKey()) : comparision;
    });

    int iCount = 0;
    for (Map.Entry<String, Integer> entry : list) {
    String key = entry.getKey();
    if (key.startsWith(search)) {
    if (!skipOutput) {
    if (iCount++ > 0) System.out.print(", ");
    System.out.print(key);
    }
    }
    }
    if (!skipOutput) {
    System.out.println();
    }
    return showTime(startTime);
    }


    }

    class Solve6 {

    public static long solve6(String input, String search, long startTime, boolean skipOutput) {
    System.out.println("solve6");
    StringTokenizer st = new StringTokenizer(input, ",");

    Map<String, Integer> map = new HashMap<>();
    while (st.hasMoreTokens()) {
    String text = st.nextToken().trim();
    int count = map.getOrDefault(text, 0);
    map.put(text, count + 1);
    }

    List<Word> words = new ArrayList<>();

    for (String word : map.keySet()) {
    words.add(new Word(word, map.get(word)));
    }

    Collections.sort(words, Comparator.comparingInt(Word::getCount).reversed().thenComparing(Word::getName));

    StringBuilder sb = new StringBuilder();
    int iCount = 0;
    for (Word word : words) {
    if (!word.getName().startsWith(search)) {
    continue;
    }
    if (iCount++ > 0) sb.append(", ");
    sb.append(word.getName());
    }
    if (!skipOutput) {
    System.out.print(sb);
    System.out.println();
    }
    return FindWord.showTime(startTime);
    }
    }

    class Word {
    private String name;
    private int count;

    public Word(String name, int count) {
    this.name = name;
    this.count = count;
    }

    public String getName() {
    return name;
    }

    public int getCount() {
    return count;
    }
    }
    5
  • ISA
    633
    2019-06-24 02:07:25

    잠안오는 밤에 좋은 글 보고 갑니다. 이유야 어찌되었든 이런 고민을 같이 해나가는게 보기 좋네요

    0
  • 학생01
    373
    2019-06-24 08:09:19

    초보자인 저가 보기엔 다 '헉'소리나는 코드네용.. 특히 regigrene 님것..ㄷㄷ

    0
  • crazygun22
    596
    2019-06-24 11:21:24


    import java.io.*;
    import java.util.*;
     
    class Main {
     
        public static void main(String[] args) throws Exception {
    
            Map<String, Integer> map = makeMap(readInput());
    
            List<Map.Entry<String, Integer>> list = mapToSortList(map);
           
            print(list, readInput());
        }
    
    
      private void print(List<Map.Entry<String, Integer>> list, String input){
    
            int iCount = 0;
            for (Map.Entry<String, Integer> entry : list) {
     
                String key = entry.getKey();
                if (key.startsWith(input)) {
     
                    if (iCount++ > 0) System.out.print(", ");
     
                    System.out.print(key);
     
                }
            }
      }
    
       
    
      public String readInput() {
    
          BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
          return  br.readLine();
     } 
    
    
       List<Map.Entry<String, Integer>> mapToSortList(Map<String, Integer> map) {
    
           List<Map.Entry<String, Integer>> list = new LinkedList<>(map.entrySet());
     
            Collections.sort(list, (o1, o2) -> {
                int comparision = (o1.getValue() - o2.getValue()) * -1;
                return comparision == 0 ? o1.getKey().compareTo(o2.getKey()) : comparision;
            });
       }
    
       public Map<String, Integer> makeMap(String input){
    
            String[] inputArr = input.split(",");
            Map<String, Integer> map = new HashMap<>();
            for (int iCount = 0; iCount < inputArr.length; iCount++) {
     
                String text = inputArr[iCount].trim();
                int count = map.getOrDefault(text, 0);
                map.put(text, count + 1);
            }
    
           return map;
       }  
    }


    0
  • rezigrene
    1k
    2019-06-24 12:25:12

    참고로 오해가 있을까봐,


    제가 올린 코드는 zepinos 님, fender 님, Hyperglide 님의 원본코드와

    아래 검색결과 들의 조합입니다..

    java stream groupby map key

    java stream groupby count

    java stream string split




    (실제 프로젝트에선 써본일이 없습니다. 다음에는 쓰겠지만.)





    0
  • 돌팅이
    130
    2019-06-25 02:08:39

    제 생각엔 출제자가 알고리즘 문제로 출제해서 그런거 같습니다.

    언급하신 코드를 보면 문자열을 다루는 방법과 알고리즘을 테스트하려던게 아니였나 조심스럽게 추측합니다.


    0
  • vollfeed
    870
    2019-06-25 10:10:40

    가독성 기준의 코드 품질은 괜찮겠으나,

    코테가 관심을 가지는 효율성 측면에서는 감점좀 받겠습니다.

    제가 문제를 오해했을수도 있으나, 

    위 코드 대로라면 처음 행에 주어진 단어가 10000개면 10000 크기의 소트를 해야합니다.

    하지만 입력 2행을 먼저 반영하여 매치되는게 100으로 줄면  소트사이즈가 100이되어 그것만으로 성능이 비약적으로 늘어납니다.

    일반적으로 소트는 매우 무거운 연산으로 간주되며,

    흔한 소트 성능은 n lg n 임으로 제가 제시한 입력사이즈, 매치 사이즈에선 대략 수백배의 성능차가 생길수있습니다.


    또 위 코드는 매치비교에 startswith를 N * M회 사용해야하나,

    이를 입력단어의 각 알파벳을 노드로하는 트리구조를 구축해 트리 탐색으로 처리하면 비교 횟수가 M으로 줄어듭니다.


    알고리즘에 깊게 이해하고 작성하는 사람과 비교해 

    대략 N * N 대 C(상수) 로 성능이 비교되니

    합격점은 어려울것 같네요.


    코드의 가독성은  제가 보기에도 괜찮으나, 이게 코딩 테스트로써 컴퓨터과학을 얼마나 이해하냐가 관심사면 평가 기준이 좀 다르겠습니다.

    1
  • zepinos
    18k
    2019-06-25 10:16:09

    vollfeed 님 // 그 부분은 제가 좀 순진했음을 인정합니다. 2번째 입력값을 이용해서 1번째 입력값을 아예 필터링 해서 저장한 뒤 정렬을 하면 훨씬 정렬할 데이터가 적어서 빨리 되겠죠. 다만, 실제 개발 시 이런 식의 개발은 있을 수 없으니, 어떻게 보면 제 창의성이 부족한 걸 수도 있지만, 코딩 테스트의 꼼수를 어느 선까지 적정하다고 생각해야 하는지도...제 스스로는 좀 의문이네요.

    0
  • vollfeed
    870
    2019-06-25 12:25:37

    꼼수라고 생각하실지 모르나,

    빅데이터를 취급한다면 천만 단위는 일상인텐데요,

    천만 *천만의 성능과 천만 + 백만의 성능을 너무 다르죠. 

    본래 알고리즘은 꼼수 대잔치아닌가요? 코테면 구현을 하냐/못하냐는 기준이 안될텐데요. 하는걸 구현 하는걸 전제로 누가 가장 베스트하냐가 핵심이니 모든 꼼수를 다써야죠. 

    그리고 무엇을 먼저 계산했는가는 DBMS에 연관지어서 생각하면 어느 것을 서브쿼리로 잡아서 먼저 계산하게 하여 디비성능을 낼것인가와 일맥상통합니다. 실전과 매우 연결되는 애기는 입니다. 파트별로 2배의 성능 차이만나도 모여서 32배쯤 나는건 일도 아니니까요. 결국 모든 상황에서 효율을 고민하는 사람의 프로그램과와 아닌 사람의 그것음 쾌적하게 쓸수있는가? 아닌가?의 결과가 나올수있습니다.

    여튼 테스트는 결국 토이 이그잼플로 할수밖에 없으니, 문제가 저렇다 하더라도 평가하고싶은게 알고리즘적인 사고관을 보고자하면 은연중에 드러나는것도 평가될수밖에 없을것 같습니다.




    0
  • zepinos
    18k
    2019-06-25 14:05:52 작성 2019-06-25 14:10:43 수정됨

    vollfeed 님 // 실제 업무라면, 꼼수도 아니고 쓸 수 없는 방법입니다. 위에 제가 언급했는지 모르지만 유료로 제공되는 문제이기에 정확한 문제는 말씀드리지 않았고, 사실 입력된 여러 단어들 중 앞부분이 일치하는 단어를 빈도수로 정렬하여 보여줘라(사실상 자동완성)...입니다. 애당초 입력 데이터를 필터링 해버리면 실제 검색해야할 단어가 여러번 들어온다면 쓸 수가 없겠죠. 실제로 몇 개의 알고리즘 문제를 풀다보니 입력을 정해진 수만큼 혹은 엔터 같이 아무런 값을 넣지 않을 때까지 계속 입력받는 형태의 문제도 있던데...그런 경우라면 말씀하신 그런건 쓸 수 없었을 것이고, 저런 조건이 코딩테스트가 아니라 실제 요청이라면 더더욱 하면 안되는 것이겠죠.

    위에서도 언급했듯이 제가 이런 코딩테스트를 처음 준비(사실 하루 했습니다만)하고 처음 시험을 봐서, 실제 업무 보듯이 하는게 아니라 주어진 문제에서 숨겨진 뜻을 찾아 그것에만 만족하는 최선(속도)의 코드를 작성해야 한다...는 것을 몰라서 이런 코드를 만든 것 같네요. 사실 저런 요건이 현업에서 주어지면 당연히 RDBMS 나 NoSQL 혹은 IMDG 에 저장해놓고 쓰지...저렇게 쓰는 경우는 드물테니까요.

    첨언하자면, 당연히 현업에선 저걸 처리할 서버(주로 API/Web 서버)가 한 대도 아닐텐데 자료 구조에 넣고 그걸 정렬하고...이런걸 직접 구현할 일도 없겠죠. 그래서 차라리 SQL 문제가 낫겠다고 제가 얘기한 부분도 있습니다. 저 역시 이런 요구조건이 있다면 최소 NoSQL, 아니면 IMDG 에 넣고 처리할 것 같네요.

    0
  • 한판만
    437
    2019-06-25 14:22:39

    짬이 찰 수록 기본에 가까운 코드가 가장 좋은 코드라고 생각합니다.

    그래서 저는 평소에도 기본에 노력하려고 합니다.


    제가 볼 땐 기본을 잘 짜주신 것 같은데 뭐가 문제라는건지 잘 모르겠습니다.

    0
  • vollfeed
    870
    2019-06-25 17:18:25

    그런데 typeahead 와 bloodhound 라는 웹용 자동완성 모듈을 아시나요?

    꽤 많이 쓰이는것 같던데 저도 써본적있구요.

    블러드하운드가 제가 설명한 트리구조체를 이용한 인덱스의 구현체로 되있습니다. 관심있으면 코드분석 한번해보세요. 

    그리고 실전이란 필요하면 뭐든 쓰는것이니, 용도만 맞으면 얼마든지 쓸수있는거 아닐까요?


    0
  • zepinos
    18k
    2019-06-25 18:03:47 작성 2019-06-26 08:36:36 수정됨

    vollfeed 님 // 제가 말씀드린 부분은 "하지만 입력 2행을 먼저 반영하여 매치되는게 100으로 줄면  소트사이즈가 100이되어 그것만으로 성능이 비약적으로 늘어납니다." 이 부분에 대한 것입니다. Tree 를 사용하는 것에 대해서 반박한 것은 아닙니다.

    이와 별개로 전방일치일 경우 startWith 로 매번 찾는 것보다 처음 비용이 발생하더라고도 trie 를 쓰는게 훨씬 나은 선택지인 것이 맞습니다. 여기에 대해서 반박하려는 건 아니고, 다만 실제 개발 시에는 trie 같은 구조체를 만들어 사용하기 보다는 DB 등 Index 가 있는 곳에 데이터를 넣어두고 해당 시스템이 제공하는 search 을 이용하기에 제가 문제 풀면서 trie 을 이용해 저장하는 것을 떠올리지 못했구요. 그냥 간편하게 구현하려고만 했지, 성능 극대화는 생각치 못했습니다.

    0
  • 레버리지
    2k
    2019-06-25 18:35:22

    쿼리만 봤을땐



    --      ,IFNULL(SUM(CASE PO.ORDER_STATUS WHEN 'READY' THEN 0 WHEN 'SHIPPING' THEN 0 WHEN 'COMPLETE' THEN 1 END), 0) AS COMPLETE_CNT
    --      ,IFNULL(SUM(CASE PO.ORDER_STATUS WHEN 'READY' THEN 0 WHEN 'SHIPPING' THEN 1 WHEN 'COMPLETE' THEN 0 END), 0) AS SHIPPING_CNT
    --      ,IFNULL(SUM(CASE PO.ORDER_STATUS WHEN 'READY' THEN 1 WHEN 'SHIPPING' THEN 0 WHEN 'COMPLETE' THEN 0 END), 0) AS READY_CNT
          ,COUNT(CASE WHEN PO.ORDER_STATUS = 'COMPLETE' THEN 1 END ) AS COMPLETE_CNT
          ,COUNT(CASE WHEN PO.ORDER_STATUS = 'SHIPPING' THEN 1 END ) AS SHIPPING_CNT
          ,COUNT(CASE WHEN PO.ORDER_STATUS = 'READY'    THEN 1 END ) AS READY_CNT


    DB 타임설정이 변경될 경우를 대비해서 문자열을 조건에 넣기보다는
    
    -- AND PO.REGISTERED_AT <= '2018-12-31 23:59:59'
    AND PO.REGISTERED_AT <= STR_TO_DATE( '2018-12-31 23:59:59', '%Y-%m-%d %H:%i:%s' )


    감사합니다.

    0
  • G7CFE
    335
    2019-06-25 18:48:21
    댓글들을 보면서 오키에는 정말 고수분들이 많구나 하는걸 다시 한번 느낍니다.
    0
  • laravel.kr
    48
    2019-06-25 18:59:22

    20년차의 완성도..

    완성도라는 표현이 애매하게 들리네요. 코드가 안 이쁘다는 건지, 성능이 느리다는 건지..

    누가 말한건지는 모르겠지만 안이쁘면 어디가 못생겼는지 말해주면 되고, 성능이 느리다면 최적화 가능한 부분을 짚어주면 될 일인데.

    기분 좋으면 완성도 높고, 기분 안좋으면 완성도 낮은건지...

    0
  • rezigrene
    1k
    2019-06-26 02:39:46

    제대로 된 코딩테스트라면 의도를 명확히 드러냅니다. 1회기초데이터 입력후 1회만 검색인지 아니면 여러번 검색에쓰일지를 헷갈리게 내진않습니다. 만일헷갈리셨다면 그건 코딩테스트 문제가 이상한 거니 걍 출제자가 문제 준비를 좀 못했구나 생각하시면 됩니다.(구글 코드잼 같은 경우엔 그의도대로 작성못하면 아예 시간초과로 실패해버립니다)


    추가로 제가 올린 여러풀이법중 시간이 빠르게나온 3,4 번째풀이법은 1회 데이터 입력에 1검색어 입력을 전제로는 100점 만점에 70-80 정도 줄수 있을지 모르나, 여러번 검색을 전제로하면 10점 정도도 줄 수있을까말까한 안좋은 방법입니다.

    이경우는 vollfeed님처럼 접근해야 합니다.

    (본문의 코드는 30-40점?)


    성능상 더 좋은 방법은 구현 할수 있으나 유지보수성을 생각해서 본문처럼 구현하신 거라면 코드 완성도같은 말은 무시하시고 그냥 알고리즘 테스트 경험 부족으로 의도대로의 코드를만들어 주지 못한거니 많이 풀면서 시간초과 많이 당해보시면 자연스럽게 코딩테스트 의도대로 푸시게 될겁니다.



    0
  • 로그인을 하시면 댓글을 등록할 수 있습니다.