1. ARRAY, STRUCT 연습문제 /*1) array_exercise 테이블에서 각 영화(title)별로 장르를 UNNEST 해서 보여주세요*/ select title, genre from `advanced.array_exercises`, unnest(genres) as genre /*2) array_exercise 테이블에서 각 영화별로 배우와 배역을 보여주세요. 배우와 배역은 별도의 칼럼으로 보여주세요*/ select title, actor.actor, actor.character from `advanced.array_exercises`, unnest(actors) as actor /*3) array_exercise 테이블에서 각 영화별로 배우, 배역, 장르를 출력하세요. 한 Row에 배우, 배역, 장르가 모두 표시되어야 합니다.*/ select title, actor.actor, actor.character, genre from `advanced.array_exercises` ,unnest(actors) as actor ,unnest(genres) as genre -- 문제의의도: UNNEST를 2번 연속 사용 가능하다 -- 데이터 중복은 CROSS JOIN으로 인한 어쩔 수 없는 이슈 -- SQL 실행순서 -> FROM -> JOIN -> SELECT -- actors : ARRAY<STRUCT> -> UNNEST -> STRUCT -- genres : ARRAY<STRING> -> UNNEST -> STRING /*4) 앱로그데이터(app_logs)의 배열을 풀어주세요*/ select event_date, datetime(timestamp_micros(event_timestamp),'Asia/Seoul') as event_timestamp, event_name, event_param.key as key, event_param.value.string_value as string_value, event_param.value.int_value as int_value, user_id from `advanced.app_logs` ,unnest(event_params) as event_param -- event_params : ARRAY<STRUCT> -- event_params.value : STRUCT<STRING, INT64> 2. PIVOT 연습문제 /*1) orders 테이블에서 유저별로 주문금액의 합계를 PIVOT해주세요. 날짜를 행으로, user_id를 열로 만들어야합니다. /*case when 사용한 풀이*/ select order_date, sum(case when user_id = 1 then amount else 0 end) as user_1, sum(case when user_id = 2 then amount else 0 end) as user_2, sum(case when user_id = 3 then amount else 0 end) as user_3 from `advanced.orders` group by order_date order by order_date /*if 사용한 풀이*/ select order_date, sum(if(user_id = 1,amount,0)) as user_1, sum(if(user_id = 2,amount,0)) as user_2, sum(if(user_id = 3,amount,0)) as user_3 from `advanced.orders` group by order_date order by order_date /*2) orders 테이블에서 날짜별로 유저들의 주문금액의 합계를 PIVOT 해주세요. user_id를 행으로, order_date를 열로 만들어야 합니다.*/ select user_id, sum(if(order_date = '2023-05-01',amount,0)) as `2023-05-01`, sum(if(order_date = '2023-05-02',amount,0)) as `2023-05-02`, sum(if(order_date = '2023-05-03',amount,0)) as `2023-05-03`, sum(if(order_date = '2023-05-04',amount,0)) as `2023-05-04`, sum(if(order_date = '2023-05-05',amount,0)) as `2023-05-05` FROM `advanced.orders` group by user_id order by user_id -- ANY_VALUE: 그룹화할 대상 중 임의의 값(NULL 제외)를 표시한다. -- 나머지 값이 NULL이거나 확정적으로 값이 나올것이라 예상될 때 사용 /*3)orders 테이블에서 사용자별, 날짜별 주문이 있다면 1 없다면 0으로 PIVOT 해주세요. user_id를 행으로 order_date를 칼럼으로 만들고 주문횟수에 상관없이 1로 처리합니다.*/ select user_id, MAX(if(order_date = '2023-05-01' and amount is not null,1,0)) as `2023-05-01`, MAX(if(order_date = '2023-05-02' and amount is not null,1,0)) as `2023-05-02`, MAX(if(order_date = '2023-05-03' and amount is not null,1,0)) as `2023-05-03`, MAX(if(order_date = '2023-05-04' and amount is not null,1,0)) as `2023-05-04`, MAX(if(order_date = '2023-05-05' and amount is not null,1,0)) as `2023-05-05` FROM `advanced.orders` group by user_id order by user_id; /* 횟수를 구해달라고 하는 경우*/ -- MAX를 SUM으로 바꾸면 된다. /*4) APP_LOG PIVOT 실습하기*/ select user_id, event_date, datetime(timestamp_micros(event_timestamp), 'Asia/Seoul') as event_timestamp, event_name, max(if(params.key = 'firebase_screen',params.value.string_value,NULL)) as firebase_screen, max(if(params.key = 'food_id',params.value.int_value,NULL)) as food_id, max(if(params.key = 'session_id',params.value.string_value,NULL)) as session_id from `advanced.app_logs` , unnest(event_params) as params where event_date = '2022-08-01' group by 1,2,3,4 3. 퍼널 분석 연습문제 /* 일자별 퍼널별 유저 수 집계 기간: 2022-08-01 ~ 2022-08-18 사용할 event => event_name과 firebase_screen의 값을 concat하여 사용 1. screen_view-welcome 2. screen_view-home 3. screen_view-food_category 4. screen_view-restaurant 5. screen_view-cart 6. click_payment-cart -- screen_view-welcome에서 user_id는 NULL임 -> user_pseudo_id는 NULL이 아님 -- welcome에서 home으로 넘어가며 로그인을 하여서 그럼 -- WHERE: FROM 절에서 바로 필터링을 하고 싶은 조건을 지정 -- HAVING: GROUP BY 후에 나오는 집계 결과에 대한 조건을 지정 */ with base as ( select event_date, datetime(timestamp_micros(event_timestamp),'Asia/Seoul') as event_timestamp, event_name, user_pseudo_id, max(if(params.key = 'firebase_screen',params.value.string_value,NULL)) as firebase_screen, max(if(params.key = 'food_id',params.value.int_value,NULL)) as food_id, max(if(params.key = 'session_id',params.value.string_value,NULL)) as session_id from `advanced.app_logs` , unnest(event_params) as params where event_date between '2022-08-01' and '2022-08-18' group by 1,2,3,4 ) select event_date, event_name_with_screen, case when event_name_with_screen = 'screen_view-welcome' then 1 when event_name_with_screen = 'screen_view-home' then 2 when event_name_with_screen = 'screen_view-food_category' then 3 when event_name_with_screen = 'screen_view-restaurant' then 4 when event_name_with_screen = 'screen_view-cart' then 5 when event_name_with_screen = 'click_payment-cart' then 6 end as step_number, count(distinct user_pseudo_id) as cnt from ( select *, case when event_name in ('screen_view','click_payment') then concat(event_name,'-',firebase_screen) else null end as event_name_with_screen from base ) group by 1,2,3 having step_number is not null order by event_date, step_number
연습 문제 (1) ARRAY, STRUCT 연습문제 1-1. --1-1) array_exercises 테이블에서 각 영화(title)별로 장르(genres)를 UNNEST해서 보여주세요. SELECT title , genre FROM advanced.array_exercises , unnest(genres) as genre 1-2. --1-2) array_exercises 테이블에서 각 영화(title)별로 배우(actor)와 배역(character)을 보여주세요. 배우와 배역은 별도의 컬럼으로 나와야 합니다. select title , actor.actor , actor.character from advanced.array_exercises , unnest(actors) as actor 1-3. --1-3) array_exercises 테이블에서 각 영화(title)별로 배우(actor), 배역(character), 장르(genre)를 출력하세요. 한 Row에 배우, 배역, 장르가 모두 표시되어야 합니다. select title , actor.actor as actor , actor.character as character , genre from advanced.array_exercises , unnest(actors) as actor , unnest(genres) as genre 1-4. --1-4) 앱 로그 데이터(app_logs)의 배열을 풀어주세요 select user_id , event_date , event_name , user_pseudo_id , param.key as key , param.value.string_value as string_value , param.value.int_value as int_value from advanced.app_logs , unnest(event_params) as param where event_date = '2022-08-01' (2) PIVOT 연습문제 2-1. --2-1) orders 테이블에서 유저(user_id)별로 주문 금액(amount)의 합계를 PIVOT해주세요. 날짜(order_date)를 행(Row)으로, user_id를 열(Column)으로 만들어야 합니다. select order_date , sum(if(user_id = 1,amount,0)) as user_1 , sum(if(user_id = 2,amount,0)) as user_2 , sum(if(user_id = 3,amount,0)) as user_3 from advanced.orders group by order_date order by order_date 2-2. --2-2) orders 테이블에서 날짜(order_date)별로 유저들의 주문 금액(amount)의 합계를 PIVOT 해주세요. user_id를 행(Row)으로, order_date를 열(Column)으로 만들어야 합니다. select user_id , max(if(order_date = '2023-05-01',amount,0)) as `2023-05-01` , max(if(order_date = '2023-05-02',amount,0)) as `2023-05-02` , max(if(order_date = '2023-05-03',amount,0)) as `2023-05-03` , max(if(order_date = '2023-05-04',amount,0)) as `2023-05-04` , max(if(order_date = '2023-05-05',amount,0)) as `2023-05-05` from advanced.orders group by user_id order by user_id 2-3. --2-3) orders 테이블에서 사용자별, 날짜별로 주문이 있따면 1, 없다면 0으로 PIVOT 해주세요. user_id를 행(Row)으로, order_date를 열(Column)로 만들고 주문을 많이 해도 1로 처리합니다. select user_id , max(if(order_date = '2023-05-01',1,0)) as `2023-05-01` , max(if(order_date = '2023-05-02',1,0)) as `2023-05-02` , max(if(order_date = '2023-05-03',1,0)) as `2023-05-03` , max(if(order_date = '2023-05-04',1,0)) as `2023-05-04` , max(if(order_date = '2023-05-05',1,0)) as `2023-05-05` from advanced.orders group by user_id order by user_id 2-4. --2-4) 앱 로그 데이터 배열 PIVOT 하기 select user_id , event_date , event_name , user_pseudo_id , max(if(param.key = 'firebase_screen', param.value.string_value, null)) as firebase_screen , max(if(param.key = 'food_id', param.value.int_value, null)) as food_id , max(if(param.key = 'session_id', param.value.string_value, null)) as session_id from advanced.app_logs , unnest(event_params) as param where event_date = '2022-08-01' group by all (3) 퍼널 연습문제 3-1. --3-1) 퍼널 별 유저 수 집계(일자별) with base as ( select event_date , event_name , event_timestamp , user_id , user_pseudo_id , platform , max(if(param.key = 'firebase_screen', param.value.string_value, null)) as firebase_screen from advanced.app_logs , unnest(event_params) as param where event_date between '2022-08-01' and '2022-08-18' group by all ), filter_event as ( select * except(event_name, firebase_screen) , concat(event_name, "-", firebase_screen) as event_name_with_screen from base where event_name in ('screen_view', 'click_payment') ) select event_date , event_name_with_screen , case when event_name_with_screen = 'screen_view-welcome' then 1 when event_name_with_screen = 'screen_view-home' then 2 when event_name_with_screen = 'screen_view-food_category' then 3 when event_name_with_screen = 'screen_view-restaurant' then 4 when event_name_with_screen = 'screen_view-cart' then 5 when event_name_with_screen = 'click_payment-cart' then 6 else null end as step_number , count(distinct user_pseudo_id) as cnt from filter_event group by all having step_number is not null order by event_date 3-2. --3-2) 퍼널 별 유저 수 집계 PIVOT with base as ( select event_date , event_name , event_timestamp , user_id , user_pseudo_id , platform , max(if(param.key = 'firebase_screen', param.value.string_value, null)) as firebase_screen from advanced.app_logs , unnest(event_params) as param where event_date between '2022-08-01' and '2022-08-18' group by all ), filter_event as ( select * except(event_name, firebase_screen) , concat(event_name, "-", firebase_screen) as event_name_with_screen from base where event_name in ('screen_view', 'click_payment') ), daily_group as ( select event_date , event_name_with_screen , case when event_name_with_screen = 'screen_view-welcome' then 1 when event_name_with_screen = 'screen_view-home' then 2 when event_name_with_screen = 'screen_view-food_category' then 3 when event_name_with_screen = 'screen_view-restaurant' then 4 when event_name_with_screen = 'screen_view-cart' then 5 when event_name_with_screen = 'click_payment-cart' then 6 else null end as step_number , count(distinct user_pseudo_id) as cnt from filter_event group by all having step_number is not null order by event_date ) select event_date , max(if(event_name_with_screen = 'screen_view-welcome',cnt,null)) as screen_view_welcome , max(if(event_name_with_screen = 'screen_view-home',cnt,null)) as screen_view_home , max(if(event_name_with_screen = 'screen_view-food_category',cnt,null)) as screen_view_food_category , max(if(event_name_with_screen = 'screen_view-restaurant',cnt,null)) as screen_view_restaurant , max(if(event_name_with_screen = 'screen_view-cart',cnt,null)) as screen_view_cart , max(if(event_name_with_screen = 'click_payment-cart',cnt,null)) as click_payment_cart from daily_group group by all order by event_date
1. ARRAY, STRUCT 연습 문제 1-1) ARRAY로 된 genres 컬럼을 평면화해 genre로 alias를 붙이고, title과 CROSS JOIN 하였습니다. SELECT title , genre FROM advanced.array_exercises CROSS JOIN UNNEST(genres) AS genre 1-2) 배우와 배역은 actor컬럼에 ARRAY<STRUCT<actor STRING, character STRING>> 타입으로 저장되어 있습니다. 우선 ARRAY를 평면화하고 .(dot)을 사용해 컬럼을 구분하였습니다. SELECT title , actor.actor , actor.character FROM advanced.array_exercises CROSS JOIN UNNEST(actors) AS actor 1-3) 처음에는 UNNEST 가 익숙하지 않아 단순히 두 데이터의 title을 키 값으로 CTE_ACTOR 테이블에 CTE_GENRE 테이블을 LEFT JOIN 하자는 생각이 들었습니다. WITH CTE_ACTOR AS ( SELECT title , actor.actor AS actor , actor.character AS character FROM advanced.array_exercises CROSS JOIN UNNEST(actors) AS actor ), CTE_GENRE AS ( SELECT title , genre FROM advanced.array_exercises CROSS JOIN UNNEST(genres) AS genre ) SELECT A.title , A.actor , A.character , B.genre FROM CTE_ACTOR A JOIN CTE_GENRE B ON A.title = B.title 강의를 듣고는 CROSS JOIN 을 두 번 해보았습니다. 배열이 각각의 행으로 풀리니 이를 두 번 실행해 결과를 얻을 수 있었고 쿼리도 훨씬 간단해졌습니다 😀 WITH CTE_ACTOR AS ( SELECT title , actor.actor AS actor , actor.character AS character FROM advanced.array_exercises CROSS JOIN UNNEST(actors) AS actor ), CTE_GENRE AS ( SELECT title , genre FROM advanced.array_exercises CROSS JOIN UNNEST(genres) AS genre ) SELECT A.title , A.actor , A.character , B.genre FROM CTE_ACTOR A JOIN CTE_GENRE B ON A.title = B.title 1-4) SELECT user_id , event_date , event_name , user_pseudo_id , event_param.key , event_param.value.string_value , event_param.value.int_value FROM advanced.app_logs , UNNEST(event_params) event_param -- WHERE event_date='2022-08-02' 2. PIVOT 연습문제 2-1) order_date를 기준으로 SUM 을 사용해 유저 별 주문금액 합계를 구하고, 요구사항에 따라 빈 값은 0으로 채웠습니다. SELECT order_date , SUM(IF(user_id=1, amount, 0)) AS user_1 , SUM(IF(user_id=2, amount, 0)) AS user_2 , SUM(IF(user_id=3, amount, 0)) AS user_3 FROM advanced.orders GROUP BY order_date 2-2) SELECT user_id , SUM(IF(order_date='2023-05-01', amount, 0)) AS `2023-05-01` , SUM(IF(order_date='2023-05-02', amount, 0)) AS `2023-05-02` , SUM(IF(order_date='2023-05-03', amount, 0)) AS `2023-05-03` , SUM(IF(order_date='2023-05-04', amount, 0)) AS `2023-05-04` , SUM(IF(order_date='2023-05-05', amount, 0)) AS `2023-05-05` FROM advanced.orders GROUP BY user_id 2-3) SELECT user_id , MAX(IF(order_date='2023-05-01', 1, 0)) AS `2023-05-01` , MAX(IF(order_date='2023-05-02', 1, 0)) AS `2023-05-02` , MAX(IF(order_date='2023-05-03', 1, 0)) AS `2023-05-03` , MAX(IF(order_date='2023-05-04', 1, 0)) AS `2023-05-04` , MAX(IF(order_date='2023-05-05', 1, 0)) AS `2023-05-05` FROM advanced.orders GROUP BY user_id 2-4) SELECT user_id , event_date , event_name , event_timestamp , user_pseudo_id , MAX(IF(event_param.key='firebase_screen', event_param.value.string_value, null)) AS `firebase_screen` , MAX(IF(event_param.key='food_id', event_param.value.int_value, null)) AS `string_value` , MAX(IF(event_param.key='session_id', event_param.value.string_value, null)) `int_value` FROM advanced.app_logs , UNNEST(event_params) event_param WHERE event_date='2022-08-25' AND event_name='click_cart' GROUP BY ALL 3. 퍼널 쿼리 연습 문제 처음엔 CASE 함수를 바로 떠올리지 못해 순서를 저장한 테이블을 만들고 JOIN해 결과를 구했습니다. WITH CTE_STEP AS ( SELECT 'screen_view-welcome' `event_name_with_screen`, 1 `step_number` UNION ALL SELECT 'screen_view-home', 2 UNION ALL SELECT 'screen_view-food_category', 3 UNION ALL SELECT 'screen_view-restaurant', 4 UNION ALL SELECT 'screen_view-cart', 5 UNION ALL SELECT 'click_payment-cart', 6 ), CTE_EVENT AS ( SELECT event_date , event_name_with_screen , COUNT(DISTINCT user_pseudo_id) `cnt` FROM ( SELECT event_date , user_pseudo_id , CONCAT(event_name, '-', event_param.value.string_value) `event_name_with_screen` FROM advanced.app_logs , UNNEST(event_params) `event_param` WHERE event_date BETWEEN '2022-08-01' AND '2022-08-18' AND event_name IN ('screen_view', 'click_payment') AND event_param.key = 'firebase_screen' AND event_param.value.string_value IN ('welcome', 'home', 'food_category', 'restaurant', 'cart') ) GROUP BY event_date , event_name_with_screen ) SELECT A.event_date , A.event_name_with_screen , B.step_number , A.cnt FROM CTE_EVENT A JOIN CTE_STEP B ON A.event_name_with_screen = B.event_name_with_screen ORDER BY event_date, step_number 이후 강의를 참고해 피벗 테이블을 만들고 CASE문을 사용한 쿼리입니다. WITH base AS ( SELECT event_date , event_timestamp , event_name , user_pseudo_id , MAX(IF(event_param.key='firebase_screen', event_param.value.string_value, null)) `firebase_screen` , MAX(IF(event_param.key='session_id', event_param.value.string_value, null)) `session_id` FROM advanced.app_logs , UNNEST(event_params) `event_param` WHERE event_date BETWEEN '2022-08-01' AND '2022-08-18' AND event_name IN ('screen_view', 'click_payment') GROUP BY ALL ), filter_event_and_concat_evnet_and_screen AS ( SELECT * EXCEPT(event_name, firebase_screen, event_timestamp) , CONCAT(event_name, '-', firebase_screen) `event_name_with_screen` , DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul') `event_datetime` FROM base WHERE event_name IN ('screen_view', 'click_payment') ) SELECT event_date , event_name_with_screen , CASE WHEN event_name_with_screen = "screen_view-welcome" THEN 1 WHEN event_name_with_screen = "screen_view-home" THEN 2 WHEN event_name_with_screen = "screen_view-food_category" THEN 3 WHEN event_name_with_screen = "screen_view-restaurant" THEN 4 WHEN event_name_with_screen = "screen_view-cart" THEN 5 WHEN event_name_with_screen = "click_payment-cart" THEN 6 ELSE NULL END AS step_number , COUNT(DISTINCT user_pseudo_id) FROM filter_event_and_concat_evnet_and_screen GROUP BY ALL HAVING step_number IS NOT NULL ORDER BY event_date 추가로 결과 테이블을 피벗 테이블로 만들기 위한 쿼리입니다. SELECT event_date , MAX(IF(event_name_with_screen='screen_view-welcome', cnt, 0)) `screen_view-welcome` , MAX(IF(event_name_with_screen='screen_view-home', cnt, 0)) `screen_view-home` , MAX(IF(event_name_with_screen='screen_view-food_category', cnt, 0)) `screen_view-food_category` , MAX(IF(event_name_with_screen='screen_view-restaurant', cnt, 0)) `screen_view-restaurant` , MAX(IF(event_name_with_screen='screen_view-cart', cnt, 0)) `screen_view-cart` , MAX(IF(event_name_with_screen='click_payment-cart', cnt, 0)) `click_payment-cart` FROM result GROUP BY event_date ORDER BY event_date ✔ 느낀 점 쿼리를 작성하기 전 자료형을 파악하자. 요구사항이 헷갈릴 땐 한번에 결과를 내려고 하지말고 단계별로 해결하자. ✔ 새롭게 알게된 점 cmd + D 를 여러 번 해서 같은 단어를 동시에 바꿀 수 있다. group by ALL 을 사용하면 SELECT의 컬럼을 반복해서 적지 않아도 된다. 구글 스프레드 시트에서도 예약 시간을 사용하면 갱신되는 데이터를 제공할 수 있다(예약 시간 주의). 구글 스프레드 시트로 간단한 시각화를 해볼 수 있다. 퍼널 분석에 대한 부분을 재밌게 수강했습니다. 퍼널 분석은 개념 정도만 알고 있었는데 퍼널을 정의하는 것에서 부터 피벗 테이블을 만들고, 시각화하기까지 실무에서의 큰 흐름을 배운 것 같아요. 팀에서 로그 설계를 진행 중인데 완료되면 간단한 퍼널 분석을 시도해보겠습니다💪🏻
7) Ajax 로그인 구현 & CSRF 설정에서 그 다음으로 코드를 수정하는 챕터는 지금 이 파트인거 같은데 갑자기 SecurityConfig 클래스에 customConfigurer, ajaxAuthenticationProvider, ajaxAuthenticationSuccessHandler, ajaxAuthenticationFailureHandler 메서드가 왜 있는건가요.. 하.. 이게 한 두개 챕터면 그냥 실수라고 생각하지만 지금 10챕터 이상이 이렇게 수정하면서 진행하는데 강의 이해도도 떨어지고 시간도 다 잡아먹고 뭐하는건지 모르겠습니다.. 아무리 리팩토링하셨다고 말씀하셨더라도 그럼 리팩토링한 영상을 새로 올리시거나 아니시면 리팩토링 전으로 강의가 흘러가야되는거 아닌가요...답답해서 참다가 글 남깁니다..
===== [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예) [질문 내용] <ResetClose Client> package network.exception.close.reset; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import static util.MyLogger.log; public class ResetCloseClient { public static void main(String[] args) throws IOException, InterruptedException { Socket socket = new Socket("localhost", 12345); log("소캣 연결: " + socket); InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream(); // Client <- Server : FIN Thread.sleep(1000); // 서버가 Close() 호출할 때 까지 잠시 대기 // Client -> Server: PUSH[1] output.write(1); // Client <-- Server : RST Thread.sleep(1000); //RST 메시지 전송 대기 try { int read = input.read(); System.out.println("read = " + read); } catch (IOException e) { e.printStackTrace(); } try { output.write(1); } catch (IOException e) { e.printStackTrace(); } } } <ResetCloseServer> package network.exception.close.reset; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import static util.MyLogger.log; public class ResetCloseServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(12345); Socket socket = serverSocket.accept(); log("소캣 연결 : " + socket); socket.close(); serverSocket.close(); log("소캣 종료 : " + socket); } } <클라이언트 결과> C:\Users\nevr\.jdks\openjdk-21.0.2\bin\java.exe "-javaagent:E:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2023.3.3\lib\idea_rt.jar=49785:E:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2023.3.3\bin" -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath E:\mork\study\java-adv2\out\production\java-adv2 network.exception.close.reset.ResetCloseClient 06:06:39.444 [ main] 소캣 연결: Socket[addr=localhost/127.0.0.1,port=12345,localport=49791] java.net.SocketException: 현재 연결은 사용자의 호스트 시스템의 소프트웨어의 의해 중단되었습니다 at java.base/sun.nio.ch.SocketDispatcher.read0(Native Method) at java.base/sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:46) at java.base/sun.nio.ch.NioSocketImpl.tryRead(NioSocketImpl.java:256) at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:307) at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:346) at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:796) at java.base/java.net.Socket$SocketInputStream.read(Socket.java:1099) at java.base/java.net.Socket$SocketInputStream.read(Socket.java:1093) at network.exception.close.reset.ResetCloseClient.main(ResetCloseClient.java:29) java.net.SocketException: 현재 연결은 사용자의 호스트 시스템의 소프트웨어의 의해 중단되었습니다 at java.base/sun.nio.ch.SocketDispatcher.write0(Native Method) at java.base/sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:54) at java.base/sun.nio.ch.NioSocketImpl.tryWrite(NioSocketImpl.java:394) at java.base/sun.nio.ch.NioSocketImpl.implWrite(NioSocketImpl.java:410) at java.base/sun.nio.ch.NioSocketImpl.write(NioSocketImpl.java:440) at java.base/sun.nio.ch.NioSocketImpl$2.write(NioSocketImpl.java:819) at java.base/java.net.Socket$SocketOutputStream.write(Socket.java:1195) at java.base/java.net.Socket$SocketOutputStream.write(Socket.java:1190) at network.exception.close.reset.ResetCloseClient.main(ResetCloseClient.java:36) Process finished with exit code 0 네트워크 예외4 - 강제종료 중 받은 메시지인데 수업에 말씀하신 Connection reset , pipe broken 이 아닌 "현재 연결은 사용자의 호스트 시스템의 소프트웨어의 의해 중단되었습니다" 에러 메시지가 나옵니다 위 에러메시지에 대해 궁금합니다! (사용자의 호스트 시스템?? 이게 어떤 시스템인지.., 방화벽 문제인지, 보안소프트웨어를 말하는지) 강의 너무 잘듣고 있습니다!! 영환쌤 늘 감사드립니다.
@Service @RequiredArgsConstructor public class MemberService { private final MemberRepository memberRepository; @Transactional //변경 public Long join(Member member) { validateDuplicateMember(member); //중복 회원 검증 memberRepository.save(member); return member.getId(); } } @Repository @RequiredArgsConstructor public class MemberRepository { private final EntityManager em; public void save(Member member) { em.persist(member); } public Member findOne(Long id) { return em.find(Member.class, id); } } --------------------------------------------------------------------------- // 테스트 코드 @Transactional public class MemberServiceTest { @Autowired MemberService memberService; @Autowired MemberRepository memberRepository; @Test public void 회원가입() throws Exception { //Given Member member = new Member(); member.setName("kim"); //When Long saveId = memberService.join(member); //Then assertEquals(member, memberRepository.findOne(saveId)); } } 회원 도메인 개발 파트의 마지막 강의에 있는 테스트 부분입니다 테스트는 성공이고, 테스트 결과 로그에는 member 객체에 대한 select sql이 찍힙니다 그런데 테스트 로그에 왜 select sql이 나오는지 잘 모르겠습니다 제가 이해하고 있는 내용은 이렇습니다 //When Long saveId = memberService.join(member); 위 코드에서 insert sql 이 생성되지만 내부 트랜잭션이라 커밋하지 않아서 해당 엔티티가 영속성 컨텍스트에만 추가된 상태 //Then assertEquals(member, memberRepository.findOne(saveId)); 위 코드에선 영속성 컨텍스트의 member를 가져오니 select sql 이 실행되지 않습니다 //When Long saveId = memberService.join(member); //Then assertEquals(member, memberRepository.findOne(saveId)); 그리고 테스트 코드에서 @Transactional 은 롤백이니 join()의 결과가 롤백됩니다 join() 은 롤백됬으니 테스트 결과 로그에 insert sql 이 나오지 말아야 하고, 영속성 컨텍스트의 member를 조회했으니 select sql 도 나오지 말아야 하는 것으로 예상됩니다 하지만 insert sql 은 예상한대로 테스트 결과에 나오지 않는데, select sql 은 나오는 이유를 모르겠습니다 해결했습니다 validateDuplicateMember(member) 중복 회원 검증하는 메서드에서 select sql을 호출하고 있었습니다 em.persist() 보다 먼저 호출하기 때문에 DB에서 바로 조회해오고 있었습니다 이걸 못보다니..
# Material Tailwind 와 연관된 라이브러리 설치 npm install @emotion/react @emotion/styled @material-tailwind/react @mui/icons-material --save npm install @tailwindcss/typography autoprefixer --save-dev 지난주 공개된 next.js 15에서 Material Tailwind 설정하려니 안되서 도움 부탁드립니다. 아직 초보라 이런 부분들이 개인적으로 좀 어렵네요~~^^;
ARRAY, STRUCT -- 1) array_exercises 테이블에서 각 영화(title)별로 장르(genres)를 UNNEST해서 보여주세요. select title , genre from advanced.array_exercises as ae cross join unnest ( genres ) as genre ; #pivot 연습문제 — 1 ) 첫번째 풀이 order_date / user_1 / user_2 / user_3 --pivot : max(if(조건, true일 때의 값, false일 때의 값)) as new_colum + group by -- max 대신 집계 함수를 사용할 수도 있음 . sum -- false 일 때의 값은 null select order_date , sum ( if ( user_id = 1 , sum_of_amount , null )) as user_1 , sum ( if ( user_id = 2 , sum_of_amount , null )) as user_2 , sum ( if ( user_id = 3 , sum_of_amount , null )) as user_3 from ( select order_date , user_id , sum ( amount ) as sum_of_amount from advanced.orders group by order_date , user_id ) group by order_date order by 1 ; — 2 ) 두번째 풀이 -- 2번 문제 orders 테이블에서 유저(user_id)별로 주문 금액(AMOUNT)의 합계를 pivot 해주세요. -- 날짜(order_date)를 행(row)으로 user_id를 열 (column)으로 만들어야 합니다 -- 컬럼의 이름을 지정할 때 영어 제외하고 backtick('') option + ~ select user_id , max ( if ( order_date = "2023-05-01" , amount , 0 )) as 2023 - 05 - 01 , max ( if ( order_date = "2023-05-02" , amount , 0 )) as 2023 - 05 - 02 , max ( if ( order_date = "2023-05-03" , amount , 0 )) as 2023 - 05 - 03 , max ( if ( order_date = "2023-05-04" , amount , 0 )) as 2023 - 05 - 04 , max ( if ( order_date = "2023-05-05" , amount , 0 )) as 2023 - 05 - 05 from advanced.orders group by 1 order by 1 -- 3번 문제 orders 테이블에서 사용자(user_id)별, 날짜(order_date)별 주문이 있다면 1, -- 없다면 0 으로 pivot 해주세요. user_id를 행(row)으로 order_date를 열 (column)으로 만들어야 합니다 -- 컬럼의 이름을 지정할 때 영어 제외하고 backtick('') option + ~ select user_id , max ( if ( order_date = "2023-05-01" , 1 , 0 )) as 2023 - 05 - 01 , max ( if ( order_date = "2023-05-02" , 1 , 0 )) as 2023 - 05 - 02 , max ( if ( order_date = "2023-05-03" , 1 , 0 )) as 2023 - 05 - 03 , max ( if ( order_date = "2023-05-04" , 1 , 0 )) as 2023 - 05 - 04 , max ( if ( order_date = "2023-05-05" , 1 , 0 )) as 2023 - 05 - 05 from advanced.orders group by 1 order by 1
UNNEST 1) array_exercises 테이블에서 각 영화(title)별로 장르를(genres) unnest 해서 보여주세요 SELECT title, genre FROM advanced.array_exercises CROSS JOIN UNNEST(genres) AS genre 2) array_exercieses 테이블에서 각 영화(title) 별로 배우 (actor)와 배역(character)을 보여주세요. 배우와 배역은 별도의 컬럼으로 나와야 합니다. SELECT title, actor_info.actor, actor_info.character, FROM advanced.array_exercises CROSS JOIN UNNEST(actors) AS actor_info; actors는 하나의 배열이고 그 배열의 각 요소가 STRUCT(구조체) 배열 자체에서 바로 .actor나 .character로 접근할 수 없음 먼저 배열을 UNNEST로 펼친 후에 펼쳐진 각 STRUCT에서 필드값 을 가져와야함 actors는 '서류 묶음'(배열) 각 서류(STRUCT)에는 '배우 이름'과 '캐릭터 이름'이라는 항목이 있음 서류 묶음을 먼저 풀어서(UNNEST) 개별 서류로 만든 다음 각 서류에서 원하는 정보를 읽어야 함 3) array_exercises 테이블에서 각 영화(title) 별로 배우(actor), 배역(character), 장르(genre)를 출력하세요. 한 row 에 배우, 배역, 장르가 모두 표시되어야 합니다. SELECT title, actor_info.actor, actor_info.character, genre, FROM advanced.array_exercises CROSS JOIN UNNEST(actors) AS actor_info CROSS JOIN UNNEST(genres) AS genre; actors 는 배열 인데 구조에 그 밑에 스트링, 스트링 2개 항목이 저장된거고 genres 는 배열인데 스트링 한 계층?의 구조가 있는거라 서로 구조가 달라서 둘이 같이 unnest 불가함 그래서 cross join, unnest 두 번 써줘야함 actors : ARRAY<STRUCT> => UNNEST => STRUCT genres : ARRAY<STRING> => UNNEST => STRING 4) 앱 로그 데이터(app_logs)의 배열을 풀어주세요. SELECT user_id, event_date, event_name, user_pseudo_id, params.key AS key, params.value.string_value AS string_value, params.value.int_value AS int_value, FROM advanced.app_logs CROSS JOIN UNNEST(event_params) AS params ORDER BY event_date; 2022.08.01 부터 나와있길래 그때 날짜부터 정렬인줄 알았더니 where 절에서 2022.08.01 로 필터링 한거였음;; 에러 메시지의 의미: "Expected end of input but got keyword UNNEST" → FROM 절 다음에 UNNEST가 바로 나오면 안 되고, JOIN이나 CROSS JOIN이 먼저 나와야 한다는 뜻 JOIN이나 CROSS JOIN이 필요한 이유: 원본 테이블(app_logs)의 행과 UNNEST로 펼친 배열(event_params)의 요소들을 어떻게 연결할지 명시해야 하기 때문 PIVOT 1) orders 테이블에서 유저(user_id)별로 주문금액(amount)의 합계를 PIVOT 해주세요 날짜(order_date)를 행(Row)으로, user_id를 열(Column)으로 만들어야 합니다. SELECT order_date, SUM(IF(user_id=1, amount, 0)) AS user_1, SUM(IF(user_id=2, amount, 0)) AS user_2, SUM(IF(user_id=3, amount, 0)) AS user_3 FROM advanced.orders GROUP BY order_date ORDER BY order_date ASC 첫번째 풀이는 PIVOT을 하면서 바로 SUM을 한 것 다른 풀이는 집계 함수를 사용해서(SUM) 집계한 후에 PIVOT 컬럼 별칭(AS)에 작은따옴표를 사용함 (AS '2023-05-01') → 백틱 `` 을 사용해야 함. GROUP BY 절에 직접 ASC/DESC를 사용함 → ORDER BY를 따로 사용해야 함 2) orders 테이블에서 날짜(order_date)별로 유저들의 주문금액(amount)의 합계를 PIVOT 해주세요. user_id를 행으로, order_date를 열으로 만들어야 합니다. SELECT user_id, SUM(IF(order_date = '2023-05-01', amount, 0)) AS `2023-05-01`, SUM(IF(order_date = '2023-05-02', amount, 0)) AS `2023-05-02`, SUM(IF(order_date = '2023-05-03', amount, 0)) AS `2023-05-03`, SUM(IF(order_date = '2023-05-04', amount, 0)) AS `2023-05-04`, SUM(IF(order_date = '2023-05-05', amount, 0)) AS `2023-05-05` FROM advanced.orders GROUP BY user_id ORDER BY user_id; 3) orders 테이블에서 사용자(user_id)별, 날짜(order_date)별로 주문이 있다면 1, 없다면 0으로 PIVOT 해주세요. user_id를 행으로, order_date를 열로 만들고 주문을 많이 해도 1로 처리합니다. SELECT user_id, MAX(IF(order_date = "2023-05-01",1,0)) AS `2023-05-01`, MAX(IF(order_date = "2023-05-02",1,0)) AS `2023-05-02`, MAX(IF(order_date = "2023-05-03",1,0)) AS `2023-05-03`, MAX(IF(order_date = "2023-05-04",1,0)) AS `2023-05-04`, MAX(IF(order_date = "2023-05-05",1,0)) AS `2023-05-05` FROM advanced.orders GROUP BY user_id ORDER BY user_id 주문 횟수별로 구해달라고 했을 땐? → MAX 대신 SUM 값이 있으면 1로 넣어달라고 했기 때문에 이걸 SUM 하면 주문 횟수 더한 값이 됨. 4) user_id = 32888 이 카트 추가하기 (click_cart)를 누를 때 어떤 음식(food_id)을 담았나요? WITH base AS ( SELECT event_date, event_name, user_pseudo_id, event_timestamp, user_id, MAX(IF(param.key = "firebase_screen", param.value.string_value, NULL)) AS firebase_screen, MAX(IF(param.key = "food_id", param.value.int_value, NULL)) AS food_id, MAX(IF(param.key = "session_id", param.value.string_value, NULL)) AS session_id FROM advanced.app_logs CROSS JOIN UNNEST(event_params) AS param WHERE event_date = "2022-08-01" GROUP BY ALL ) SELECT select_date, COUNT(user_id) AS user_cnt FROM base WHERE event_name = "click_cart" GROUP BY event_date
ARRAY, STRUCT 연습문제 -- 1. array_exercises 테이블에서 title 별로 genres를 UNNEST하기 SELECT title , genre FROM advanced.array_exercises CROSS JOIN UNNEST(genres) as genre; -- 2. array_exercises 테이블에서 title 별로 actor, character 추출 -- actor, character는 별도의 컬럼으로 빼기 (struct의 key로써 존재하면 안 됨.) SELECT title , ACTORS.actor AS actor , ACTORS.character AS character FROM advanced.array_exercises CROSS JOIN UNNEST(actors) AS ACTORS; -- 3. array_exercises 테이블에서 title 별로 actor, character, genre 추출 -- 여러 ARRAY 컬럼을 UNNEST할 경우, 각 컬럼별로 UNNEST한 것을 CROSS JOIN 진행하면 됨. SELECT title , ACTORS.actor AS actor , ACTORS.character AS character , genre FROM advanced.array_exercises CROSS JOIN UNNEST(actors) AS ACTORS CROSS JOIN UNNEST(genres) as genre; -- 4. app_logs 테이블(약 73만 건의 로그 데이터)의 ARRAY를 UNNEST 하기 -- event_params 형태? -- ARRAY<STRUCT<key STRING, value STRUCT<string_value STRING, int_value INT64>>>[ -- STRUCT('firebase_screen', STRUCT('food_detail', NULL)) -- , ... -- ] SELECT user_id , event_date , event_name , user_pseudo_id , EVENT_PARAMS.key AS key , EVENT_PARAMS.value.string_value AS string_value , EVENT_PARAMS.value.int_value AS int_value FROM advanced.app_logs CROSS JOIN UNNEST(event_params) AS EVENT_PARAMS PIVOT 연습문제 -- 1. 날짜별 유저별 주문금액 합계 -- 첫번째 그룹 axis는 GROUP BY 대상 컬럼임. (여기서는 '날짜별'에 해당함.) -- 두번째 그룹 axis는 IF 혹은 CASE WHEN을 이용하여 각 컬럼으로 만들어야 함. (여기서는 '유저별'에 해당함.) -- 세번째 그룹 axis는 두번째 그룹 axis를 다루는 과정에서 '값'으로 들어가야 함. (여기서는 '주문금액'에 해당함.) SELECT order_date, SUM(IF(user_id=1,amount,0)) AS user_1, SUM(IF(user_id=2,amount,0)) AS user_2, SUM(IF(user_id=3,amount,0)) AS user_3 FROM `advanced.orders` WHERE 1=1 AND user_id IN (1,2,3) GROUP BY order_date ORDER BY order_date ASC; -- 2. 유저별 날짜별 주문금액 합계 -- 문자열을 결과 컬럼으로 넣기 위해선 백틱(`)으로 감싸기!! SELECT user_id, SUM(IF(order_date='2023-05-01',amount,0)) AS `2023-05-01`, SUM(IF(order_date='2023-05-02',amount,0)) AS `2023-05-02`, SUM(IF(order_date='2023-05-03',amount,0)) AS `2023-05-03`, SUM(IF(order_date='2023-05-04',amount,0)) AS `2023-05-04`, SUM(IF(order_date='2023-05-05',amount,0)) AS `2023-05-05`, FROM `advanced.orders` WHERE 1=1 AND user_id IN (1,2,3) AND order_date IN ('2023-05-01','2023-05-02','2023-05-03','2023-05-04','2023-05-05') GROUP BY user_id ORDER BY user_id ASC; -- 3. 유저별 날짜별 주문내역 존재여부 -- 주문 존재하면 1, 없으면 0; 주문횟수가 아님에 유의 SELECT user_id, MAX(IF(order_date='2023-05-01',1,0)) AS `2023-05-01`, MAX(IF(order_date='2023-05-02',1,0)) AS `2023-05-02`, MAX(IF(order_date='2023-05-03',1,0)) AS `2023-05-03`, MAX(IF(order_date='2023-05-04',1,0)) AS `2023-05-04`, MAX(IF(order_date='2023-05-05',1,0)) AS `2023-05-05` FROM `advanced.orders` WHERE 1=1 AND user_id IN (1,2,3) AND order_date IN ('2023-05-01','2023-05-02','2023-05-03','2023-05-04','2023-05-05') GROUP BY user_id ORDER BY user_id ASC; -- 4. app_logs 테이블 PIVOT 하기 -- user_id=32888이 카트 추가하기(click_cart)를 누를 때 어떤 음식(food_id)를 담았나요? -- 일반화된 문제 정의: 유저별 이벤트별 이벤트 파라미터 key-value 보기 -- 특정 유저가 특정 action을 취했을 때 (event가 발생했을 때) 앱 내부적으로 어떤 정보가 오갔는지 보고싶을 때 -- 방법 1) 쪼개서 생각하기 WITH base AS ( -- step 1) app_logs 테이블은 ARRAY 컬럼이 존재하는 테이블: UNNEST 하기 → UNNEST한 테이블을 임시테이블화 SELECT event_date, event_timestamp, event_name, params.key AS key_, params.value.string_value AS string_value_, params.value.int_value AS int_value_, user_id, user_pseudo_id, platform FROM advanced.app_logs CROSS JOIN UNNEST(event_params) AS params WHERE 1=1 -- AND user_id=32888 -- AND event_name='click_cart' AND event_date >= '2022-08-01' AND event_date <= '2022-08-31' ) -- step 2) event_params에 대해 PIVOT 진행 SELECT user_id, event_date, event_name, event_timestamp, user_pseudo_id, MAX(IF(key_='food_id',int_value_,NULL)) AS `food_id`, MAX(IF(key_='session_id',string_value_,NULL)) AS `session_id`, MAX(IF(key_='firebase_screen',string_value_,NULL)) AS `firebase_screen`, FROM base GROUP BY user_id, event_date, event_name, event_timestamp, user_pseudo_id; -- 방법 2) UNNEST + PIVOT을 한 쿼리에 WITH base AS ( SELECT user_id, event_date, event_name, event_timestamp, user_pseudo_id, MAX(IF(params.key='food_id',params.value.int_value,NULL)) AS `food_id`, MAX(IF(params.key='session_id',params.value.string_value,NULL)) AS `session_id`, MAX(IF(params.key='firebase_screen',params.value.string_value,NULL)) AS `firebase_screen`, FROM advanced.app_logs CROSS JOIN UNNEST(event_params) AS params GROUP BY ALL ) -- user_id=32888이 카트 추가하기(click_cart)를 누를 때 어떤 음식(food_id)을 담았나요? SELECT * FROM base WHERE 1=1 AND user_id=32888 AND event_name='click_cart'; 퍼널 쿼리 연습문제 WITH main AS ( SELECT event_date, CONCAT(event_name,'-', event_param.value.string_value) AS event_name_with_screen, CASE WHEN event_name = 'screen_view' AND event_param.value.string_value = 'welcome' THEN 1 WHEN event_name = 'screen_view' AND event_param.value.string_value = 'home' THEN 2 WHEN event_name = 'screen_view' AND event_param.value.string_value = 'food_category' THEN 3 WHEN event_name = 'screen_view' AND event_param.value.string_value = 'restaurant' THEN 4 WHEN event_name = 'screen_view' AND event_param.value.string_value = 'cart' THEN 5 WHEN event_name = 'click_payment' AND event_param.value.string_value = 'cart' THEN 6 END AS step_num, COUNT(DISTINCT user_pseudo_id) AS cnt FROM advanced.app_logs CROSS JOIN UNNEST(event_params) AS event_param WHERE 1=1 AND event_date BETWEEN '2022-08-01' AND '2022-08-18' AND event_param.key = 'firebase_screen' AND event_name IN ("screen_view",'click_payment') GROUP BY 1,2,3 HAVING step_num IS NOT NULL ) SELECT event_date, SUM(IF(step_num = 1, cnt, 0)) AS `screen_view-welcome`, SUM(IF(step_num = 2, cnt, 0)) AS `screen_view-home`, SUM(IF(step_num = 3, cnt, 0)) AS `screen_view-food_category`, SUM(IF(step_num = 4, cnt, 0)) AS `screen_view-restaurant`, SUM(IF(step_num = 5, cnt, 0)) AS `screen_view-cart`, SUM(IF(step_num = 6, cnt, 0)) AS `click_payment-cart` FROM main GROUP BY 1 ORDER BY event_date ASC
List<Integer> list1 = new ArrayList<>(); List<Integer> list2 = new ArrayList<Integer>(); 객체의 타입에는 들어갈 데이터의 타입을 명시해 주어야 하지만 인스턴스를 생성할 때는 타입추론으로 인해 다이아몬드에 타입을 입력하지 않아도 되는 부분에서 궁금증이 생겼습니다. 타입추론은 인스턴스 생성 시 개발자의 편의를 위해 몇 자 줄여주는 정도인가요? 아니면 다른 부가적인 장점이 있는지 궁금합니다. 컴파일러가 타입을 추론하기 위한 시간이 추가적으로 소요될 것 같은데(고려할 정도는 아니겠지만...) 오히려 타입추론으로 최적화 적인 방면으로 이점도 있을까요?..
ArrayList는 배열을 기반으로 만든 List이며 LinkedList는 객체의 참조로 연결된 List로 이해했습니다. 요즘에는 메모리 용량이 많이 크기 때문에 고려하지 않아도 된다고 생각하는데 궁금증이 있어 질문을 남깁니다. LinkedList는 데이터가 추가 될 때 마다 새로운 Node를 만들어서 현재 가지고 있는 데이터의 크기만큼 메모리를 차지하는 반면 ArrayList는 들어있는 데이터 크기가 일정 크기를 넘는 경우 더 많은 데이터를 저장할 수 있는 새로운 Array를 생성하여 반환하기 때문에 동일한 개수의 데이터를 저장해도 ArrayList가 메모리 상에서 차지하는 크기가 크다고 생각하면 되나요?
package com.jinyoung.myapplication import android.view.View import android.view.ViewGroup import android.widget.BaseAdapter class ListViewAdapter(var List : MutableList<String>) : BaseAdapter() { override fun getCount(): Int { return List.size } override fun getItem(p0: Int): Any { return List[p0] } override fun getItemId(p0: Int): Long { TODO("Not yet implemented") } override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View { TODO("Not yet implemented") } } 이 코드에서 3번째 함수 getItemId 함수의 return 값이 List[p0].toLong()이 되어야하는거 아닌가요????
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 안녕하세요, 2강 코틀린에서 null을 다루는 방법 수강중 궁금한 지점이 생겨 문의드립니다. name이라는 필드에 NotNull 어노테이션을 달아주어서 인스턴스 초기화시 null값은 받을 수 없게 하고싶었는데이런 경우에는 null을 대입해도 에디터상에서 에러를 짚어주지 못하더라구요.회사 프로젝트를 전환하는 과정중에는 이런 문제가 꽤나 발생할 것 같은데 좋은 해결책이 있을까요? package com.lannstark.lec02; import org.jetbrains.annotations.NotNull; public class Person { @NotNull private final String name; public Person(String name) { this.name = name; } public String getName() { return name; } } package com.lannstark.lec02 fun main() { val person = Person(null) startsWithA(person.name) }
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (아니오) [질문 내용] 여기에 질문 내용을 남겨주세요. 컬렉션 프레임워크9에서 문제와풀이2의 문제6번에 대해 여쭤봅니다. memberMap이라는 해시맵에서 첫번째 매개변수로 member.getId 가 들어가고 두번째 매개변수로 member클래스가 들어가는데 첫번째 매개변수가 member클래스의 멤버변수이어야 하는 이유가 있을까요? 문제를 풀던 중에 아직은 잘 모르겠지만 member클래스의 멤버변수 두개를 memberMap이라는 해시맵에 두개를 지정해도 될거같기도 한데(제 극히 초보인 관점에서 본 추측일 뿐입니다.그렇게 풀어볼 능력까진 없어서요.) 굳이 두번째에 클래스를 넣으시고 첫번째에 클래스의 멤버변수 하나를 넣으신 이유가 궁금합니다. 제 짧은 시각으로 보면 중복 같아 보여서요 질문6번의 의도가 파악이 안 돼서 궁금하여 여쭤봅니다.
SELECT [1, 2, 3, 4, 5] AS some_numbers ; SELECT ARRAY<INT64>[1, 2, 3, 4, 5] AS some_numbers ; SELECT GENERATE_ARRAY(1, 5, 1) AS some_numbers ; SELECT [SAFE_OFFSET()] ; SELECT (1, 2, 3) AS struct_test ; SELECT STRUCT<hi INT64, hello INT64>(1, 2) AS struct_test ; SELECT a.title, b AS genre FROM workspace.array_exercises AS a JOIN UNNEST(genres) AS b ; SELECT a.title, b.actor, b.character FROM workspace.array_exercises AS a JOIN UNNEST(actors) AS b ; SELECT a.title, b.actor, b.character, c AS genre FROM workspace.array_exercises AS a JOIN UNNEST(actors) AS b JOIN UNNEST(genres) AS c ; SELECT a.user_id, a.event_date, a.event_name, a.user_pseudo_id, b.key, b.value.string_value, b.value.int_value FROM workspace.app_logs AS a JOIN UNNEST(event_params) AS b ; SELECT key, string_value, count(distinct user_pseudo_id) FROM ( SELECT a.user_id, a.event_date, a.event_name, a.user_pseudo_id, b.key, b.value.string_value, b.value.int_value FROM workspace.app_logs AS a JOIN UNNEST(event_params) AS b ) WHERE event_name = 'screen_view' GROUP BY ALL ; SELECT order_date, sum(if(user_id = 1, amount, 0)) as user_1, sum(if(user_id = 2, amount, 0)) as user_2, sum(if(user_id = 3, amount, 0)) as user_3 FROM workspace.orders GROUP BY ALL ORDER BY order_date ; SELECT user_id, sum(if(order_date = '2023-05-01', amount, 0)) `2023-05-01`, sum(if(order_date = '2023-05-02', amount, 0)) `2023-05-02`, sum(if(order_date = '2023-05-03', amount, 0)) `2023-05-03`, sum(if(order_date = '2023-05-04', amount, 0)) `2023-05-04`, sum(if(order_date = '2023-05-05', amount, 0)) `2023-05-05` FROM workspace.orders GROUP BY ALL ORDER BY user_id ; SELECT user_id, if(sum(if(order_date = '2023-05-01', amount, 0)) > 0, 1, 0) `2023-05-01`, if(sum(if(order_date = '2023-05-02', amount, 0)) > 0, 1, 0) `2023-05-02`, if(sum(if(order_date = '2023-05-03', amount, 0)) > 0, 1, 0) `2023-05-03`, if(sum(if(order_date = '2023-05-04', amount, 0)) > 0, 1, 0) `2023-05-04`, if(sum(if(order_date = '2023-05-05', amount, 0)) > 0, 1, 0) `2023-05-05` FROM workspace.orders GROUP BY ALL ORDER BY user_id ; WITH events AS ( SELECT event_date, event_timestamp, event_name, user_id, user_pseudo_id, platform, MAX(IF(b.key = "firebase_screen", b.value.string_value, NULL)) AS firebase_screen, MAX(IF(b.key = "session_id", b.value.string_value, NULL)) AS session_id FROM workspace.app_logs AS a JOIN UNNEST(event_params) AS b WHERE event_date >= '2022-08-01' AND event_date < '2022-08-19' GROUP BY ALL ), filter_event_and_concat_event_and_screen AS ( SELECT * EXCEPT(event_name, firebase_screen, event_timestamp), CONCAT(event_name, "-", firebase_screen) AS event_name_with_screen, DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul') AS event_datetime FROM events WHERE event_name IN ('screen_view', 'click_payment') ) SELECT event_date, event_name_with_screen, CASE WHEN event_name_with_screen = 'screen_view-welcome' THEN 1 WHEN event_name_with_screen = 'screen_view-home' THEN 2 WHEN event_name_with_screen = 'screen_view-food_category' THEN 3 WHEN event_name_with_screen = 'screen_view-restaurant' THEN 4 WHEN event_name_with_screen = 'screen_view-cart' THEN 5 WHEN event_name_with_screen = 'click_payment-cart' THEN 6 END AS step_number, COUNT(DISTINCT user_pseudo_id) AS cnt FROM filter_event_and_concat_event_and_screen GROUP BY ALL HAVING step_number IS NOT NULL ;