[오늘의 회고]

 

1. 어려웠던 부분 : 알고리즘 풀이를 시작한 날이었다. 28개의 문제를 목요일까지 풀어야 된다는 생각에 그리고 가능하다면 최대한 많이 풀어보고 싶다는 욕심에 너무 피곤하지만 새벽 두시까지 붙잡고 있었다. 아직 어색해서 더 어렵게 느껴지는 것 같은데, 하다보면 익숙해 질 것 같다. 특히 핸드폰번호 가리기 문제와 마지막으로 풀었던 행렬의 덧셈은 정말 어려웠다. 내일 다시 복습하는 걸로.

 

 

2. 느낀 점 : 알고리즘은 수능공부라 생각하자. 오늘 2시간 넘게 걸린 문제도 있었고 하루종일 머리를 쥐어짰는데, 막상 풀었던 코드들을 다시 리뷰해보니, 어려울게 하나도 없었다. 사고하는 과정이 가장 어려웠고, 이런 부분은 문제를 많이 풀수록 개선이 될 것 같다.

 

3. 새로 알게 된 내용 : charAt(), toCharArray(), String.toCharArray(), String.valueOf, Arrays.deepToString(), 다차열행렬의 덧셈

 

4. 셀프칭찬  :

  • 알고리즘은 이전에 딱 한번 제일 쉬운 난이도로 풀어봤는데, 하루사이에 많이 발전 한 것 같다.
  • 첫 문제를 풀었을 때와 마지막 문제를 풀었을때 이미 많이 성장한 것 같다는 생각이 들었다. 

 

5. 내일 할 일 :

 

 

어제 못한 블로그 정리를 미루지말고 제발 끝내자

  • 미니프로젝트애서 처음 사용했던 함수나 검색했던 기능들 정리, (하면서 헷갈렸던 부분이나 모르는 부분) 이해하기
  • 깃허브에 올린 코드들에 주석을 달아서 다시 재업로드 하기
  • 배포한 페이지에 피드백 받았던 회원가입후 로그인페이지로 바로 이동하는걸로 바꿔서 재배포 하기
  • JWT 토큰세션이랑 질문잘하기 세션 내용 정리해서 블로그 업로드하기
  • 크롤링 방법 - 셀리늄, 웹드라이브 (내일 찾아보기)
  • css 인라인으로 주는 것과 폴더에 넣는 것의 차이 알아보기

+ 알고리즘 10문제 ! 

 


[오늘 공부한 부분]

 

  • 프로그래머스 알고리즘 풀이

https://leejincha.tistory.com/98

 

[02] 22.11.18 (10문제)

직사각형 별찍기 이 문제에는 표준 입력으로 두 개의 정수 n과 m이 주어집니다. 별(*) 문자를 이용해 가로의 길이가 n, 세로의 길이가 m인 직사각형 형태를 출력해보세요. 짝수와 홀수 문제 설명

leejincha.tistory.com

 

 

 

  • 직사각형 별찍기

이 문제에는 표준 입력으로 두 개의 정수 n과 m이 주어집니다.
별(*) 문자를 이용해 가로의 길이가 n, 세로의 길이가 m인 직사각형 형태를 출력해보세요.

 

 

 
  • 짝수와 홀수
문제 설명

정수 num이 짝수일 경우 "Even"을 반환하고 홀수인 경우 "Odd"를 반환하는 함수, solution을 완성해주세요.

 

 
  • 가운데 글자 가져오기
문제 설명

단어 s의 가운데 글자를 반환하는 함수, solution을 만들어 보세요. 단어의 길이가 짝수라면 가운데 두글자를 반환하면 됩니다.

 

 

  • 두 정수 사이의 합
문제 설명

두 정수 a, b가 주어졌을 때 a와 b 사이에 속한 모든 정수의 합을 리턴하는 함수, solution을 완성하세요.
예를 들어 a = 3, b = 5인 경우, 3 + 4 + 5 = 12이므로 12를 리턴합니다.

 

 

  • 문자열을 정수로 바꾸기
문제 설명

문자열 s를 숫자로 변환한 결과를 반환하는 함수, solution을 완성하세요. 

 

 

  • 없는 숫자 더하기
문제 설명

0부터 9까지의 숫자 중 일부가 들어있는 정수 배열 numbers가 매개변수로 주어집니다. numbers에서 찾을 수 없는 0부터 9까지의 숫자를 모두 찾아 더한 수를 return 하도록 solution 함수를 완성해주세요.

 

 

  • 음양 더하기
문제 설명

어떤 정수들이 있습니다. 이 정수들의 절댓값을 차례대로 담은 정수 배열 absolutes와 이 정수들의 부호를 차례대로 담은 불리언 배열 signs가 매개변수로 주어집니다. 실제 정수들의 합을 구하여 return 하도록 solution 함수를 완성해주세요.

 

 

  • 평균 구하기
문제 설명

정수를 담고 있는 배열 arr의 평균값을 return하는 함수, solution을 완성해보세요.

 

 

  • 핸드폰 번호 가리기
문제 설명

프로그래머스 모바일은 개인정보 보호를 위해 고지서를 보낼 때 고객들의 전화번호의 일부를 가립니다.
전화번호가 문자열 phone_number로 주어졌을 때, 전화번호의 뒷 4자리를 제외한 나머지 숫자를 전부 *으로 가린 문자열을 리턴하는 함수, solution을 완성해주세요.

 

내가 한 풀이 - 구현하면 같은 값은 나오지만 프로그래머스에서의 정답은 아니다. 여기서 아이디어를 얻어 답안을 작성했다.

 

프로그래머스에 풀었던 나의 답
https://velog.io/@tjgurtn112/%ED%95%AD%ED%95%B499-%EC%9E%90%EB%B0%94-1%EC%9D%BC%EC%B0%A8-10

위는 같은조 혁수님이 푸신 답안. 이렇게 쉽게 풀릴 수 있다니 ㅠㅠ 

 

  • 행렬의 덧셈
문제 설명

행렬의 덧셈은 행과 열의 크기가 같은 두 행렬의 같은 행, 같은 열의 값을 서로 더한 결과가 됩니다. 2개의 행렬 arr1과 arr2를 입력받아, 행렬 덧셈의 결과를 반환하는 함수, solution을 완성해주세요.

 

 

미경님의 친절한 설명 덕분에 다차열 배열이 조금 더 이해가 된 것 같다 : ) 감사합니다 ! 

'알고리즘' 카테고리의 다른 글

[06] 두 정수 사이의 합 JAVA  (0) 2022.11.21
[05] 가운데 글자 가져오기 JAVA  (0) 2022.11.21
[04] 짝수와 홀수 JAVA  (0) 2022.11.21
[03] 직사각형 별찍기 JAVA  (0) 2022.11.21
[01] 항해99 Java 알고리즘 과제  (1) 2022.11.18

과제1

  • 다음 코드를 실행하면 출력 결과로 5를 기대했는데 4가 출력되었습니다. 어디에서 잘못 작성된 것일까요?

풀이 : 

위 문제에서 var1 과 var 2는 int 타입의 변수이다. 따라서 var1/var2 연산은 정수끼리의 연산이기 때문에 결과도 정수만 출력이 되어 2.5가 아닌 2가 된다. 이 연산을 2.5라는 실수타입의 값으로 변환하기 위해 다음과 같은 방법이 있다.

  • double var3 = (double) var1/var2
  • double var3 = var1/(double) var2
  • double var3 = (double) var1/ (double) var2

이렇게 int 타입을 double 타입으로 변환해 값을 출력하면 5 라는 값을 얻을 수 있다.

 

 

과제 2

  • 다음 코드를 실행했을 때 출력 결과는 무엇입니까? (증감연산자에 대해 알아보세요!)

풀이:

증감연산자 ++x 는 다른 연산을 수행하기 전에 이미 값이 1 증가되었기 때문에 12가 된다. 그리고 증감연산자 y--의 경우 다른 연산을 수행하고 나서 값이 -1 되기 때문에 19가 된다. 12+19의 값인 31이 출력된다.

 

https://unknownyun.blogspot.com/2018/08/blog-post_38.html

 

과제3

  • while문과 Math.random() 메소드를 이용해서 2개의 주사위를 던졌을 때 나오는 눈을 (눈1, 눈2) 형태로 출력하고, 눈의 합이 5가 아니면 계속 주사위를 던지고, 눈의 합이 5이면 실행을 멈추는 코드를 작성해보세요. 눈의 합이 5가 되는 조합은 (1,4), (4,1), (2,3), (3,2)입니다.

결과 출력창

 

① 원하는 숫자의 랜덤 숫자를 출력하는 매소드 Math.random() 

  • 이 메소드는 0.0과 1.0 사이에 속하는 double 타입의 난수 하나를 리턴한다. 0.0 <= Math.random<1.0
  • start부터 시작하는 n개의 정수 중에서 임의의 정수 하나를 얻기 위한 연산식은 다음과 같다
  • int num = (int)(Math.random()*n) + start;
  • int num = (int) (Math.random() * (최댓값-최소값+1)) + 최소값
    • 예시) 1 ~ 6 까지의 랜덤 숫자 6가지 출력→ (int) (Math.random() * 6) + 1
    • (int) (Math.random() * (6-1+1)) + 1
    • 예시) 12 ~ 24까지의 랜덤 숫자 10가지 출력
      (int) (Math.random() * 13) + 12

② 따라서 이 문제는 변수 두개를 설정하고, 각각 랜덤값을 지정한 뒤, 반복문 while(true)로 계속해서 돌리다가 둘의 합이 5 일 때 break; 로 멈춰주면 원하는 값을 출력할 수 있다.

'알고리즘' 카테고리의 다른 글

[06] 두 정수 사이의 합 JAVA  (0) 2022.11.21
[05] 가운데 글자 가져오기 JAVA  (0) 2022.11.21
[04] 짝수와 홀수 JAVA  (0) 2022.11.21
[03] 직사각형 별찍기 JAVA  (0) 2022.11.21
[02] 22.11.18 (10문제)  (0) 2022.11.19

[오늘의 회고]

 

1. 어려웠던 부분 : 어제 새벽 세시에 일단 배포를 마치고 오늘은 반응형 페이지 작업을 했다. 쉬울줄 알았던 부분이 은근히 시간이 많이 소요됐다. 4일만에 한 프로젝트를 끝내는데, 첫날 화면단 만들고 둘째날과 셋째날 서버단을 꾸역꾸역 끝내고 마지막날 발표를 준비하는 과정 자체가 너무 힘들었다. 우리조 사람들은 아마 각자 본인이 도움이 안되는 것 같다는 마음고생을 많이 하셨을 것 같고, 나는 나대로 프로젝트를 마무리하자는 마음에 어떻게든 끌고가려고 아등바등 혼자 삽질을 하는 것 같아서 너무 힘들었다. 이 프로젝트를 끝내고 싶어서 코드와 씨름하며 달리는 힘든 마음과, 왜 우리조는 이렇게 편성되었나 하는 원망과, 상황탓을 하는 내자신에 대한 죄책감과, 알게모르게 예민해지고 조원분들에게 언행을 잘못 하진 않았을까 라는 생각도 들고 괜히 나때문에 더 심란하시진 않을까 하는 마음 등등 여러가지 감정이 섞여서 너무 힘든 하루였다.

 

 

2. 느낀 점 : 사실 다른 조들의 결과물을 보고 마음이 무거워 졌다. 내가 이곳에 있어도 되는건가 하는 생각이 너무 많이 들었다. 미경님과 현빈님 그리고 유리님이 너무 공감해주셔서 힘이 많이 됐는데, 뭔가 발을 잘못 들인게 아닌가 하는 생각에 이번주말은 생각이 많아질 것 같다. 시작단계니까 못하는게 당연한데, 잘하시는 분들이 너무 많아서 괜히 민폐가 될 것 같기도 하고, 좀더 시간이 걸리더라도 시작이 비슷한 사람들이 많은 부트캠프를 선택했어야 했나라는 생각이 들었다. 앞으로 오늘처럼 마음이 힘들어 지는 날이 찾아올텐데 그때마다 잘 넘길 수 있는 단단한 마음을 가져야 겠다고 생각했다.

 

3. 새로 알게 된 내용 : 오늘은 반응형 웹개발과 모바일형 작업을 통해 폰트에 미디어쿼리로 rem을 사용하는 법을 배웠다. 그리고 영상 업로드용 썸네일도 제작해 보았다. 다른 조들의 발표를 통해 크롤링에는 셀리늄과 웹드라이브 방식이 있다는 것을 알게되었는데, 더 알아봐야 할 것 같다. 그리고 은솔님의 설명 덕분에 API 방식의 흐름을 좀 더 이해할 수 있었다.

 

4. 셀프칭찬  :

오늘 잘한일이 뭐가 있을까 ..........

  • 심난한 마음에도 TIL 쓰는 나 칭찬해...
  • 발표를 준비 못해서 횡설수설 하긴 했지만, 정말 프로젝트 시작부터 끝까지 책임감을 가지고 포기하지 않은 점
  • 다른 사람의 고민을 들어준 나 칭찬하자

 

5. 내일 할 일 :

  • 미니프로젝트애서 처음 사용했던 함수나 검색했던 기능들 정리, (하면서 헷갈렸던 부분이나 모르는 부분) 이해하기
  • 깃허브에 올린 코드들에 주석을 달아서 다시 재업로드 하기
  • 배포한 페이지에 피드백 받았던 회원가입후 로그인페이지로 바로 이동하는걸로 바꿔서 재배포 하기
  • JWT 토큰세션이랑 질물잘하기 세션 내용 정리해서 블로그 업로드하기
  • 크롤링 방법 - 셀리늄, 웹드라이브 (내일 찾아보기)
  • css 인라인으로 주는 것과 폴더에 넣는 것의 차이 알아보기

 


[오늘 공부한 부분]

 

  • 반응형 폰트 rem
  • 다른조들의 발표를 통해 발표를 하는 법을 배울 수 있었다.
  • 크롤링 방법 - 셀리늄, 웹드라이브 (내일 찾아보기)
  • 오늘 다른 조 발표때 나왔던 키워드 : 웹소켓, 웹 API, 웹폰트가 클라이언트마다 다르게 보르는 문제 (크로스브라우징), 마크업(프론트 개발자의 기본역량), Spring Batch, CRUD 
  • 최대한 코드마다 주석을 달면 흐름을 이해할 수 있다.

1. 크롤링 오류

① 구현하고자 했던 부분 

  • 네이버 카타르 경기일정 페이지에서 정보 크롤링 해오기

② 발생한 이슈

  • 크롤링해오는 값에 오류가 뜨거나 계속해서 None 값이 나타나는 오류가 발생.

③ 시도한 방법

  • 웹 개발 종합반 강의에서 스파르타피디아 크롤링에 사용하였던 beautifulsoup 라이브러리 select() 함수 사용
  • 다양한 웹사이트 시도 - 구글 카타르 경기 일정, FIFA 사이트, Radiokorea.com
  • 이 중 Radiokorea 사이트에서 필요한 값이 몇 가지 추출되었지만, 어떤 값들은 역시나 None으로 저장됨.

④ 해결 방법

  • 기술개발자님에게 도움 요청! - 안타깝게도 시도 중 매니저님 노트북 베터리가 사망하면서 중단됨
  • 지나가는 능력자 신원님에게 도움요청 - 파이썬으로 select() 함수가 아닌 find() 함수를 이용해서 코드 시현을 해주심
  • 보여주신 파이썬 코드를 참고하여 자바스크립트로 find() 함수를 작성하여 구현 성공 

※ find()와 select()의 차이

  • find: html tag를 통한 크롤링
  • select: css를 통한 크롤링
  • find() 함수에서 사용하는 태그는 이름(name), 속성(attribut), 값(value)으로 구성되어 있기 때문에 find로 해당 이름이나 속성, 값을 특정하여 태그를 찾을 수 있다.
  • find와 select는 태그 이름, 속성, 속성 값을 특정하는 방식은 같다. 하지만 CSS는 이 외에도 다양한 선택자(selector)를 갖기 때문에 여러 요소를 조합하여 태그를 특정하기 쉽다.
  • 그러므로 더 다양한 조건을 이용하여 더 직관적인 방법으로 태그를 찾기 위해서는 find보다는 select를 사용하는 게 적합
  • select 가 문장도 더 간결해 보이고, 속도가 빠르다고 한다.
 

웹크롤링 - BeautifulSoup에서 find와 select 사용하기

BeautifulSoup 라이브러리의 find 메소드와 select메소드 / find 메소드와 select 메소드의 사용 예제 / select()와 find_all() & select_one() 과 find() / find와 select 차이점

velog.io

 

 

2. mongoDB 오류

① 발생한 이슈 

  • 이슈 1 : bson.objectid 라이브러리를 임포트 한 후 사용하지 않게 되어 삭제하였는데, 그때부터 아래와 같은 오류가 발생
  • bson 은 다른 여러 라이브러리에서도 참조되어 사용되는 경우가 많은데 이때 버전마다 상이한 기능으로 인해 오동작하게 되는 경우가 많다고 한다.

② 시도한 방법

  • pip uninstall bson, pip uninstall pymongo, pip uninstall flask-pymongo 삭제하고 flask-pymongo, pymongo 만 설치하라는 글을 보고 따라서 시도했으나 실패
 

bson 설치후 오류, from flask_pymongo import PyMongo - 인프런 | 질문 & 답변

안녕하세요. bson을 설치한 이후에 아래와 같이 오류가 납니다. (env_doc) D:\workspace\python36\DocN_web>python run.py Traceback (most recent call last):   File 'run.py', ...

www.inflearn.com

③ 해결 방법

  • 작업하던 파일들 다른 폴더에 백업하고 프로그램 강제 종료. 백업한 파일로 새로 인터프리터를 받아 작업함.
  • 같이 화면 공유로 도움을 주신 신원님 은솔님 감사합니다 : )

④ 그리고 이후에 mongoDB 에 데이터가 원활히 저장되지 않는 오류가 발생하였는데, 해당 Cluster를 삭제하고 새로 생성하여 사용하니 해결이 되었다. 초급자는 결국 엎어버리는 것이 최후의 수단인 것 같다. 즉, 백업을 하는 습관을 잘 들이자! 

 

잘가...

 

 

3. mongoDB 컬렉션에서 필요한 정보만 추출 하기 

① 구현하고자 했던 부분

  • mongoDB에 크롤링으로 저장되어 있는 메인 페이지 데이터 중 필요한 값만 추출해서 세부 페이지 이동에 사용하고, 세부 페이지에서 그 값만 가져오기

③ 발생한 이슈

  • 저장된 데이터 값만으로는 각 객체별 구분이 어려워 객체를 구분해 줄 수 있는 다른 키값이 필요했다.
  • ObjectId만 추출해서 구현을 하라고 기술 매니저님과 신원님이 조언해 주셨지만, 추출한 값을 어디다 어떻게 사용하는지 이해하지 못했다.

④ 시도한 방법

  • 무한 구글링을 통해 find_one() 함수로 objectid값을 추출하는 데는 성공했지만, 이걸 어떻게 활용해야 할지 설계가 되지 않음

⑤ 해결 방법

  • 기술개발자님에게 도움 요청! - 답을 주셨지만 이해하지 못함
  • 지나가는 능력자 신원님에게 도움요청 - 역시나 기술 매니저님과 같은 힌트를 주셨지만, 이해하지 못함
  • 천사 은솔님에게 도움 요청 -
    • 크롤링을 하면서 DB에 저장되는 순서에 따라 num값을 부여하고 num=len(count)+1 이라는 조건을 붙여 각 객체마다 id 값을 갖도록 했다. 
    • 그리고 아래와 같이 find_one() 함수를 이용하여 id 값을 추출하고, 메인 페이지에서 세부 페이지로 이동할 때  
onclick="location.href='/review?id=${id}

를 이용하여 사용하였다.

    id_receive = int(request.args.get('id'))
    match = db.world.find_one({'id': id_receive}, {'_id': False})

 

그리고 이 값을 이용해 세부 페이지에 필요한 객체만 불러오는 것도 구현할 수 있었다.

const param = window.location.search;
const paramData = new URLSearchParams(param)
const id = paramData.get('id')

 

 

4.  mongoDB 같은 컬렉션에 저장되어 있는 세부 페이지 댓글을 작성한 세부 페이지에만 추출하는 법

① 구현하고자 했던 부분

  • 해당 세부 페이지에서 작성한 댓글은 작성된 세부페이지에서만 보여주는 기능 구현하기

③ 발생한 이슈

  • 각각 다른 세부 페이지에서 comment를 작성하더라도 mongoDB 하나의 콜렉션 안에 저장이 되었다.
  • 따라서 모든 경기별 세부 페이지에 나와선 안될 다른 세부페이지의 댓글이 섞여 보여지는 상황 발생

④ 시도한 방법

  • 세부페이지에 사용된 경기정보가 담겨있는 컬렉션의 나라 키값과, 코멘트 정보가 들어있는 컬렉션 안에 들어있는 나라의 키값이 일치하면 화면단으로 보여주는 작업을 하면 될 거라는 생각이 들었다.
  • 그러나 이 두 개의 컬렉션을 어떻게 하면 동시에 사용할 수 있을지 잘 답이 서지 않았다. Objectid를 이해 못 한 연장선이다.
  • 찾아보니 몽고 디비의 $lookup 함수를 용하면 두 개의 컬렉션을 하나로 합칠 수 있고, 그렇다면 그 안에서 키값을 비교하여 가져올 수 있다는 가설이 생겼다. 그러나 구현에 성공하지 못함
  • https://www.mongodb.com/docs/v4.2/reference/operator/aggregation/lookup/
 

$lookup (aggregation) — MongoDB Manual

let Optional. Specifies variables to use in the pipeline field stages. Use the variable expressions to access the fields from the documents input to the $lookup stage. The pipeline cannot directly access the input document fields. Instead, first define the

www.mongodb.com

  • 또다시 은솔님에게 도움 요청...(나란 민폐녀 ^_ㅠ) - 사전 프로젝트 대나무 숲을 구현하셨던 코드가 저장되어 있는 깃허브를 공유해 주셨다. 사용하신 방법은 인 배딩 방식으로 하나의 컬렉션 안에 배열을 넣고, 그 배열에 다른 컬렉션을 넣어서 사용하는 방식이다.
  • 코드를 참고해서 따라 해보려 했지만, 중간에 암호화된 아이디 값과 비밀번호 값을 처리하는 코드와 내가 필요한 코드를 잘 분별하지 못해서 구현을 하지 못했다.

⑤ 해결 방법

  • 또 다른 능력자 민승님에게 도움 요청 - 프론트엔드 개발자를 준비하시는 민승님은 내가 화면 공유로 화면을 보여드리자 마자 바로 주소창에 있는 아이디 값을 가져오면 되겠다고 하셨다.

  • 위의 페이지 주소 값 마지막에 있는 숫자를 아래와 같은 방법으로 추출하여 코멘트가 저장될 때마다 같이 저장하도록 했다. ['POST']
  • let order = window.location.href.slice(-1)
  • 그리고 코멘트를 ['GET']으로 호출할 때 if문을 사용하여 저장된 값과 페이지 주소 값의 마지막 자릿수가 일치하는 댓글들만 보여주는 코드로 구현에 성공!
function show_comment() {
    let order = window.location.href.slice(-1)

    $.ajax({
        type: "GET",
        url: '/show/comment',
        data: {},
        success: function (response) {
            let rows = response['comments']
            for (let i = 0; i < rows.length; i++) {
                if (order == rows[i]['order']) {   //이부분에서 위의 order와 콜렉션 order값을 비교하여 추출
                    let comment = rows[i]['comment']
                    let team = rows[i]['team']
                    let title = rows[i]['title']

 

 

 

5. AWS 키체인 permission 

① 발생한 이슈

  • 배포를 위해 Terminal 창으로 작업을 하던 중 'aws permission denied (publickey)'이라는 오류와 함께 파이 질라에서 서버와의 연결 오류가 발생하였다.

② 시도한 방법

  • 키 페어 문제라 생각되어 새로 키 페어를 생성하였지만 역시나 같은 문제가 발생했다.

③ 해결 방법

  • 같이 있던 현빈님이 블로그 글에서 키 페어를 다른 디렉토리로 옮겨서 해결했다는 글을 공유해줘서 설마라는 생각으로 키페어를 데스크톱 아무 폴더에 넣었더니 오류가 해결되었다.

 

 


결국 내 문제 해결의 90% 이상은 같은 반 사람들 덕분에 해결이 되었다. 스스로 구글링을 통해 해결하는 것이 가장 베스트이겠지만, 혼자 했으면 훨씬 더 오래 걸렸을 오류들을 훨씬 더 효율적으로 해결한 것 같다. 다들 미니 프로젝트로 바쁘신 와중에 긴 시간 공을 들여 도와주셔서 감사합니다 ㅠㅠ 아 그리고 이 모든 과정을 같이 옆에서 찾아봐주시고 도와주신 팀원들께도 감사를 !! 힘들었지만 너무 훈훈한 첫 시작으로 기억될 것 같다.

+ Recent posts