inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

묻고 답해요

172만명의 커뮤니티!! 함께 토론해봐요.

2장. 작전2: 분산 서버 로그 처형 작전 (시스템에 의존적인) SystemCommandTasklet 관련 질문

미해결

죽음의 Spring Batch: 새벽 3시의 처절한 공포는 이제 끝이다.

안녕하십니까 형님! 2강 작전2 : 분산 서버 로그 처형 작전 관련해서, 실무적으로 어떠한 방향으로 접근하는 것이 좋을지 의문이 생겨서 질문 드려봅니다! 제가 윈도우 환경에서 실행하다보니, 자료에 기술된 cli를 윈도우 환경으로 바꾸는데 많은 비용을 소모하였습다. 특히 이번 내용의 경우, 단순 개행문자 및 OS간 인식문제를 넘어서, 명령어를 아예 윈도우 환경에 맞게 변경을 해야한다는 점에서 다소 힘이 많이 들었던 것 같습니다. 뿐만 아니라, 윈도우 cmd 환경에서의 실행을 고려해서 cmd -c의 명령어를 추가해준다든지, 리눅스의 mkdir -p가 먹히지 않으므로 mkdir를 &&로 이어서 실행하게끔 한다든지..(이러다보니 collected_logs 폴더도 최초부터 존재하지 않아야 정상 실행이 가능한 환경적 제약사항까지 고려해야 하였습니다) 전체적으로 환경의 차이를 많이 느끼고 그만큼의 비용도 일전보다는 훨씬 많이 소모된 느낌이 있었던 내용이었습니다. //-p 옵션은 윈도우에서 안먹힘..따라서 collected_logs라는 디렉토리 생성(mkdir)은 처음부터 존재하지 않아야 한다. //SystemCommandTasklet -> 반드시 cli 명령어 작성 필요 @Bean @StepScope public SystemCommadTasklet mkdirTasklet( @Value("#{jobParameters['date']}") String date) throws IOException { SystemCommandTasklet tasklet = new SystemCommandTasklet(); // //tasklet.setWorkingDirectory(System.getProperty("user.home")); //실행환경은 batch 명령어 작성환경에 상관없이 무조건 cmd //경로직접주임은 반드시 백슬래쉬 tasklet.setWorkingDirectory("C:\\Users\\gyrbs\\OneDrive\\Desktop"); String collectedLogsPath = "collected_logs\\" + date; String processedLogsPath = "processed_logs\\" + date; //String collectedLogsPath = String.format("collected_logs\\%s", date); //String processedLogsPath = String.format("processed_logs\\%s", date); String command = String.format("mkdir %s && mkdir %s", collectedLogsPath, processedLogsPath); // //tasklet.setCommand("mkdir", "-p", collectedLogsPath, processedLogsPath); //tasklet.setCommand("cmd", "/c", "mkdir", collectedLogsPath); tasklet.setCommand("cmd", "/c", command); tasklet.setTimeout(3000); // 3초 타임아웃 return tasklet; } @Bean @StepScope public SystemCommandTasklet scpTasklet( @Value("#{jobParameters['date']}") String date) { SystemCommandTasklet tasklet = new SystemCommandTasklet(); //tasklet.setWorkingDirectory(System.getProperty("user.home")); String rootDirectory = "C:\\Users\\gyrbs\\OneDrive\\Desktop"; tasklet.setWorkingDirectory(rootDirectory); String collectedLogsPath = String.format("collected_logs\\%s", date); StringJoiner commandBuilder = new StringJoiner(" && "); for (String host : List.of("localhost", "loan", "pay")) { //String command = String.format("scp %s:~\\logs\\%s.log .%s\\%s.log", // host, date, processedLogsPath, host); String src = String.format("logs\\%s.log", date); String dest = String.format("%s\\%s.log", collectedLogsPath, host); commandBuilder.add(String.format("copy %s %s", src, dest)); } //String src = String.format("logs\\%s.log", date); //String dest = String.format("%s\\%s.log", collectedLogsPath, "localhost"); tasklet.setCommand("cmd", "/c", commandBuilder.toString()); tasklet.setTimeout(10000); //10초 타임아웃 return tasklet; } 이런 느낀점이 많다보니, 이번 내용을 진행하면서, SystemCommandTasklet을 실무적으로 어떻게 활용하면 좋을지 의문점이 생겼습니다. 첫번째는, 단순히 배치를 실행하는 환경이 리눅스라면, 리눅스 환경은 어차피 한동안 안고쳐질거니까, 윈도우라면 그것대로 윈도우 배치 환경은 거의 변경이 없겠지?라는 생각으로 단순히 SystemCommandTasklet으로 구성해야겠네!라고 생각하는게 맞는지 일단 의문이 들었습니다. 배치라는게 이러한 환경적 독립성을 유지하지 않고, 배치실행환경에 맞게 일단 구성하는게 맞을까요? 물론 이 내용이 SystemCommandTasklet을 배우는데 중점이 있을 수 있겠지만, 이번에 꽤 많은 소모를 느껴서 질문드리게 되었습니다! 두번째로, SystemCommandTasklet이 아닌 Tasklet을 사용하여 아래와 같이 시스템에 의존하지 않고 모든 시스템에 적용이 가능한 Java API버전으로 구성하였는데, 빌드 성공하긴 했습니다. //-p 옵션은 윈도우에서 안먹힘..따라서 collected_logs라는 디렉토리 생성(mkdir)은 처음부터 존재하지 않아야 한다. //tasklet -> return (con, ch) @Bean @StepScope public Tasklet mkdirTasklet( @Value("#{jobParameters['date']}") String date) throws IOException { return ((contribution, chunkContext) -> { Path desktop = Paths.get("C:\\Users\\gyrbs\\OneDrive\\Desktop"); Path collected = desktop.resolve("collected_logs").resolve(date); Path processed = desktop.resolve("processed_logs").resolve(date); Files.createDirectories(collected); Files.createDirectories(processed); return RepeatStatus.FINISHED; }); } 이렇게 처음부터 시스템에 상관없는(상관없이 구동이 가능한) 로직을 구현하는게 유지보수적으로도 맞지 않을까..하는 생각이 들었습니다. 형님께서는 실무적으로 이를 적용할때 어떠한 방향으로 구성하시는지, 제가 지금 의문이 드는게 맞는것인지.. 궁금해서 질문드려봅니다!(아니면 만약 SystemCommandTasklet과 같이 시스템에 의존적인 구현이 필요하다면, 이 경우는 환경적으로 바뀔 가능성이 현저히 적다던가..이러한 특수적인 상황적 조건이 있을때 사용하실까요?) 제약사항이 생각보다 커져서 배꼽이 더 커지는 상황이었기에, 형님께 여쭤보고자 하였습니다! 감사합니다!

  • java
  • spring
  • spring-boot
  • spring-batch
  • backend
Hyo Kyun Lee 댓글 2 좋아요 1 조회수 106

타임베이스 정산 배치 실패 시 처리에 대해

해결됨

제미니의 개발실무 - 커머스 백엔드 기본편

안녕하세요 시간날 때 틈틈히 보다가 어느새 막바지에 다다랐네요! 타임베이스 정산 배치가 실패했을 때 궁금한점이 있습니다. 우선 현재는 시간 대별로 오전 1시, 4시, 9시로 배치가 나눠져있는데 이 상황에서 강의에서도 설명하셨듯이 오전 1시 배치가 오전 4시 30분에 끝나서 오전 4시의 배치가 에러가 뱉는 상황에 대해 문제를 해결하는 방법들이 궁금합니다! 우선 제가 생각한 방법들은 다음과 같습니다. 1. 이벤트 기반 이벤트 기반으로 각 단계가 완료되면 바로 다름 배치 단계로 넘어가는 방식입니다. 하지만 이 방법은 이미 기존 인프라와 코드를 변경해야하고 정산 시간이 고정 된다는 요구사항이 있다고도 보여 현재 요구사항에는 적절하진 않은 것 같습니다. 2. 각 배치마다 전 단계 완료 여부 상태를 확인 오전 4시, 오전 9시 배치가 이전 단계의 배치 중 실패한 배치 이력이 있는지 확인하는 방법입니다. 현재 스케줄링 방식으로는 이를 위해 별도의 배치 기록 테이블을 만들어서 배치 이력 관리를 추가합니다. 오전 4시, 오전 9시에 해당 정산 날짜에 대해 실패한 이전 배치 단계 이력이 있다면 성공할 때 까지 N번 재시도 합니다. 정산의 즉시성이 필요하다면 N번 재시도 후 실패했을 때 해당 배치 단계를 에러 상태로 처리하고, 에러 로깅과 알림을 전송합니다. 다음 배치 단계는 정산 날짜에 해당하는 이전 배치 이력이 에러 상태로 남아 있다면 실행하지 않습니다. 현재 요구사항에서는 수동으로 처리할 부분은 최대한 제거하는 2번 방식을 사용할 수 있을 것 같습니다. 제가 배치 관리에 대해 잘 이해하고 있는지 모르겠어서 생각한 방법에 대해서 재민님은 어떻게 생각하시는지 궁금합니다.! 항상 잘 보고 있습니다 감사합니다!

  • kotlin
  • spring-boot
  • 도메인
  • dbms/rdbms
  • backend
HH 댓글 2 좋아요 1 조회수 81

백준 1940 주몽의 명령 시간복잡도

미해결

Do it! 알고리즘 코딩테스트 with JAVA

해당 문제 시간복잡도 O(NlogN)이 맞나요? sort 함수 이용 O(NlogN) 투포인터 탐색 O(N) 이니까요

  • java
  • 코딩-테스트
  • 알고리즘
이진호남선 댓글 0 좋아요 0 조회수 82

멀티스레드 상황인데 currentParticipants 가 AtomicInteger가 아닌 이유?

해결됨

6주 완성! 백엔드 이력서 차별화 전략 4가지 - 똑같은 이력서 속에서 돋보이는 법

1. 현재 학습 진도 몇 챕터/몇 강을 수강 중이신가요? 4-6. 트랜잭션 실습 - 쿠폰 동시성 문제 해결 여기까지 이해하신 내용은 무엇인가요? 2. 어려움을 겪는 부분 똑같이 멀티스레드 환경인데 successCount 는 AtomicInteger로 세팅되어 있고, currentParticipants 는 AtomicInteger가 아닌 이유가 궁금합니다. 이렇게 되면 Java단에서부터 currentParticipants++할 때 동시성 문제가 발생하지 않을까요? 3. 시도해보신 내용 GPT에 물어보니 AttributeConverter 사용하면 JPA에서도 AtomicInteger를 쓸 수 있다고 합니다.

  • java
  • spring
  • mysql
  • redis
  • 이력서
  • 카카오공채-채용
happyrichman 댓글 3 좋아요 0 조회수 128

order_item 테이블 (order_id, product_id) 유니크 제약조건 누락

미해결

김영한의 실전 데이터베이스 - 설계 1편, 현대적 데이터 모델링 완전 정복

안녕하세요. 항상 좋은 강의 잘 듣고 있습니다. 다름이 아니라, 강의 및 강의 자료에 누락된 부분이 있는 것 같아 글 남깁니다. [물리적 모델링 - 실습] 파트에서 테이블 정의서 및 DDL 스크립트를 작성하는 부분에 order_item 테이블이 order_id와 product_id를 각각 외래키로 들고 있는데, 앞선 강의에서 설명해주신 바에 따르면, 주문 항목 데이터 저장 시, 특정 주문에 대한 특정 상품 하나가 여러 번 중복으로 저장되는 걸 방지하기 위해, (order_id, product_id)에 UNIQUE 제약조건을 만들어야 된다 라고 하셨는데, 그 부분이 빠진 것 같습니다. 감사합니다.

  • sql
  • mysql
  • dbms/rdbms
  • 소프트웨어-설계
  • SQLD
김민석 댓글 2 좋아요 0 조회수 103

사가 패턴 질문

해결됨

카카오 면접관이 알려주는 MSA 관점에서의 분산 트랜잭션 패턴

안녕하세요. 현재 사가패턴을 공부하고 있는 개발자입니다. 제가 알고 있는 내용과 조금 다른 내용이 있어서 궁금증에 물어봅니다. 오케스트레이션 사가를 원래 rest api로 구현하나요? 이것도 카프카 같은 브로커 사용해서 만든다고 알고 있어서요.

  • java
  • spring
  • mysql
  • spring-boot
  • msa
김재연 댓글 2 좋아요 0 조회수 121

커서 기반 페이지네이션 과 무한 스크롤링

해결됨

스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판

안녕하세요 강사님 강의를 수강하는 학생입니다. 무한 스크롤링은 두 번째부터 보통 끝쪽 id 에 데이터를 기반으로 데이터를 불러오는 방식이 보통 커서 기반 페이지네이션과 큰 차이가 없는 건가요? (claude 나 ) 블로그 보면서 이것도 비슷한 개념인 거 같아서 질문 드립니다. @Entity @Table( name = "post", indexes = { @Index( name = "idx_post_board_created_id", columnList = "board_id, created_at DESC, post_id DESC" ), @Index( name = "idx_post_created_id", columnList = "created_at DESC, post_id DESC" ) } ) @Getter @NoArgsConstructor public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "post_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) } 이렇게 엔티티에 인덱스를 만들어서 사용하는지 궁금합니다. 강의에서 처럼 SnowFlake도 보통 저렇게 인덱스를 만들어서 활용하는지도 궁금합니다.

  • java
  • mysql
  • spring-boot
  • kafka
  • redis
KMC 댓글 2 좋아요 0 조회수 155

추가 질문 드립니다!

미해결

포트폴리오 어나더레벨 | 합격하는 이력서 작성법

제가 이제 만으론 28살이지만 내년에 한국나이로 30살입니다! 여태까지 자격증만 따느라 언어 실력이 많이 부족합니다. 알고리즘은 백준으로는 골드 2이고 현재 스프링을 통해 DB 없는 웹 CRUD 만 만들 수 있는 수준 (SQL은 이제 공부하려고 합니다) 인데 싸피 16기 입과가 7월이어서 수료하면 한국나이 31살이 넘는데 싸피 모집일까지 기다렸다가 싸피를 가는게 좋을까요 아니면 내년 초에 바로 시작할 수 있는 프로그래머스, 멋사, 코드잇 같은 다른 B급 부캠을 가는게 좋을까요? 나이를 먹어가니까 뭔가 다급해지는 경향이 있어서 여쭤봅니다 ㅠㅠ 모아둔 돈은 넉넉해서 돈 관련해서는 여유롭습니다! 그리고 커리어에 군경력을 추가하는게 좋을지도 궁금합니다! 계속 여쭤봐서 죄송합니다ㅜㅜ

  • HTML/CSS
  • javascript
  • python
  • java
  • 포트폴리오
eovnfjfpa 댓글 1 좋아요 0 조회수 318

질문있습니다!

미해결

포트폴리오 어나더레벨 | 합격하는 이력서 작성법

안녕하세요 큰돌님 강의 잘 보고 있습니다 궁금한게 몇가지 있어서 여쭤봅니다! 군경력이 6년있고 군생활동안 사이버 대학으로 학사 졸업을 했는데 비전공이어서 따로 학점은행제 편입 후 컴퓨터 공학 학사를 취득했는데 학위를 기재하는게 좋을까요? 사이버대학 학점 3.4 학점은행제 학점 3.8입니당 EX -> 사이버 대학 OO 졸업 학점은행제 컴공 졸업 군생활 도중 교관 경연대회 우수 사단장 상장을 받았었는데 이걸로 소프트 스킬 역량을 강조할 수 있는지도 궁금합니다. 정보처리기사, 빅데이터분석기사, adsp , sqld를 취득했었는데 자격증 칸을 만들어서 추가하는게 나을지도 궁금합니다! 사무자동화산업기사와 토익스피킹IM3도 보유중이긴 한데 그건 어필이 따로 안될 것 같은데 넣는게 좋을지도 궁금합니다!

  • HTML/CSS
  • javascript
  • python
  • java
  • 포트폴리오
댓글 1 좋아요 0 조회수 101

배치에서 full outer join을 쓴다고 하셨는데 예시를 알 수 있을까요?

미해결

데이터 분석 SQL Fundamentals

- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 배치에서 full outer join을 쓴다고 하셨는데 예시를 알 수 있을까요? 배치에서 쓰인다는게 잘 와닿지가 않습니다.

  • sql
  • postgresql
  • dbms/rdbms
  • 데이터-엔지니어링
가나파이 댓글 2 좋아요 0 조회수 120

Madrid에 살고 있는 고객이 주문한 주문 정보를 구할것. 실습 질문드립니다

미해결

데이터 분석 SQL Fundamentals

안녕하세요 해당 강의 2분 50초 정도에서 궁금한 것이 있습니다. 고객이 한 번도 주문을 하지 않은 경우에도 고객정보는 조회가 되어야하기 때문에 left outer join을 쓰는 이유는 이해가 되는데요. 그 다음 주문접수 직원정보를 구해야 할 때 left join을 하는 부분이 이해가 되지 않습니다. 저는 join key를 b와 c 테이블에 대해서 하니까 b와 c간의 join으로 생각이 되어서 inner join을 걸었었는데요. 이건 사실 customers와 employees 간의 outer join이니까 left outer join을 써야하는걸까요?

  • sql
  • postgresql
  • dbms/rdbms
  • 데이터-엔지니어링
가나파이 댓글 2 좋아요 0 조회수 79

강의 PDF는 어디에서 다운로드 할 수 있을까요?

해결됨

제미니의 개발실무 - 커머스 백엔드 기본편

"4. 강의 PDF 자료 및 프로젝트"에서 프로젝트 소스는 있는데 강의PDF는 안 보이네요.. 어디에서 강의 PDF를 찾을 수 있을까요?

  • kotlin
  • spring-boot
  • 도메인
  • dbms/rdbms
  • backend
bong4043 댓글 2 좋아요 1 조회수 70

[MyString 실습 인스턴스 중복 생성 문의] MyString.setString(String param)

해결됨

기초 탄탄! 독하게 시작하는 Java - Part 2 : OOP와 JVM

선생님 안녕하십니까 public void setString(String param) { string = param.getBytes().clone(); } getBytes() 메서드 호출 시점에 [] bytes 인스턴스가 새로 생성 되었으나 이후에 clone() 메서드를 호출하는 것은 getBytes()로 새로 생성한 인스턴스에 대한 또 다른 인스턴스를 생성하는 것이므로 이는 중복 생성이 아닌가.. 하는 생각이 들어 여쭙고자 합니다. 제 생각에는 매개변수의 타입이 [] bytes 임을 전제로 하시고 말씀하신것 같습니다만 말씀하시는 내용과 코드가 달라 혹여 수강생들에게 혼란을 주지 않을까 싶어 질문으로 남깁니다. 혹여나 제가 잘못 생각한 부분이 있다면 따끔히 지적 부탁드립니다. 항상 좋은 상의 만들어주셔서 감사합니다 (꾸벅)

  • java
  • 객체지향
  • jvm
  • data-structure
ygu9907 댓글 2 좋아요 1 조회수 133

강의 교안 제공 문의

미해결

실리콘밸리 데이터 리더가 알려주는 기초 SQL

안녕하세요 강사님! 정말 명강입니다....감탄하면서 잘 듣고 있습니다! 다름이 아니라, 혹시 강의 교안은 따로 제공이 될까요? 코랩 실습파일은 있는데, 강의 교안 다운 링크가 없어서요 혹시 강의 교안 pdf로 제공되는지 문의드립니다!

  • sql
  • 빅데이터
  • dbms/rdbms
  • 데이터-엔지니어링
  • duckdb
  • 데이터-리터러시
장혜성 (Hye Seong, Ja 댓글 2 좋아요 1 조회수 103

지역 클래스 예제3 지역변수 질문

해결됨

김영한의 실전 자바 - 중급 1편

[질문 내용] 지역 클래스 예제3 의 LocalPrinter 인스턴스 생성 직후 메모리 그림인데 이때 스택 영역에서 이미지에 질문이 있습니다. process 메소드가 끝나면서 x002 를 리턴하는데 이를 받는 변수가 main 메소드 내에 printer 지역변수가 있어야 되지않나요? 더해서 localOuter 의 변수또한 가져야 된다고 생각해서 스택 프레임이 사라지면 printer 변수에 담긴 객체는 '어떻게 지역변수와 파라미터를 기억하냐?' 에 대해서는 힙 영역의 객체를 가리키고 LocalPrinter 객체를 만들때 사라질 운명인 지역변수를 복사해서 넣어둔다고 이해했습니다.

  • java
  • 객체지향
무깨 댓글 2 좋아요 0 조회수 88

neo4j 데스크탑 config파일설정변경

미해결

graphRAG - Neo4J로 구현하는 지식 그래프 기반 RAG 시스템 (feat. LangChain)

강사님 안녕하세요 강의에서는 setting을 누르라고 하는데 , 제가 설치한것에서는 setting버튼이 없습니다 ㅎ

  • dbms/rdbms
  • neo4j
  • llm
  • langchain
  • rag
  • 카카오공채-개발
anycad11 댓글 2 좋아요 0 조회수 162

CommandLineJobRunner를 통한 실행

미해결

죽음의 Spring Batch: 새벽 3시의 처절한 공포는 이제 끝이다.

형 이거 CommandLineJobRunner를 통한 실행할 때 프로필도 줄 수 있어?

  • java
  • spring
  • spring-boot
  • spring-batch
  • backend
ABBCD 댓글 2 좋아요 1 조회수 101

실습권한좀 얼른주세요

미해결

개발 역량 + 자격증까지, Java로 PCCP 자격증 준비하기

실습권한좀 얼른주세요. 진도를 못나갑니다.

  • java
  • 코딩-테스트
  • 알고리즘
  • data-structure
Cat Developer 댓글 1 좋아요 0 조회수 76

실습권한 부여언제되나요 신청했는데 얼른주세요.

미해결

개발 역량 + 자격증까지, Java로 PCCP 자격증 준비하기

실습권한 부여언제되나요. 권한없어서 강의 진도를 못나가고 있습니다.

  • java
  • 코딩-테스트
  • 알고리즘
  • data-structure
Cat Developer 댓글 1 좋아요 0 조회수 71

취소-코드느끼기 / Cancel을 별도의 스키마로 관리하는 방식의 장점

해결됨

제미니의 개발실무 - 커머스 백엔드 기본편

안녕하세요, 강사님. 취소 - 코드느끼기 수업의 5:00~ 부분부터 '결제', '취소'의 스키마를 분리하여 레코드를 immutable하게 다루는 상황의 예시로 결제 취소 상황을 설명하셨는데 어떤 부분에서 유리한건지 모르겠습니다. 7일 전 주문을 30일 후에 취소함 '취소'를 '결제'의 상태로 반영할 때 '결제'의 상태를 '취소'상태로 변경 - 레코드 수정 시각 갱신 취소된 '결제' 조회 시 레코드 수정 시각을 이용해야 함 '취소'를 스키마로 관리할 때 새로운 '취소' 레코드 추가 제가 이해한 상황은 '취소된 결제를 결제 id 없이 취소 시각으로 조회해야한다'는 것입니다. 취소된 결제를 결제 id 대신 취소 시각으로 찾는 경우는 어떤 상황인지, 조회를 위해 어느정도 취소시각의 범위를 특정할 수 있는 데이터가 존재할텐데 데이터가 결제 id와 분리되어 존재하는 이유가 무엇인가요? '취소'스키마가 따로 존재해서 결제 취소가 레코드로 쌓일 때, 취소 시각으로 찾는다면 무엇이 다른건지 모르겠습니다. '규모적으로 선택하라. 테이블이 적고 테이블 로우가 적고 접근 범위 자체를 줄일 수 있고, 이런 장점으로 보면 페이먼트 테이블을 만들어도 되는데요' 라고 말씀하신 부분도 모르겠습니다! 수업 진행하시면서 1) 각 개념의 레코드가 자신의 영역 안의 맥락으로만 수정된다, 2) '결제'의 레코드는 많고, '취소'의 레코드는 적으므로 취소 일자로 조회하는 속도 차이가 난다고 하시는 것은 이해했습니다만 앞서서 예시로 들어주신 부분은 잘 모르겠네요. 감사합니다.

  • kotlin
  • spring-boot
  • 도메인
  • dbms/rdbms
  • backend
송진우 댓글 2 좋아요 2 조회수 160

인기 태그

인프런 TOP Writers

주간 인기글