학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] Wrong user name or password 에러가 뜨는데 어떻게 해야되나요? org.h2.jdbc.JdbcSQLInvalidAuthorizationSpecException: Wrong user name or password [28000-224] 이 오류가 떠요
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 이렇게 해서 접속을 했는데 거부합니다. 홈화면에 test.mv .db까지 완성한 단계입니다.
선생님 안녕하세요 쿼리로 누적 백분위 구하려고하는데 질문드립니다 A 10 B 20 C 30 D 40 E 50 Sum: 150 이렇게 있을때 상위 10%, 20%, 30% .. 등등에 속하는 개수는 몇개인지 구하려하는데요 예를 들어 150의 10%인 15를 만족하는 갯수는 전체 중 1개( A = 10) 20%인 30 을 만족하는 갯수는 전체 중 2개 (A와 B) 전체 중 100%는 5개 (A, B, C, D, E) 이런식으로 누적 백분위를 구하고싶은데 이렇게도 가능할까요? 그럼 A+B 값이 10% 에 드는지 20%에 드는지 A+B+C 값은 몇프로에 드는지 각각 case when을 써서 비교해보려고했는데 누적 합산을 A+B, A+B+C 이렇게 끊어서 비교하는 방법을 모르겠습니다
Weekly Retention은 diff_of_week 을 활용하여, 시간의 경과에 따른 리텐션 변화를 구합니다. 따라서, 제가 생각했을 때, Weekly Retention을 구하는 쿼리에서는 다음과 같은 가정을 하고 진행한 것이 아닐까? 라는 생각이 들었습니다. 주차별로(시간에 따라) 활동 중인 사용자 수는 달라질 것이다. user_type에 따라 활동 중인 사용자 수는 차이가 있을 것이다. 2.의 경우는 만약의 신규/복귀... 유저를 구분한다면, 해당 가설을 기반으로, Weekly Retention을 구하는 행위를 한 것이 아닐까? 라는 생각이 들었습니다. (추가 궁금증) Retention에 영향을 주는 인자를 분석하는 경우도 있을까요? (실무에서) 저는 Retention을 분석하기 전에, Retention과 관련이 높은 것이 무엇일지, 가설을 세우고 검정을 해보았습니다. 가설 : 방문일수는 Retention에 높은 상관관계를 가진다. 데이터 범위: 2022-08-01 ~ 2022-11-01 D7_retention : (bool) 사용자의 첫번째 이벤트 시점 ~ 7일 이후에도 활동을 하면, 1 아니면 0 D30_retention : (bool) 사용자의 첫번째 이벤트 시점 ~ 30일 이후에도 활동을 하면, 1아니면 0 import os from google.cloud import bigquery from google.oauth2 import service_account import pandas as pd import statsmodels.api as sm from scipy.stats import pointbiserialr import numpy as np os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = './service_account.json' key_path = './service_account.json' credentials = service_account.Credentials.from_service_account_file( key_path, scopes = ["https://www.googleapis.com/auth/cloud-platform"], ) client = bigquery.Client(credentials=credentials, project=credentials.project_id, location="US") query = """ WITH user_visits AS ( SELECT user_pseudo_id, COUNT(DISTINCT event_date) AS visit_days FROM advanced.app_logs WHERE event_date BETWEEN '2022-08-01' AND '2022-11-01' GROUP BY user_pseudo_id ), retention_data AS ( SELECT user_pseudo_id, MIN(event_date) AS first_event_date, MAX(event_date) AS last_event_date, CASE WHEN MAX(event_date) >= DATE_ADD(MIN(event_date), INTERVAL 7 DAY) THEN 1 ELSE 0 END AS D7_retention, CASE WHEN MAX(event_date) >= DATE_ADD(MIN(event_date), INTERVAL 30 DAY) THEN 1 ELSE 0 END AS D30_retention FROM advanced.app_logs WHERE event_date BETWEEN '2022-08-01' AND '2022-11-01' GROUP BY user_pseudo_id ), combined_data AS ( SELECT v.user_pseudo_id, v.visit_days, r.D7_retention, r.D30_retention FROM user_visits v JOIN retention_data r ON v.user_pseudo_id = r.user_pseudo_id ) SELECT * FROM combined_data; """ df = client.query(query).to_dataframe() df['visit_days'] = pd.to_numeric(df['visit_days'], errors='coerce').astype(np.float64) df['D7_retention'] = pd.to_numeric(df['D7_retention'], errors='coerce').astype(np.float64) df['D30_retention'] = pd.to_numeric(df['D30_retention'], errors='coerce').astype(np.float64) # 결측치가 있는지 확인하고 제거 df = df.dropna(subset=['visit_days', 'D7_retention', 'D30_retention']) # 상수항 추가 X = sm.add_constant(df[['visit_days']]) # D7_retention에 대한 로지스틱 회귀 모델 적합 y_D7 = df['D7_retention'] logit_model_D7 = sm.Logit(y_D7, X).fit() print(logit_model_D7.summary()) # D30_retention에 대한 로지스틱 회귀 모델 적합 y_D30 = df['D30_retention'] logit_model_D30 = sm.Logit(y_D30, X).fit() print(logit_model_D30.summary()) visit_days_range = np.linspace(df['visit_days'].min(), df['visit_days'].max(), 100) prob_D7 = logit_model_D7.predict(sm.add_constant(visit_days_range)) prob_D30 = logit_model_D30.predict(sm.add_constant(visit_days_range)) plt.plot(visit_days_range, prob_D7, label='D7 Retention Probability') plt.plot(visit_days_range, prob_D30, label='D30 Retention Probability', linestyle='--') plt.xlabel('Visit Days') plt.ylabel('Retention Probability') plt.title('Retention Probability vs Visit Days') plt.legend() plt.show() 따라서, 방문일수는 Retention과 상관성을 보인다. 가설2. 방문일 수는 user_type에 따라 각기 다른 상관성을 보일 것이다. 결론: user_type은 new_user, current_user는 통계적으로 유의하며, 높은 상관성을 가지나, 휴면 유저, 복귀 유저는 통계적으로 유의미하지 않으며, 낮은 상관성을 띈다. 이렇게 결론을 내놓는 방식이, 적합한 방식인지 궁금합니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 빌드하고 실행하기 부분에서 명령프롬프터를 사용하신것 같은데 윈도우 파워쉘로 사용해도 되나요??
자바 오버라이딩 규칙 중에서 부모보다 같거나 적은 개수의 예외를 던져야 한다고 알고 있습니다. 그런데, Runnable 구현 클래스의 run()에 'throws RuntimeException'을 선언하면 컴파일 오류가 발생하지 않는데 왜 그런건가요? 부모보다 많은 개수의 예외를 던졌으니 컴파일 오류가 발생해야 하는 것 아닌가요..? 제가 무엇을 잘못 알고 있는 걸까요?
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 회원을 등록하고 조회하면 1 ~ 2 ~ 이렇게 번호가 매겨지는 게 어떻게 되는 건가요??
안녕하세요 카일스쿨님! 총 정리 문제 2번의 쿼리 결과를 보고 문뜩 궁금증이 생겨 질문 남깁니다. 이렇게 쿼리를 짜서 카일스쿨님이 문제 풀이해주신 것처럼 같은 결과가 나왔습니다. 다만, 현재 GROUP BY가 타입1과 한글 이름 두 가지로 기준을 잡고 있어서 저는 타입 1을 기준으로 가장 많은 포켓몬 수가 나올지, 아니면 한글이름 기준으로 가장 많은 포켓몬 수가 나올지 궁금해 했었는데, 한글 이름 기준으로 가장 많은 포켓몬 수가 나오더라구요! 이러한 경우에는 왜 타입1과 한글 이름 두 가지가 다 기준으로 잡혀있는데, 타입1 기준이 아니라 한글 이름 기준으로 가장 많은 포켓몬 수가 출력되는 걸까요? 참고로 타입 1 기준으로 가장 많은 포켓몬 수를 구해봤더니 1순위는 Water로 나왔습니다.
카일님, 안녕하세요! LEFT JOIN 기준이 헷갈려 이에 대한 질문 드립니다. LEFT JOIN 시 테이블의 데이터가 더 많은 테이블을 제일 왼쪽으로 두면 된다고 이해하면 될까요? 또한, 예외도 있다고 하셨는데, 어떤 경우인지도 알 수 있을까요? 마지막으로 LEFT말고 다른 JOIN할 때도 테이블의 데이터가 더 많은 테이블을 기준으로 잡아도 괜찮나요? 감사합니다!
MVC와 템플릿 엔진 강의 06:51 실행하여도 INFO문만 찍히고 강의와 달리 WARN은 발생하지 않습니다. 콘솔에 찍힌 값은 다음과 같습니다. 2024-07-30T10:30:55.095+09:00 INFO 33580 --- [hello-spring] [ main] h.hello_spring.HelloSpringApplication : Starting HelloSpringApplication using Java 17.0.11 with PID 33580 (C:\Users\user\IdeaProjects\hello-spring\build\classes\java\main started by user in C:\Users\user\IdeaProjects\hello-spring) 2024-07-30T10:30:55.115+09:00 INFO 33580 --- [hello-spring] [ main] h.hello_spring.HelloSpringApplication : No active profile set, falling back to 1 default profile: "default" 2024-07-30T10:30:57.606+09:00 INFO 33580 --- [hello-spring] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http) 2024-07-30T10:30:57.631+09:00 INFO 33580 --- [hello-spring] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2024-07-30T10:30:57.631+09:00 INFO 33580 --- [hello-spring] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.26] 2024-07-30T10:30:57.775+09:00 INFO 33580 --- [hello-spring] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2024-07-30T10:30:57.776+09:00 INFO 33580 --- [hello-spring] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2539 ms 2024-07-30T10:30:58.049+09:00 INFO 33580 --- [hello-spring] [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html] 2024-07-30T10:30:58.643+09:00 INFO 33580 --- [hello-spring] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/' 2024-07-30T10:30:58.663+09:00 INFO 33580 --- [hello-spring] [ main] h.hello_spring.HelloSpringApplication : Started HelloSpringApplication in 4.99 seconds (process running for 7.892) 2024-07-30T10:31:29.971+09:00 INFO 33580 --- [hello-spring] [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2024-07-30T10:31:29.972+09:00 INFO 33580 --- [hello-spring] [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2024-07-30T10:31:29.975+09:00 INFO 33580 --- [hello-spring] [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms 제 코드는 다음과 같습니다. package hello.hello_spring.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @Controller public class HelloController { @GetMapping("hello") public String hello(Model model) { model.addAttribute("data", "hello!!"); return "hello"; } @GetMapping("hell-mvc") public String hellmvc(@RequestParam("name") String name, Model model) { model.addAttribute("name", name); return "hello-template"; } }
pdf파일 158페이지, 159페이지에 있는 where과 having 쿼리 예시문을 보다가 궁금한 게 생겨서요. where 쿼리문 : select 컬럼 1, 컬럼2라고 돼 있는데 group by 컬럼1, 컬럼2 라고 하지 않아도 실행이 가능한가요? having 쿼리문 : group by하는(그룹화 기준) 컬럼과 count 컬럼이 같을 수도 있나요? count (컬럼3)이어야 하지 않는지 궁금합니다!
static void solution(Queue<Point> q, int[][] box) { while (!q.isEmpty()) { // int len = q.size(); // for (int i = 0; i < len; i++) { Point tmp = q.poll(); for (int j = 0; j < 4; j++) { int nx = tmp.x + dx[j]; int ny = tmp.y + dy[j]; if (nx >= 0 && ny >= 0 && nx < n && ny < m && box[nx][ny] == 0) { box[nx][ny] = 1; q.offer(new Point(nx, ny)); dis[nx][ny] = dis[tmp.x][tmp.y] + 1; } // } } } } 위에서 주석처리된 queue의 사이즈를 구한 후 레벨 탐색(?)으로 진행하게 되면 타임에러가 발생하는데 왜 발생하는지 모르겠습니다. 성능 차이가 많이 나나요 ?
회원가입에서 회원가입 등록후 회원 목록에서 조회가 안됩니다. 콘솔에 찍어보니 회원 등록은 정상적으로 잘 되는 것 같은데 목록 조회가 안됩니다. 파일 첨부하겠습니다! 감사합니다 :) https://drive.google.com/file/d/1W7jN-wppJGC_hbWtQoa4PBvN0LRa41X9/view?usp=sharing
궁금한 점 static 메서드는 일반메서드처럼 메서드 영역에 위치하나요? 1-1. 맞다면, static 키워드의 단점으로 GC에 대상이 되지 않아서 자주 사용한다면 메모리 효율이 떨어진다고 알고있습니다. 그런데 static 메서드나 일반메서드나 GC 대상이 되지 않는 메모리 영역에 있는거 아닌가요? 아니라면 어디에 위치하는지 궁금합니다. 제가 궁금하게 된 계기가 유틸 클래스를 주로 @Bean이 아닌 staitc 메서드를 모아둔 형태로 사용하는 이유가 궁금해서 찾아보다가 다른 영한님 서포터즈 분이 답변하신 글을 보았습니다. https://www.inflearn.com/community/questions/472317/%EC%98%81%ED%95%9C%EB%8B%98-%EC%A7%88%EB%AC%B8%EB%93%9C%EB%A6%BD%EB%8B%88%EB%8B%A4 " 사용빈도가 너무 희소하다면, static으로 관리하기 보단 필요할떄마다 new 로 생성해서 쓰는게 더 효율적일 수도 있습니다. GC의 효율이 예전처럼 나쁘지 않습니다. 자주 사용하지 않는 객체를 메모리에 상주시키것이 더 낭비일 수 있습니다." 위 답변으로 미루어 보아 단순히 static 메서드가 메모리에 올라가는게 아니라 힙 영역이 아닌 static 메서드를 가지고 있는 객체 형태(?)로 다른 메모리에 올라간다고 이해해서 헷갈립니다.
최근에, 면접이 있어서 공부를 제대로 못했네요. 오늘부터 매일 매일 못했던 공부를 다시 해보려고 합니다. 문제: core_event를 'click_payment'라고 설정하고, Weekly Retention을 구하여라. 저는 click_payment 까지의 사용자 여정 별로 세그먼트를 나누려는 시도를 해보았습니다. 문제에 대한 고민사항은 길어지니, 최종적으로 나누었던 세그먼트를 먼저 소개해드리겠습니다. click_payment 까지의 여정은 크게 다음과 같이 구성할 수 있습니다. click_search(검색) -> click_payment click_banner(배너 클릭) -> click_payment click_food_category(음식 카테고리 클릭) -> click_payment click_restaurant_nearby( 내 위치 기반 주변 레스토랑) -> click_payment click_recommend_food(추천) -> click_payment 크게 다음과 같이 5개의 세그먼트로 나누어보고, 각 세그먼트 별로, Count를 해보았습니다. 이 때, click_login -> click_search -> .... -> click_payment -> click_search .... 이런 경우를 대비해서, 각 이벤트마다 제일 처음 이벤트가 발생한 시간 을 기준으로 구분하도록 하였습니다. 이제 각 퍼널별로 리텐션을 구해보겠습니다. WITH user_events AS ( SELECT user_pseudo_id, event_name, event_date, DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul') AS event_timestamp, ROW_NUMBER() OVER (PARTITION BY user_pseudo_id, event_name ORDER BY event_timestamp) AS r_num FROM advanced.app_logs WHERE event_name IN ('click_search', 'click_banner', 'click_food_category', 'click_restaurant_nearby', 'click_recommend_food', 'click_payment') AND event_name NOT IN ('screen_view', 'click_login') ), FIRST_EVENTS AS ( SELECT *, DATE_DIFF(event_week, first_week, WEEK) AS diff_of_week FROM ( SELECT user_pseudo_id, event_date, DATE_TRUNC(MIN(event_date) OVER (PARTITION BY user_pseudo_id), WEEK(MONDAY)) AS first_week, DATE_TRUNC(event_date, WEEK(MONDAY)) AS event_week, MIN(CASE WHEN event_name = 'click_search' THEN event_timestamp END) OVER (PARTITION BY user_pseudo_id, event_date) AS first_search_time, MIN(CASE WHEN event_name = 'click_payment' THEN event_timestamp END) OVER (PARTITION BY user_pseudo_id, event_date) AS first_payment_time, MIN(CASE WHEN event_name = 'click_banner' THEN event_timestamp END) OVER (PARTITION BY user_pseudo_id, event_date) AS first_banner_time, MIN(CASE WHEN event_name = 'click_food_category' THEN event_timestamp END) OVER (PARTITION BY user_pseudo_id, event_date) AS first_food_category_time, MIN(CASE WHEN event_name = 'click_restaurant_nearby' THEN event_timestamp END) OVER (PARTITION BY user_pseudo_id, event_date) AS first_restaurant_nearby_time, MIN(CASE WHEN event_name = 'click_recommend_food' THEN event_timestamp END) OVER (PARTITION BY user_pseudo_id, event_date) AS first_recommend_food_time FROM user_events ) ), FUNNEL_CLASSIFICATION AS ( SELECT DISTINCT *, CASE WHEN first_search_time IS NOT NULL AND first_payment_time IS NOT NULL AND first_search_time < first_payment_time THEN 'click_search -> click_payment' WHEN first_banner_time IS NOT NULL AND first_payment_time IS NOT NULL AND first_banner_time < first_payment_time THEN 'click_banner -> click_payment' WHEN first_food_category_time IS NOT NULL AND first_payment_time IS NOT NULL AND first_food_category_time < first_payment_time THEN 'click_food_category -> click_payment' WHEN first_restaurant_nearby_time IS NOT NULL AND first_payment_time IS NOT NULL AND first_restaurant_nearby_time < first_payment_time THEN 'click_restaurant_nearby -> click_payment' WHEN first_recommend_food_time IS NOT NULL AND first_payment_time IS NOT NULL AND first_recommend_food_time < first_payment_time THEN 'click_recommend_food -> click_payment' ELSE 'Other' END AS funnel FROM FIRST_EVENTS WHERE first_payment_time IS NOT NULL ), PIVOTED_ANALYSIS AS ( SELECT diff_of_week, COUNT(DISTINCT CASE WHEN funnel = 'click_search -> click_payment' THEN user_pseudo_id END) AS user_cnt_search_payment, COUNT(DISTINCT CASE WHEN funnel = 'click_banner -> click_payment' THEN user_pseudo_id END) AS user_cnt_banner_payment, COUNT(DISTINCT CASE WHEN funnel = 'click_food_category -> click_payment' THEN user_pseudo_id END) AS user_cnt_food_category_payment, COUNT(DISTINCT CASE WHEN funnel = 'click_restaurant_nearby -> click_payment' THEN user_pseudo_id END) AS user_cnt_restaurant_nearby_payment, COUNT(DISTINCT CASE WHEN funnel = 'click_recommend_food -> click_payment' THEN user_pseudo_id END) AS user_cnt_recommend_food_payment FROM FUNNEL_CLASSIFICATION GROUP BY diff_of_week ), INITIAL_USERS AS ( SELECT FIRST_VALUE(user_cnt_search_payment) OVER (ORDER BY diff_of_week) AS first_user_cnt_search_payment, FIRST_VALUE(user_cnt_banner_payment) OVER (ORDER BY diff_of_week) AS first_user_cnt_banner_payment, FIRST_VALUE(user_cnt_food_category_payment) OVER (ORDER BY diff_of_week) AS first_user_cnt_food_category_payment, FIRST_VALUE(user_cnt_restaurant_nearby_payment) OVER (ORDER BY diff_of_week) AS first_user_cnt_restaurant_nearby_payment, FIRST_VALUE(user_cnt_recommend_food_payment) OVER (ORDER BY diff_of_week) AS first_user_cnt_recommend_food_payment FROM PIVOTED_ANALYSIS LIMIT 1 ) SELECT pa.diff_of_week, pa.user_cnt_search_payment AS search_active_user, pa.user_cnt_banner_payment AS banner_active_user, pa.user_cnt_food_category_payment AS category_active_user, pa.user_cnt_restaurant_nearby_payment AS nearby_active_user, pa.user_cnt_recommend_food_payment AS recommend_active_user, iu.first_user_cnt_search_payment AS search_cohort_user, iu.first_user_cnt_banner_payment AS banner_cohort_user, iu.first_user_cnt_food_category_payment AS category_cohort_user, iu.first_user_cnt_restaurant_nearby_payment AS nearby_cohort_user, iu.first_user_cnt_recommend_food_payment AS recommend_cohort_user, ROUND(SAFE_DIVIDE(pa.user_cnt_search_payment, iu.first_user_cnt_search_payment), 3) AS retention_week_rate_search_payment, ROUND(SAFE_DIVIDE(pa.user_cnt_banner_payment, iu.first_user_cnt_banner_payment), 3) AS retention_week_rate_banner_payment, ROUND(SAFE_DIVIDE(pa.user_cnt_food_category_payment, iu.first_user_cnt_food_category_payment), 3) AS retention_week_rate_food_category_payment, ROUND(SAFE_DIVIDE(pa.user_cnt_restaurant_nearby_payment, iu.first_user_cnt_restaurant_nearby_payment), 3) AS retention_week_rate_restaurant_nearby_payment, ROUND(SAFE_DIVIDE(pa.user_cnt_recommend_food_payment, iu.first_user_cnt_recommend_food_payment), 3) AS retention_week_rate_recommend_food_payment FROM PIVOTED_ANALYSIS pa, INITIAL_USERS iu ORDER BY pa.diff_of_week; 1000자 이내로 작성해야 게시글 하나를 쓸 수 있음.. screen_view -> screen_view -> click_login의 경우, 사용자가 로그인을 하지 않고, 앱이 잠시 백그라운드 상에 동작중인 상태에서, 다시 앱을 켰을 때, screen_view 로그가 찍히는 것을 확인했습니다. 현재 집중해야 할 부분은 사용자가 상품을 들여다보는 시간이나, item을 찾을 때, UI/UX 적으로 개선할 부분이 있는지 찾기보다, click_payment를 하기까지의 주요 이벤트 여정을 세그먼트로 분류하는 작업을 하고 있기 때문에, screen_view 이벤트는 제외해야겠다는 생각을 했습니다. 또한, screen_view -> click_login -> screen_view -> screen_view -> click_login 처럼, 이전 이벤트와 현재 이벤트가 같은 경우를 제외한 다른 경우만을 필터링해서, 굵직한 이벤트만을 필터링해보자! 라는 생각을 가졌었습니다. --- 1000자 이내로 작성해야 글이 올ㅠ 퍼널의 수가 열 몇개로 나오지 않을까? 하는 예상과 다르게 총 433개의 단계가 나왔습니다. click_login -> click_food_category -> click_restaurant -> click_food -> click_cart -> view_recommend_extra_food -> click_payment 와 같이 click_login ----- > click_payment까지의 여정의 가짓수가 너무 많아, 단계를 단순화할 필요성이 있어보였습니다. 그래서, 위의 결과와 같이 총 5개로 퍼널을 나누었습니다.
public enum HttpStatus { NONE(-1, "정의되지 않은 상태코드입니다."), OK(200, "OK"), BAD_REQUEST(400, "Bad Request"), NOT_FOUND(404, "Not Found"), INTERNAL_SERVER_ERROR(500, "Internal Server Error"); ... public static HttpStatus findByCode(int code) { for (HttpStatus status : HttpStatus.values()) { if (status.getCode() == code) { return status; } } return HttpStatus.NONE; } public boolean isSuccess() { return code >= 200 && code < 300; } } public class HttpStatusMain { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("HTTP CODE: "); int httpCodeInput = scanner.nextInt(); HttpStatus status = HttpStatus.findByCode(httpCodeInput); System.out.println(status.getCode() + " " + status.getMessage()); System.out.println("isSuccess = " + status.isSuccess()); } } 안녕하세요 김영한 강사님. 먼저 항상 양질의 강의를 제공해주셔서 감사하다는 말씀을 전해드리고 싶습니다. HttpStatusMain 에서 null 체크하는 일이 발생하여 이를 제거해보면 어떻게 해야할까 에 대한 고민이 들었습니다. 그래서, Enum 에 null object pattern 을 적용해보면 어떨까 하여 HttpStatus.NONE(-1, "정의되지 않은 상태코드입니다.") 와 같이 정의해봤습니다. 이렇게 적용했을 때, 1. 다른 사이드 이펙트가 있을까요? 1-1. findByCode() 에서 return HttpStatus.NONE 이 아닌 throw new IllegalArgumentException("해당하는 상태코드가 없습니다."); 와 같이 RuntimeException 을 날리는 것이 더 나은 방법일까요? 2. 현업에서는 enum 타입의 null check, null object pattern 를 어떻게 구현하는지 궁금합니다. 긴 글 읽어주셔서 감사합니다.