diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..195b24b Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9905a3b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +package-lock.json +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 1160530..5d4fef7 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,101 @@ # TypeScript + Colyseus 입문 스터디 (2025) -📌 운영 방식 - • 진행 기간: 2025년 4월 12일 ~ 6월 26일 (총 6주차) - • 진행 방식: 비대면 / 매주 자료 제공 + 실습 또는 회고 / PR로 제출 - • 제출 기한: 각 주차 금요일에 자료 제공 → 다음 목요일 밤까지 제출 - • 과제 제출: 동아리 GitHub 레포에 각자 폴더 생성 후 .md 또는 코드 파일 업로드 - • 관리 방식: 포크 또는 브랜치 생성 후 PR 요청, 스터디장이 머지 담당 - -⸻ - -📁 폴더 구조 예시 - -ts-colyseus-study-2025/ -├── week1/ -│ ├── yoonad16/ -│ │ └── reflection.md -│ ├── seyeong/ -│ │ └── 실습.ts -├── week2/ -│ ├── yoonad16/ -│ │ └── reflection.md -... - - • weekX는 각 주차별 과제 제출 폴더 - • 자신의 GitHub ID나 닉네임으로 폴더 생성 - • .md, .ts, .js 등 자유롭게 작성 가능 - -⸻ - -✅ 과제 제출 방법 - 1. 레포지토리를 포크하거나 브랜치를 생성합니다 - 2. 주차별 폴더(weekX) 안에 본인 폴더를 생성합니다 - 3. 실습/회고 파일을 작성하여 업로드합니다 - 4. 커밋 후 Pull Request를 요청합니다 - 5. PR 제목은 아래처럼 써주세요: - • [week1] yunatype 회고 제출 - 6. 스터디장이 확인 후 머지합니다 - -⸻ - -🧩 참여자 목록 - -이름 GitHub ID -정윤아 yoonad16 -공세영 -기지현 -김혜원 -김유리 -박채윤 - -⸻ - -🧾 기타 안내 - • 코드를 올릴 때는 가능하면 실행 결과 캡처나 간단한 설명도 함께 써주세요! - • PR에 질문이 있으면 코멘트로 남겨주세요. - • 노션을 쓰고 싶은 분은 회고용 템플릿도 따로 공유해드릴게요. 자유롭게 선택하셔도 됩니다! - -⸻ - -감사합니다! - -이번 스터디는 결과물이 중요한 게 아니라 서버와 프레임워크 개념을 내 손으로 한번 만져본다는 데 의의가 있어요. -부담 없이, 재미있게, 궁금한 건 바로 물어보면서 함께 해봐요 :) +## 📌 운영 방식 +- **진행 기간**: 2025년 4월 12일 ~ 6월 26일 (총 6주차) +- **진행 방식**: 비대면 / 매주 자료 제공 + 실습 또는 회고 / PR로 제출 +- **제출 기한**: 각 주차 금요일에 자료 제공 → 다음 목요일 밤까지 제출 +- **과제 제출**: 동아리 GitHub 레포 각자 폴더에 `.md` 또는 코드 파일 업로드 +- **관리 방식**: 포크 또는 브랜치 생성 후 PR 요청, 스터디장이 머지 담당 + +--- + +## 📁 폴더 구조 예시 + +``` +ts-colyseus-study-2025/ +├── week1/ +│ ├── 15정윤아/ +│ │ ├── .gitkeep +│ │ └── reflection.md ← 예시 제공됨! 해당 파일을 만들어 추가하고 푸시하면 됩니다. +│ ├── 15공세영/ +│ │ └── .gitkeep +... +``` + +- `weekX`는 각 주차별 과제 제출 폴더 +- 자신의 이름 폴더 안에 `reflection.md` 또는 실습 코드 작성 +- `.gitkeep` 파일은 폴더 유지를 위한 용도입니다 + +--- + +## ✅ 과제 제출 방법 + +1. 본인의 브랜치(`week1/본인이름`)로 체크아웃 (미리 생성되어 있음!) +2. 해당 주차 폴더 → 본인 폴더 안에 `reflection.md` 파일 작성 +3. GitKraken으로 변경사항 감지 확인 +4. 변경사항 Stage → 커밋 (예: `docs: add week1 회고`) +5. 푸시(Push) → GitHub에 브랜치 업로드 +6. PR(Pull Request) 생성 + - PR 제목: `[week1] 15정윤아 회고 제출` + - PR 메시지: 간단한 요약 또는 빈칸 OK +7. 스터디장이 확인 후 머지합니다 + +> ❗ 브랜치와 폴더는 미리 세팅되어 있으니, 자신의 폴더에만 작성해주세요! + +--- + +## 🧩 참여자 목록 + +| 이름 | GitHub ID | +|------|-----------| +| 정윤아 | yoonad16 | +| 공세영 | 7amm_0302 | +| 김유리 | uri-git23 | +| 김혜원 | nyamic | +| 박채윤 | profobbb | +| 기지현 | itisjeon94 | +| 박서영 | sum-young | + +--- + +## 📄 회고 예시 파일 (`reflection.md`) + +```md +# Week 1: 서버와 프레임워크, Colyseus 구조 이해 + +## 새로 알게 된 개념 3가지 +1. 서버는 요청을 받고 응답을 주는 역할을 한다는 것 +2. 프레임워크는 반복적인 구조를 미리 구성해둔 도구라는 것 +3. Colyseus는 Room, State, Client 구조로 실시간 게임 서버를 구성한다는 것 + +## 헷갈렸던 부분 +- 클라이언트와 서버의 실제 작동 흐름이 머릿속에 그려지지 않음 + +## 한 문장 요약 +Colyseus는 여러 플레이어가 공유하는 상태를 서버 중심으로 효율적으로 동기화해주는 프레임워크이다. + +## 느낀 점 +게임을 만들면서도 서버를 이렇게 직접 다뤄볼 기회가 많지 않았는데, 이번에 흐름을 이해해보는 계기가 됐다! +``` + +--- + +## 🛠️ GitKraken으로 과제 제출하는 방법 + +1. GitKraken 실행 → 좌측 상단에서 레포 열기 +2. 좌측 브랜치 목록에서 본인 브랜치(`week1/이름`) 선택 (더블클릭으로 체크아웃) +3. Finder에서 `week1/이름/` 폴더 열고 `reflection.md` 파일 작성/수정 +4. GitKraken 돌아와서: + - 변경사항 자동 감지됨 + - Stage All → 커밋 메시지 작성 (예: `docs: add 회고 week1`) + - Commit → Push 버튼 클릭 +5. 우측 상단 “Create Pull Request” 클릭 → GitHub에서 PR 생성 + +> 📌 머지 전까지는 본인 브랜치 안에서 계속 수정 가능해요! + +--- + +감사합니다! +> 이번 스터디는 결과물이 중요한 게 아니라 **서버와 프레임워크 개념을 내 손으로 한번 만져본다**는 데 의의가 있어요. +> 부담 없이, 재미있게, 궁금한 건 바로 물어보면서 함께 해봐요 :) + diff --git a/week1/.DS_Store b/week1/.DS_Store new file mode 100644 index 0000000..5a7db29 Binary files /dev/null and b/week1/.DS_Store differ diff --git "a/week1/15 \352\263\265\354\204\270\354\230\201/.gitkeep" "b/week1/15 \352\263\265\354\204\270\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week1/15 \352\263\265\354\204\270\354\230\201/reflection.md" "b/week1/15 \352\263\265\354\204\270\354\230\201/reflection.md" new file mode 100644 index 0000000..bc09583 --- /dev/null +++ "b/week1/15 \352\263\265\354\204\270\354\230\201/reflection.md" @@ -0,0 +1,46 @@ +# WEEK 01 : 서버, 클라이언트, 프레임워크의 이해 / Colyseus 맛보기 + + +## 배운 것 요약 + +1. 서버(Server) +서빙 - 제공자, 요청에 응답하는 쪽 +용도 다양(게임, 웹, DB, 프록시, 메일, 앱...etc) + +2. 클라이언트(Client) +갑, 요청하는 쪽. a.k.a 프론트엔드 + +>> Web Browser(Firefox, Chrome, etc) ⇄ Web Server(Apache, Nginx, etc) ⇄ PHP Engine +Client - Web Browser : 요청을 HTML로 보내고 받은 HTML를 렌더링 +Server - Web Server : HTML 받으면 그대로 제공 / PHP 파일이면 엔진에게 하청 넣음. +PHP Engine : PHP 파일 읽고 OS 무관 요청 처리 후 HTTP(정적)로 서버에게 올림. + +3. 프레임워크 vs 라이브러리 +Framework - 이미 만들어진 뼈대에 내가 맞춤, 프레임워크가 내 코드를 읽음(호출?) +Library - 필요할 때 꺼내 쓰는 레토르트 공구, 코드에 넣고 호출하면 됨. +>> 건축 설계도 vs 도구 상자라고 보면 된다. + +4. Colyseus +- 프레임워크, Room이라는 개념을 사용. +룸으로 인스턴스를 생성한다는 것으로 미루어 보아 객체지향 설계 구조체인 듯. +>> 자바 Object/Class와 비슷한? 추상 클래스? +- State Synchronisation : 서버-클라이언트 동기화로 무결성/일관성 유지 +- 확장이 용이하고 클라우드 서비스 제공. + + +## Questions +1. React 같은 JS 프레임워크를 이용하면 Web Browser, 즉 클라이언트/프론트엔드 쪽에서 동적으로 HTML을 이용할 수 있다. +그럼 이제 PHP나 PHP engine은 필요성을 잃은 건가 궁금함. + +2. Colyseus = 실시간 / 멀티플레이어 게임 / 서버 / 프레임워크 +그래서 언어 뭐 쓴다는 건지 모르겠음. +>> 찾아보니 Node.js 환경에서 동작하므로 TypeScript(추천), JavaScript(가능, 근데 경우에 따라 불리할 수 있음.) +>> 클라이언트는 그냥 엔진 따라 언어 달라짐. 당연함. Colyseus는 서 버 엔 진 이 니 까. + + +## 주저리 +정통공 일부의 빅픽쳐를 다시 본 기분. +서버는 항상 클라우드 서비스 이용하거나 호스팅 받았는데 직접 건드려 볼 기회가 생겨서 기대가 됩니다. +웹사이트 만들겠다고 500원 내고 호스팅 받던 게 어제 같은데...... 실제로도 어제네요. +Colyseus가 무료 호스팅을 제공하면 수익은 어떻게 내는 건지 좀 신기. +>> 찾아보니 프레임워크만 오픈소스로 제공하고 클라우드 쓰려면 플랜 결제해야 하는 것이었음. 당연함. diff --git "a/week1/15 \352\270\260\354\247\200\355\230\204/.gitkeep" "b/week1/15 \352\270\260\354\247\200\355\230\204/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week1/15 \352\270\260\354\247\200\355\230\204/reflection.md" "b/week1/15 \352\270\260\354\247\200\355\230\204/reflection.md" new file mode 100644 index 0000000..efbc4d3 --- /dev/null +++ "b/week1/15 \352\270\260\354\247\200\355\230\204/reflection.md" @@ -0,0 +1,34 @@ +# 1. 새로 알게 된 개념 3가지 + +1. 서버-클라이언트 관계 + 1. 서버 : 제공자, 클라이언트 : 요청자 의 관계 + 2. 클라이언트 sw : 클라이언트 머신 위에서 동작하는 sw + 3. 서버 sw : 서버머신에서 구동하는 프로그램들 +2. 서버란? + 1. 네트워크를 통해 서비스를 제공하는 것 + 2. 웹 서버, 메일 서버, 게임 서버, 프록시 서버 등이 존재 (서비스명 + 서버) + 3. 운용형태에 따라 : 온프레미스 / 클라우드 (AWS, GCP, Azure) +3. PHP (server side script) + 1. php를 이해하기 위해, html을 알아야 한다. + 2. web client → web server로 (ex)topic.php를 전달하면, web client 측에서 이를 해석할 수 없기에 php engine이 대신 동작한다… 이런 개념. + 3. CGI(common gateway interface) : web server 와 php engine 사이의 통신규약(약속) +4. 프론트엔드 백엔드 + 1. 고객 - client = front end - server=back end +5. 라이브러리와 프레임워크 + 1. 라이브러리 : 내가 필요할 때 쓰는 것. + 2. 프레임워크 : 가 나를 부름. (나에게 규칙을 정할 권한이 없다.) (ex.django) + 3. 리액트는 라이브러리도, 프레임워크도 될 수 있다. + 1. 라이브러리일 때 : 나의 애플리케이션의 UI를 빌드할 때, 리액트를 부른다면 이때는 라이브러리 + 2. 프레임워크일 때 : 리액트가 우리의 컴포넌트를 부를 때 + +# 2. 헷갈렸던 부분 + +api 문서를 읽는 방법 : 요청 + 응답 이라는 부분이 확실하게 와닿지 않음. + +# 3. 한 문장 요약 + +서버-클라이언트의 관계성, 비전공자도 알아야 하는 api 문서, 프론트엔드와 백엔드, 라이브러리와 프레임워크. + +# 4. 느낀 점 + +서버와 클라이언트의 개념에 대해 공부해볼 수 있었다. 추상적으로 알고 있던 개념을 구체적으로 배워볼 수 있어 유익했고, 프론트엔드와 백엔드라는 단어의 뜻을 새롭게 알 수 있었다. diff --git "a/week1/15 \352\271\200\354\234\240\353\246\254/.gitkeep" "b/week1/15 \352\271\200\354\234\240\353\246\254/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week1/15 \352\271\200\355\230\234\354\233\220/.gitkeep" "b/week1/15 \352\271\200\355\230\234\354\233\220/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week1/15 \352\271\200\355\230\234\354\233\220/reflection.md" "b/week1/15 \352\271\200\355\230\234\354\233\220/reflection.md" new file mode 100644 index 0000000..fb8c96e --- /dev/null +++ "b/week1/15 \352\271\200\355\230\234\354\233\220/reflection.md" @@ -0,0 +1,19 @@ +# week1 : Server, Framework, Colyseus + +## 기본 개념 +1. 서버 = 클라이언트의 요청에 대한 서비스 제공 +2. 클라이언트 = 서비스 요청 +3. 프레임워크 = 개발 템플릿 소프트웨어 + -Colyseus = 실시간 멀티 플레이어 게임 서버를 쉽게 만들 수 있게 해주는 프레임워크 + + +## 알게 된 점 +1. 서버와 클라이언트의 관계와 서버 소프트웨어의 역할에 대해 알게 됨. +2. 서버의 물리적인 모습은 컴퓨터이나 특정 컴퓨터에게 네트워크를 통해 요청을 받고 응답을 하게 만든 것임을 알게 됨. +3. 개발자가 라이브러리를 불러오는 반면 프레임워크는 프레임워크의 규칙을 개발자가 따라야 함 + + +## 느낀 점 +1. 서버, 프레임워크, 라이브러리 등 평소에 많이 쓰거나 들은 단어이지만 그 이름과 작동을 명확히 알 수 있어서 도움이 되었다. +2. 프레임워크의 규칙을 따라 템플릿과 컨트롤러 등을 넣는다는 부분이 구체화되지 않아 이해가 어려웠다. + diff --git "a/week1/15 \353\260\225\354\204\234\354\230\201/.gitkeep" "b/week1/15 \353\260\225\354\204\234\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week1/15 \353\260\225\354\204\234\354\230\201/reflection.md" "b/week1/15 \353\260\225\354\204\234\354\230\201/reflection.md" new file mode 100644 index 0000000..38782d4 --- /dev/null +++ "b/week1/15 \353\260\225\354\204\234\354\230\201/reflection.md" @@ -0,0 +1,42 @@ +# Week1: 서버, 프레임워크, 및 Colyseus 입문 + +## 1. 새로 알게된 점 + +- 서버 소프트웨어: 서버가 구동하는 부분 + 예: Apache, My SQL, PHP 등이 존재 + + - 서버의 종류: 온프레미스(on-premise), 클라우드 + - 온프레미스(on-premise): 스스로 설비를 보유하여 시스템을 운용관리함 + - 클라우드: 다른 사업자가 보유한 설비로 시스템을 운용관리 예: AWS, GCP, Azure + +- PHP: 서버 스크립트 언어. html과는 다르게 ~~.php의 url로 서버에 요청한다면, + 서버에서 바로 처리할 수 없음 (html은 바로 처리 가능), PHP 엔진 호출 후 처리 + + - 서버의 기술 (구분되는 느낌): + - Apache, IIS + - PHP, python, Java + - My SQL, Oracle (아마 데이터베이스?) + +- 서버 개발자: + - 서버의 목적: 24시간 365일 안정적으로 서버를 구동하는 것 + - 리눅스 => 인기있는 서버 올리는 OS + - 리눅스 OS는 다양한 언어를 지원하지 않지만, 자바처럼 파이썬, 자바스크립트, 루비 등 이용가능. + +- API 문서: 프로그램의 특정 기능이 어떻게 작동하는지 정리하는 문서 + +- 프레임워크 vs 라이브러리 + 라이브러리는 개발자가 필요에 의해 호출하여서 사용하는 것을 말하고, 반대로 프레임워크는 호출을 당해서 프레임워크의 규칙에 따라야함. 하지만, 이 구분이 항상 명확한 것은 아님. + +- Colyseus: Colyseus는 JavaScript/TypeScript/Node.js를 사용해 직접 멀티플레이어 게임서버를 쓸 수 있는 프레임워크. + => 선호하는 프론트엔드/게임엔진과 쉽게 합칠 수 있음 +## 2. 헷갈렸던 부분 + + Apache랑 PHP의 차이점이나 다른게 무엇인지, 그리고 프레임워크의 개념에 대해서 헷갈리고 이해가 어려운 부분이 있었습니다. 넓은 관점으로 서버의 동작을 파악하는 것이 쉽지 않았습니다. + +## 3. 한 문장 요약 + + 서버라는 개념과 서버를 이루는 다양한 요소들에 대해서 학습하고 파악할 수 있었습니다. + +## 4. 느낀점 + + 웹 서버라는 단어는 익숙했는데, 생각해보니 게임에서도 정말 서버가 필요한 것 같아서 흥미로웠고, Colyseus에 대해 더 알아보고 싶어졌습니다. 몇 번 들었던 단어들/개념들이 무엇인지 정확하게 알게되어서 좋았습니다! \ No newline at end of file diff --git "a/week1/15 \353\260\225\354\261\204\354\234\244/.gitkeep" "b/week1/15 \353\260\225\354\261\204\354\234\244/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week1/15 \353\260\225\354\261\204\354\234\244/reflection.md" "b/week1/15 \353\260\225\354\261\204\354\234\244/reflection.md" new file mode 100644 index 0000000..0eeee40 --- /dev/null +++ "b/week1/15 \353\260\225\354\261\204\354\234\244/reflection.md" @@ -0,0 +1,15 @@ +#Week 1: 서버와 프레임워크, Colyseus 구조 이해 + +## 새로 알게 된 개념 3가지 +1. 서버와 클라이언트의 관계와 서버 소프트웨어의 역할에 대해 알게 됨. +2. 서버의 물리적인 모습은 컴퓨터이나 특정 컴퓨터에게 네트워크를 통해 요청을 받고 응답을 하게 만든 것임을 알게 됨. +3. 개발자가 라이브러리를 불러오는 반면 프레임워크는 프레임워크의 규칙을 개발자가 따라야 함 + +## 헷갈렸던 부분 +프레임워크의 규칙을 따라 템플릿과 컨트롤러 등을 넣는다는 부분이 구체화되지 않아 이해가 어려움 + +## 한 문장 요약 +Colyseus는 실시간 멀티 플레이어 게임 서버를 쉽게 만들 수 있게 해주는 프레임워크이다. + +## 느낀 점 +서버, 프레임워크, 라이브러리 등 평소에 많이 쓰거나 들은 단어이지만 그 이름과 작동을 명확히 알 수 있어서 도움이 되었다. diff --git "a/week1/15 \354\240\225\354\234\244\354\225\204/.gitkeep" "b/week1/15 \354\240\225\354\234\244\354\225\204/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week1/15 \354\240\225\354\234\244\354\225\204/reflection.md" "b/week1/15 \354\240\225\354\234\244\354\225\204/reflection.md" new file mode 100644 index 0000000..b49d4a6 --- /dev/null +++ "b/week1/15 \354\240\225\354\234\244\354\225\204/reflection.md" @@ -0,0 +1,220 @@ +# Week1 - 서버의 개념 정리 + +## 🌐 서버(Server) + +- **제공하는 쪽**. 네트워크를 통해 서비스를 제공하는 시스템. +- 서비스의 종류에 따라 이름이 다름: 웹 서버, 메일 서버, 게임 서버, DB 서버, 파일 서버, 사운드 서버, 프록시 서버 등. +- 서버가 클라이언트의 역할(포지션)이 되기도 함. +- 일반 컴퓨터처럼 생겼지만, 대규모 서비스는 수많은 컴퓨터를 연결한 형태로 구성됨. +- **정의**: 네트워크를 통해 특정 컴퓨터에게 요청을 받고, 그에 대한 응답을 처리하도록 구성된 시스템. + +## 🛠 서버의 운용 형태 + +### 온프레미스 (On-Premise) +- 직접 설비를 보유하여 시스템을 운용·관리 +- 구축과 조달에 시간 소요됨 +- 구조를 자유롭게 구성 가능 +- 트러블 발생 시 직접 원인 파악이 쉬움 + +### 클라우드 (Cloud) +- AWS, GCP, Azure 등의 서비스 이용 +- 설비 구축 시간 거의 없음 +- 사업자가 제공하는 설비 범위 내에서만 운영 가능 +- 트러블 슈팅이 어려울 수 있음 + +--- + +## 🙋‍♀️ 클라이언트(Client) + +- **요청하는 쪽**, 서버가 제공하는 서비스를 받는 사용자 혹은 프로그램 +- 웹 브라우저 = 클라이언트 (e.g., Chrome, Safari, Firefox 등) +- 클라이언트 머신(아이폰, 노트북 등)과 클라이언트 소프트웨어로 구성됨 + +> 웹 브라우저에서 사용자가 URL을 요청 → 그 요청을 서버가 처리 + +--- + +## 🖥 서버 구성 요소와 관련 기술 + +| 역할 | 예시 | +|------|------| +| 서버 소프트웨어 | Apache, IIS, Nginx | +| 서버 언어 | PHP, Python, Java | +| 데이터베이스 | MySQL, Oracle 등 | + +--- + +## 📜 정적 웹 vs 동적 웹 + +- HTML은 정적 웹 문서를 만들 수 있음 +- 사용자가 많아지면 정적 웹 페이지로는 한계 발생 → **동적 웹 페이지 필요!** + +--- + +## 🔁 동적 웹의 탄생과 CGI (Common Gateway Interface) + +1. 사용자가 웹 브라우저에서 URL 입력 +2. 웹 서버는 요청받은 파일의 확장자를 확인함 +3. `.html` 파일이면 바로 전송 +4. `.php`와 같은 서버 사이드 파일이면 **웹 서버가 직접 처리 못함** +5. 대신, PHP 엔진 같은 프로그램에게 처리를 위임 +6. PHP 엔진이 파일을 실행하고 그 결과를 HTML로 돌려줌 +7. 웹 서버는 그 HTML을 다시 웹 브라우저에게 전송함 + +- 브라우저는 HTML만 이해할 수 있음 → **php인지 아닌지 알 수 없음** +- 이 과정에서 CGI는 웹 서버와 외부 엔진(PHP, Python 등) 간의 통신 표준 역할을 함 + +--- + +## 💡 CGI의 장점 + +- 다양한 기술들(PHP, Python, Java 등)을 독립적으로 바꿔 끼울 수 있음 +- 프로그램 간 통신의 **표준화**를 통해 유지보수와 확장성이 높아짐 + +--- + +## 🧠 PHP란? + +- 서버 사이드 스크립트 언어 +- 정적인 HTML 파일 대신, **동적인 웹페이지를 자동으로 생성** 가능 +- 하나의 PHP 파일로 여러 사용자에게 각기 다른 내용을 보여줄 수 있음 + +--- + +## 🧩 OS (Operating System) + +- **운영체제(Operating System, OS)**는 하드웨어와 소프트웨어 사이에서 중재자 역할을 하는 시스템 소프트웨어 +- 사용자의 명령을 하드웨어가 이해할 수 있도록 바꿔주고, 소프트웨어가 안정적으로 작동하게 돕는 필수 시스템 +- 대표적인 OS 종류: + - Windows, macOS, Linux (데스크탑/노트북) + - Android, iOS (모바일) + - Ubuntu, CentOS, Red Hat 등 (서버) + +--- + +## 👨‍💻 클라이언트 vs 서버 개발자의 OS 활용 + +### 클라이언트 개발자 +- 웹 개발자와 애플리케이션 개발자로 나뉨 +- 애플리케이션 개발자는 OS별 대응이 필요함 (ex. iOS용 앱, Android용 앱은 따로 만들어야 함) + +### 서버 개발자 +- 서버는 하나의 프로그램이 OS 위에서 실행됨 +- 보통 서버는 **리눅스(Linux)** 위에서 작동함 (안정성 + 비용 효율성 때문) +- macOS는 고가이고 GUI 중심이라 서버 용도로는 비효율적임 + +> 💬 "서버도 결국 하나의 컴퓨터이며, 그 위에 OS가 존재해야만 동작할 수 있다." + +--- + +## 🧬 OS 독립적인 개발 + +- 예전에는 OS마다 별도 개발이 필요했음 → 유지보수 어려움 +- 현대에는 OS에 독립적인 개발 방식이 대세 (한 번 만들면 어디서든 돌아가게!) +- 대표적 예시: **Java** + - JVM(Java Virtual Machine) 위에서 실행되므로 OS에 관계없이 작동함 + - 한번 작성하면 모든 플랫폼에서 실행 가능: "Write once, run anywhere" + +--- + +## 🔁 API와 서버 응답 흐름 + +- 서버는 클라이언트의 다양한 요청을 처리하고, 그에 맞는 응답을 보내는 프로그램 +- 클라이언트는 웹, 앱 등 다양한 형태로 서버에 요청을 보냄 +- 서버는 **기능별 요청을 구분**하고 **적절한 응답을 보냄** +- 이때 "요청과 응답 간의 규약"을 정리해 놓은 문서가 **API 문서** + +### 📄 API 문서란? +> 프로그램의 특정 기능이 어떻게 작동하는지 정리해 놓은 문서 +> - 요청 (Request): 요청할 때 어떤 데이터가 필요한지 +> - 응답 (Response): 응답에는 어떤 데이터가 담겨 있는지 +> - 예: 로그인 API +> - 요청: username, password +> - 응답: access token, user id + +--- + +## 🧑‍💻 프론트엔드 vs 백엔드 + +| 구분 | 설명 | +|------|------| +| 프론트엔드(Frontend) | 클라이언트 프로그램 개발자. 웹이든 앱이든 사용자가 직접 보게 되는 UI 개발 담당 | +| 백엔드(Backend) | 서버 개발자. 사용자 요청을 처리하고, DB 연동, 비즈니스 로직을 처리하는 시스템 구축 담당 | + +--- + +## 🧱 라이브러리 (Library) + +> 내가 필요할 때 **내가 호출해서** 사용하는 도구 + +- 개발자가 주도권을 가짐 (내 코드가 주도) +- 특정 기능이 필요할 때 꺼내 쓰는 도구 느낌 +- 내가 원하는 방식대로 코드를 구성하고, 라이브러리는 기능만 보조해주는 역할 +- 자유롭고 유연하지만 통일된 구조는 없음 + +**📌 예시**: +- `jQuery`: HTML 요소 선택, 애니메이션 처리 등 DOM 제어에 특화된 JS 라이브러리 +- `Lodash`: 배열, 객체 등 JS 데이터 처리 기능 강화 +- `ReactJS`: UI를 컴포넌트화해서 관리할 수 있는 라이브러리 (하지만 구조화된 방식 덕분에 종종 프레임워크처럼 쓰이기도!) + +--- + +## 🏗 프레임워크 (Framework) + +> 나는 **프레임워크가 호출하는 입장**, 즉 규칙을 따라야 하는 구조 + +- 개발자가 주도권을 일부 넘겨줌 (프레임워크가 구조와 흐름을 정함) +- "어디에 코드를 넣을지" 정해져 있고, 그 틀에 맞춰 개발함 +- 강력한 기능 제공 + 일관된 구조 → 빠르고 안정적인 개발 가능 +- 대신, 구조에 대한 이해가 부족하면 진입 장벽이 높음 + +**📌 예시**: +- `Django`: Python 기반 웹 프레임워크. MVC(MTV) 구조가 뚜렷함. +- `Angular`: 구글이 만든 프론트엔드 프레임워크 +- `NestJS`: 백엔드용 TypeScript 프레임워크. Express 기반. + +## 🎮 Colyseus Multiplayer Framework란? + +> Colyseus는 **JavaScript/TypeScript + Node.js** 환경에서 **멀티플레이어 게임 서버를 구축**할 수 있게 해주는 오픈소스 프레임워크! + +- 서버와 클라이언트 간 상태 동기화(State Sync)를 **자동화**해주고 +- 클라이언트가 룸(Room)에 쉽게 매칭되도록 도와주는 기능 제공 +- 프론트엔드나 Unity, Godot 등 **게임 엔진과 쉽게 연동 가능** + +### 🌟 주요 특징 요약 + +- 👥 **Match-making into Rooms** + → 하나의 Room 정의만으로 여러 클라이언트가 자동으로 해당 Room 인스턴스에 매칭됨 + +- 🔁 **State Synchronization** + → 서버에서 클라이언트로 상태 정보를 자동 동기화! (게임에서 유닛 위치, 체력 같은 정보가 실시간으로 동기화됨) + +- 📈 **Scalable (확장성)** + → 수평/수직적 확장이 가능하게 설계되어 큰 규모의 게임 서비스도 대응 가능 + +- ☁ **Cloud agnostic** + → 자유롭게 자체 서버에 호스팅 가능하고, 상용 서비스인 [Colyseus Cloud]도 사용 가능 + +--- + +## 🧾 Node.js란? + +> **자바스크립트를 브라우저 밖, 즉 서버에서도 실행할 수 있게 해주는 런타임 환경** + +### ✨ Node.js의 핵심 기능 + +- 원래 JS는 웹 브라우저 안에서만 동작했는데, Node.js 덕분에 서버에서도 사용할 수 있게 됨 +- **V8 엔진** 기반 (크롬 브라우저에서 쓰는 JS 엔진) +- 비동기 I/O, 이벤트 기반 구조 → 서버에서 많은 클라이언트를 동시에 처리하는 데 최적화됨 +- 파일 읽기/쓰기, 네트워크 요청 처리, 데이터베이스 접근 등 서버 작업 가능 + +### 💡 Node.js의 장점 + +- 프론트엔드와 백엔드 모두 **같은 언어(JS/TS)**로 개발 가능 → 개발 효율성 증가! +- 속도가 빠르고 확장성이 뛰어나서 **실시간 채팅, 게임 서버, REST API** 개발에 많이 사용됨 + +--- + + +📁 작성자: 정윤아 +📅 Week1 - 서버 기본 개념 정리 diff --git a/week1/reflection.md b/week1/reflection.md index 5259073..b256cad 100644 --- a/week1/reflection.md +++ b/week1/reflection.md @@ -1,3 +1,28 @@ +# week1 : Server, Framework, Colyseus + +## 기본 개념 +1. 서버 = 서비스(데이터 or 기능 in web) 제공 +//중앙 컴퓨터, 프로그램 +2. 클라이언트 = 서비스 요청 + 소비 +//웹 브라우저, 모바일 앱 +3. 프레임워크 = 개발 템플릿 소프트웨어 + -Colyseus = 실시간 멀티플레이어 + 오픈소스 프레임워크 + //룸 매칭, 동기화, 확장성, 클라우드 독립적 + -Node.js = Typescript 실행 환경 + + +## 알게 된 점 +1. 클라이언트가 각각 앱 실행 -> 중앙 서버 접속(based on Colyseus Framework) -> 클라이언트 쪽에서 데이터 전송 -> 서버가 데이터 가공 후 다시 클라이언트에게 전송 -> 반영 +//클라이언트 - 서버 간의 통신은 상호적 +2. 실시간 멀티플레이어 게임 = 이런 통신이 즉각적으로 이루어져야 함 +3. 일일이 코딩해야 하는 단계를 줄여주는 Framework가 Colyseus + +## 느낀 점 +1. 멀티플레이어 게임의 기본 원리에 대해 알게 되었다 +2. Colyseus는 개발자에게 편리한 환경을 제공한다 + + + ## week 1: 서버와 프레임워크, Colyseus 구조 이해 ## 새로 알게 된 개념 3가지 @@ -45,4 +70,4 @@ cf. API 문서: 프로그램의 특정 기능이 어떻게 작동하는지 정 1주차 내용은 괜찮은데 아직 깃크라켄이 너무 어색해요..............ㅠㅠ ## 느낀 점 -항상 백엔드 개념이 추상적으로만 느껴졌는데 서버개발을 하는 과정과 원리에 대해 전체적으로 볼 수 있어서 좋았다! 리눅스, PHP 등 들어는 봤지만 정확히 모르는 개념도 정리해 보는 기회가 되었다. \ No newline at end of file +항상 백엔드 개념이 추상적으로만 느껴졌는데 서버개발을 하는 과정과 원리에 대해 전체적으로 볼 수 있어서 좋았다! 리눅스, PHP 등 들어는 봤지만 정확히 모르는 개념도 정리해 보는 기회가 되었다. diff --git a/week2/.DS_Store b/week2/.DS_Store new file mode 100644 index 0000000..4997a03 Binary files /dev/null and b/week2/.DS_Store differ diff --git "a/week2/15 \352\263\265\354\204\270\354\230\201/.gitkeep" "b/week2/15 \352\263\265\354\204\270\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week2/15 \352\263\265\354\204\270\354\230\201/1-\353\263\200\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \352\263\265\354\204\270\354\230\201/1-\353\263\200\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..143a3a0 --- /dev/null +++ "b/week2/15 \352\263\265\354\204\270\354\230\201/1-\353\263\200\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,14 @@ +// 이름 변수는 string으로 지정. 값은 내 이름이다. +let 이름: string = "공세영"; + +// 나이 변수는 number로 지정. 숫자니까. +let 나이: number = 22; + +// 합격 여부는 true/false로 나뉘는 boolean으로 지정. +let 합격여부: boolean = true; + + +// 아래로는 콘솔창에 위 세 가지 변수를 출력하는 코드. +console.log(이름); +console.log(나이); +console.log(합격여부); \ No newline at end of file diff --git "a/week2/15 \352\263\265\354\204\270\354\230\201/2-\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" "b/week2/15 \352\263\265\354\204\270\354\230\201/2-\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" new file mode 100644 index 0000000..b798e25 --- /dev/null +++ "b/week2/15 \352\263\265\354\204\270\354\230\201/2-\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" @@ -0,0 +1,9 @@ +// 문자열을 담는 좋아하는_음식 배열 생성. +let 좋아하는_음식: Array = ["샤오롱바오", "아이스크림", "샤브샤브"]; + +// 다른 타입을 담는 튜플 생성. +let 프로필: [string, number] = ["이화여자대학교", 3]; + +// 아래는 위 배열들을 출력하는 코드. +console.log(좋아하는_음식); +console.log(프로필); \ No newline at end of file diff --git "a/week2/15 \352\263\265\354\204\270\354\230\201/3-\354\241\260\352\261\264\353\254\270.ts" "b/week2/15 \352\263\265\354\204\270\354\230\201/3-\354\241\260\352\261\264\353\254\270.ts" new file mode 100644 index 0000000..15c3523 --- /dev/null +++ "b/week2/15 \352\263\265\354\204\270\354\230\201/3-\354\241\260\352\261\264\353\254\270.ts" @@ -0,0 +1,11 @@ +// 점수 variable에 100을 할당. +let 점수: number = 100; + +// 60이 넘는다면 +if (점수 >= 60){ + console.log("합격"); // 합격 출력. + alert("합격"); // 그냥 다른 출력 방식 없나 찾아봤는데 팝업 뜨는 게 재밌어서 같이 붙여둡니다 ^.^v +} else { // 그 외의 경우는 모두 + console.log("불합격"); // 얄짤없이 불합격 출력. + alert("불합격"); +} \ No newline at end of file diff --git "a/week2/15 \352\263\265\354\204\270\354\230\201/4-\355\225\250\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \352\263\265\354\204\270\354\230\201/4-\355\225\250\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..5fd56a8 --- /dev/null +++ "b/week2/15 \352\263\265\354\204\270\354\230\201/4-\355\225\250\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,9 @@ +// 두 숫자를 입력받아 합을 반환하는 함수 선언 +// 첫 번째 숫자는 a, 두 번째 숫자는 b이며 매개변수와 반환 타입 전부 number 타입으로 지정 +function addNumbers(a: number, b: number): number { + return a + b; +} + +// 함수 호출 및 결과 출력. a는 10이고, b는 20이다. +const result: number = addNumbers(10, 20); +console.log(`두 수의 합: ${result}`); \ No newline at end of file diff --git "a/week2/15 \352\263\265\354\204\270\354\230\201/reflection.md" "b/week2/15 \352\263\265\354\204\270\354\230\201/reflection.md" new file mode 100644 index 0000000..3438f52 --- /dev/null +++ "b/week2/15 \352\263\265\354\204\270\354\230\201/reflection.md" @@ -0,0 +1,169 @@ +# WEEK 02 : TypeScript 맛보기(개념 및 실습) + +## 배운 것 요?약? + +TypeScript Playground & StackBlitz +별도의 로컬 개발환경이 없어도 브라우저 상에서 TypeScript 코드를 작성 및 실행할 수 있는 플랫폼. += 온라인 통합 개발 환경, Online IDE(Integrated Development Environment), Web IDE + +TypeScript Playground는 단일 파일 기반으로 코드 테스트가 가능한 반면 StackBlitz는 다중 파일로 프로젝트 빌드까지 가능하며 브라우저에서 직접 실행도 할 수 있다. + + + +TypeScript 문법 + +01. 변수 / Variables + 02. 자료형 / Type + +>> 변수 선언 시 콜론(:)을 이용해 타입 명시 가능 +>> 지정된 타입과 다른 타입의 값을 입력 시 컴파일 에러 발생. +>> JavaScript 타입 시스템 위에 Static Type(정적 타입)을 추가 --> int, float, double과 같이 세분화된 타입이 아닌 [통합된 상위 타입]을 대신 사용한다! +>> Type Inference(타입 추론) 기능을 통해 특정 값으로 초기화 된 변수가 있다면 해당 값 기반으로 타입을 자동 할당해준다. + +타입 사용처 예시 +number 정수, 실수, NaN 등 let 나이: number = 22; +string 문자열 let 이름: string = '공세영'; +boolean 논리값(true/false) let 통과: boolean = true; +null 명시적으로 없음 표현 let 소속: null = null; +undefined 값이 할당되지 않음 let 소속: undefined = undefined; +symbol 고유하며 변경 불가능한 값 let 아이디: symbol = Symbol("skong302"); +bigint 큰 정수 표현 let 큰_수: bigint = 36854523132482345564; + +any 어떤 타입이든 ok let 가격: any = 10000; // 추천 안 한다고 함. +unknown 타입을 모를 때(안전한 any) let 모름: unknown = "abc"; +void (함수) 반환값 없음 function log(): void {} +never 절대 도달하지 않는 함수 반환 타입 function error(): never { throw new Error(); } + + +unknown 추가 설명 +>> unknown은 any와 비슷하게 모든 값을 담을 수 있으나, any와 다르게 해당 변수의 타입이 명확해지기 전까지는 어떤 작업도 허용하지 않음. + +let 입력값: unknown = "세영"; +// console.log(입력값.length); // ❌ 에러! - TypeScript는 입력값의 타입을 모른다. 따라서 해당 변수에 .length 프로퍼티가 존재한다는 보장이 없다. +if (typeof 입력값 === "string") { + console.log(입력값.length); // ✅ 가능! - 조건문으로 변수 타입이 string일 때만 통과 시키는 타입 좁히기(Narrowing)/타입 가드(Type Guard) 작업을 했음. 입력값은 string으로 처리되고 이후 .length 접근이 가능해짐. +} + + +Array / 배열 - number[], string[], Array의 형태로 사용 +let 숫자배열: number[] = [1,2,3]; +let 문자열배열: Array = ["공","세","영"]; + + +Tuple / 튜플 - 길이와 타입이 고정된 배열. +let coordinate: [number, number, string] = [10,20,'xy']; + + +Interface / 인터페이스 - 객체의 형태를 정의하는 문법 요소 +interface student { + name: string; + age: number; +} + +Object / 객체 - 속성의 타입들을 명시할 필요가 있음 +let student: { name: string; age: number } = { name: '공세영', age:22 }; + + +사용자 정의 타입 +type ID = string | number; // 문자열이나 숫자일 것. +let userId: ID = "abcd1234"; + + +Union / 유니언 - 둘 이상의 타입을 허용함. 타입 검사 시 조건문으로 narrowing해 주어야 한다. +let ID: string | number; +ID = "skong302"; +ID = 03021234; + + +Literal Type / 리터럴 타입 - 정해진 특정 값만을 허용한다. 값 자체가 타입. 유효한 옵션을 한정하기 위해 사용. +let direction: "left" | "right"; +direction = "left"; // ✅ +direction = "diagonal"; // ❌ 오류: 허용되지 않은 값 + + +enum / 열거형 - 관련 있는 상수들을 이름으로 묶어 나열한 것. 따로 지정하지 않으면 자동으로 숫자가 할당됨. +enum Direction{ + up, // 0 + down, // 1 + left, // 2 + right // 3 +} + +enum Roles{ + leader = "정윤아" + member = "공세영" +} + +let myRole: Role = Role.member; +console.log(myRole); // "공세영" + + + +03. 조건문 / Conditional Statements +>> if, else, switch 등의 구문 +>> 결과가 무조건 boolean 타입이어야 한다! +>> 지정된 타입과 맞지 않는 비교/연산의 경우 컴파일 에러 발생. + +let age: number = 18; +if (age >= 18) { + console.log("성인입니다."); +} else { + console.log("미성년자입니다."); +} + +let grade: string = "B"; +switch (grade) { + case "A": + console.log("최우수"); + break; + case "B": + console.log("우수"); + break; + default: + console.log("보통"); +} + + + +04. 함수 / Function +>> TypeScript에서는 함수의 타입, 매개변수의 개수나 반환 타입 등을 정의해 함수의 안정성 및 명확성을 높인다. + +function add(x: number, y: number): number { + return x + y; +} + +>> 선택적 매개변수(Optional Parameter)의 개념 - 물음표(?)가 붙은 매개변수는 있어도 없어도 된다. +function 함수명(매개변수?: 타입): 반환타입 + +function greet(name?: string): void { + if (name) { + console.log(`안녕하세요, ${name}님`); + } else { + console.log("안녕하세요, 주인님"); // undefined가 들어가서 default로 출력되는? 듯? + } +} + +greet("세영"); // 출력: 안녕하세요, 세영님 +greet(); // 출력: 안녕하세요, 주인님 + + + + +# 이하 진 짜 reflection + +Q. 이번 주 학습에서 새롭게 알게 된 점은 무엇인가요? +A. TypeScript가 왜 'Type'Script인지 새삼 다시 알게 되었음. 정적 타입 시스템의 편리함에 대해서도 알게 된 듯. int, float, double을 안 쓴다니 신세계다. +정말 개발자를 위한 언어.... 그렇지만 프로젝트를 진행하는 게 아니라서 그런지 TypeScript의 강점인 컴파일 타임 시점의 코드 안정성 확보가 와닿지는 않는 것 같음. 여전히 입력값 잘못 넣으면 오류는 난다. (물론 다른 언어들은 런타임 오류가 많아서 디버깅이 고역임을 이론적으로 알고 있긴 하지만....) + + +Q. 실습하면서 헷갈렸던 부분이 있었나요? +A. TypeScript Playground의 에디터 작동 방식을 설명하는 노션 글 중, 편집기에 입력된 TypeScript 코드를 자동으로 'JavaScript'로 컴파일해 보여준다는 부분에 의문을 가졌음. +TypeScript가 Interpreter에 의해 바로 Binary Code나 기계어로 컴파일 될 것이라고 생각했으나... +먼저 브라우저가 이해할 수 있는 JavaScript로 transpile(by tsc)하는 과정이 필요하단 것을 새로 알게 되었다. + +any 타입은 편리해 보이는데 사용을 지양해야 하는 이유에 대해서도 헷갈렸는데... 조금 생각해 보니 TypeScript의 강점인 타입 검사 기능을 무용지물로 만든다는 점에서 사용 지양해야 함을 깨달음. +코드의 의도(가독성)를 파악하기 어려울 뿐만 아니라 예측하기 어려우니 유지보수하기 어려울 테니까. 컴파일 타임에서의 any 오류는 어떻게 나지? 맥락이 없으니 어쩐지 두루뭉술하다.... + + +Q. Playground(또는 StackBlitz) 사용해본 소감은 어떤가요? +A. 초기 환경 설정 과정을 생략하고, 별도의 설치 없이 곧바로 실습에 돌입할 수 있다는 점에서 Web IDE의 뛰어난 접근성을 체감함. +다만, 이러한 웹 기반 환경의 편의성에도 불구하고 로컬 개발환경을 이용하는 이유, 대규모 프로젝트까지 안정적으로 개발할 수 있는지 여부에 대해선 아직 궁금증이 남음. \ No newline at end of file diff --git "a/week2/15 \352\270\260\354\247\200\355\230\204/.gitkeep" "b/week2/15 \352\270\260\354\247\200\355\230\204/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week2/15 \352\270\260\354\247\200\355\230\204/1.\353\263\200\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \352\270\260\354\247\200\355\230\204/1.\353\263\200\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..e46a2b9 --- /dev/null +++ "b/week2/15 \352\270\260\354\247\200\355\230\204/1.\353\263\200\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,7 @@ +let 이름 : string = '기지현'; +let 나이 : number = 23; +let 합격여부 : boolean = true; + +console.log("내 이름은 " + 이름); +console.log("나이는 " + 나이); +console.log("합격여부는 " + 합격여부 ); \ No newline at end of file diff --git "a/week2/15 \352\270\260\354\247\200\355\230\204/2.\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" "b/week2/15 \352\270\260\354\247\200\355\230\204/2.\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" new file mode 100644 index 0000000..ee1b9a0 --- /dev/null +++ "b/week2/15 \352\270\260\354\247\200\355\230\204/2.\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" @@ -0,0 +1,8 @@ +//1. 배열 연습, 좋아하는 음식 출력하기. +let foods : string[] = ["마라탕", "커피", "과자"]; + +//2. 튜플 선언 엽습. +let school : [string, number] = ["이화여대", 3]; //학교이름, 학년. + +//확인용 출력 +console.log(school) \ No newline at end of file diff --git "a/week2/15 \352\270\260\354\247\200\355\230\204/3.\354\241\260\352\261\264\353\254\270.ts" "b/week2/15 \352\270\260\354\247\200\355\230\204/3.\354\241\260\352\261\264\353\254\270.ts" new file mode 100644 index 0000000..d21261e --- /dev/null +++ "b/week2/15 \352\270\260\354\247\200\355\230\204/3.\354\241\260\352\261\264\353\254\270.ts" @@ -0,0 +1,9 @@ +//1. 점수 선언 +let score : number = 0; + +//2. 조건문 +if (score >= 60) { + console.log("합격!"); +} else { + console.log("불합격."); +} \ No newline at end of file diff --git "a/week2/15 \352\270\260\354\247\200\355\230\204/4.\355\225\250\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \352\270\260\354\247\200\355\230\204/4.\355\225\250\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..b55e174 --- /dev/null +++ "b/week2/15 \352\270\260\354\247\200\355\230\204/4.\355\225\250\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,3 @@ +function add(x: number, y: number): number { + return x + y; +} \ No newline at end of file diff --git "a/week2/15 \352\270\260\354\247\200\355\230\204/reflection.md" "b/week2/15 \352\270\260\354\247\200\355\230\204/reflection.md" new file mode 100644 index 0000000..9d72100 --- /dev/null +++ "b/week2/15 \352\270\260\354\247\200\355\230\204/reflection.md" @@ -0,0 +1,17 @@ +A1. java의 var타입 같은 typescript의 any형에 대해 배울 수 있었다. +//typescript의 any : 모든 타입 허용, 런타임에도 타입 자유로움 +//java의 var : 타입 추론(명확한 타입이 존재.), 컴파일 시 타입이 고정됨. +//그러니까 안 쓰는 게 되도록 좋지. +라는 특징을 가지고 있다. + +또한 union형에 대해 배우며 이런 궁금증이 들었다. +그럼 하나의 변수에 두 개의 값을 저장할 수도 있는 건가? +->그건 아님. 걍 여러 타입 중 하나를 넣을 수 있다~ 이런 뜻. +뭐 입력을 받을 때 사람이 나이를 스물다섯. 쓸 수도 있꼬 25 쓸 수도 있는 그런 상황에서 잘 활용할 수 있을 것 같다. + +A2. int, float가 아닌 number로 자료형을 처리하는 것이 약간 헷갈렸다. +또한 기존에 겪어보지 못한 literal type이 처음에는 어색했다. + +A3. 게임 만들기에 아주 좋은 언어같다! 특히 다양한 자료형 중 literal type을 활용하면 게임용 변수를 +효과적으로 만들 수 있을 것 같다. 또한, 무슨 언어를 배우든, 결국 언어들의 뿌리는 비슷비슷하다는 생각이 들었다. +여러 언어를 접해보고, 그 중 한 언어를 정해서 진득하게 공부해보는 시간이 꼭 필요할 것 같다. diff --git "a/week2/15 \352\271\200\354\234\240\353\246\254/.gitkeep" "b/week2/15 \352\271\200\354\234\240\353\246\254/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week2/15 \352\271\200\354\234\240\353\246\254/arrayTuple.ts" "b/week2/15 \352\271\200\354\234\240\353\246\254/arrayTuple.ts" new file mode 100644 index 0000000..53f3d89 --- /dev/null +++ "b/week2/15 \352\271\200\354\234\240\353\246\254/arrayTuple.ts" @@ -0,0 +1,11 @@ +// 2-배열과튜플.ts +// 좋아하는 음식 이름 3개를 string 배열로 선언 +// `(학교이름, 학년)`을 담은 튜플 선언 + +let favFood : Array = ["떡볶이" , "닭갈비" , "토마토스프"]; +let schoolID : [string, number] = ["Ewha Womens' Univ.", 2]; +// Tuple = 고정된 길이 + 형태 다른 요소 + +console.log(favFood, schoolID); +// 출력 시 배열 형태 그대로 출력 + diff --git "a/week2/15 \352\271\200\354\234\240\353\246\254/condition.ts" "b/week2/15 \352\271\200\354\234\240\353\246\254/condition.ts" new file mode 100644 index 0000000..9ba5c6f --- /dev/null +++ "b/week2/15 \352\271\200\354\234\240\353\246\254/condition.ts" @@ -0,0 +1,14 @@ +// `3-조건문.ts` +// 점수(number)를 하나 선언하고 +// 60점 이상이면 "합격", 미만이면 "불합격"을 출력하는 if/else문 작성 + +let score : number = 75; +// 값 선언 + +if (score >= 60) { + console.log("Pass"); +} +else { + console.log("Fail"); +} +//if else 를 통해 조건에 따른 결과 출력 \ No newline at end of file diff --git "a/week2/15 \352\271\200\354\234\240\353\246\254/function.ts" "b/week2/15 \352\271\200\354\234\240\353\246\254/function.ts" new file mode 100644 index 0000000..8851456 --- /dev/null +++ "b/week2/15 \352\271\200\354\234\240\353\246\254/function.ts" @@ -0,0 +1,10 @@ +// `4-함수선언.ts` +// 두 숫자를 입력받아 합을 반환하는 함수 선언 (매개변수 타입, 반환 타입 명시) +// 함수를 호출해서 결과 출력 + +function addition(x: number, y: number) : number { + return x + y; +} +// function 함수 이름(변수: 타입, 변수: 타입) : return 타입 { 함수 content } + +console.log(addition(3,25)); \ No newline at end of file diff --git "a/week2/15 \352\271\200\354\234\240\353\246\254/reflection.md" "b/week2/15 \352\271\200\354\234\240\353\246\254/reflection.md" new file mode 100644 index 0000000..bc7c485 --- /dev/null +++ "b/week2/15 \352\271\200\354\234\240\353\246\254/reflection.md" @@ -0,0 +1,30 @@ +# week2 : Variable, Type, Condition and Function + +## 기본 개념 +1. 자료형 +-기본 타입 = string(문자열), number(정수, 실수), boolean(TF), null(값X), undefined, bigint, symbol(고유 식별자) +-배열, 튜플(길이 고정+형태 다른 요소) +-객체, any(아무거나 가능), unknown, union(복수 타입 가능), literal type(정해진 값만), enum(상수 그룹) +2. 조건문 +-조건식 결과는 무조건 T OR F +-타입 비교 틀리면 컴파일 에러 +3. 함수 선언 +-매개변수 타입 지정 필요 +-정해진 타입, 정해진 개수를 전달하지 않으면 에러 + +## 알게 된 점 +1. any(아무 타입이나 가능), unknown 같은 타입이 있음 +2. 조건식의 결과가 무조건 true OR false +3. 매개변수 타입은 지정 필수; BUT 반환 타입은 선택 + +## 헷갈렸던 점 +1. C나 Java와는 변수 선언 방식이 다르다 +2. 매개변수 타입도 반드시 지정해줘야 한다(무려 컴파일 단계에서 에러!) +3. +사소한 거지만 let으로 변수 선언한다(근데 직관적이어서 좋음) + +## 느낀 점 (Playground or StackBlitz 포함) +1. Playground는 온라인 에디팅이 간단해서 편리했다(설치 필요X) +2. 컴파일 단계에서 에러 내주는 것도 좋다(코드 확인하기 쉬움) +3. 다른 컴퓨터 언어와는 명령 체계 같은 것이 또 달라서 헷갈린다 +4. 사용자에게 직접 input 받는 것도 짜보고 싶어졌다! +5. 타입 체크를 any로 하면 함수 가독성이 떨어져서 문제인가요??? \ No newline at end of file diff --git "a/week2/15 \352\271\200\354\234\240\353\246\254/variable.ts" "b/week2/15 \352\271\200\354\234\240\353\246\254/variable.ts" new file mode 100644 index 0000000..057fe16 --- /dev/null +++ "b/week2/15 \352\271\200\354\234\240\353\246\254/variable.ts" @@ -0,0 +1,11 @@ +// 1-변수선언.ts +// 이름(string), 나이(number), 합격여부(boolean) 변수를 타입을 명시하여 선언 +//`console.log`로 출력해보기 + +let firstname : string = "youri"; +let age : number = 23; +let pf : boolean = true; +// (변수 이름) : (타입) = (값); 꼴 + +console.log(firstname, age, pf); +// console.log()를 통해 출력 \ No newline at end of file diff --git "a/week2/15 \352\271\200\355\230\234\354\233\220/.gitkeep" "b/week2/15 \352\271\200\355\230\234\354\233\220/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week2/15 \352\271\200\355\230\234\354\233\220/1-\353\263\200\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \352\271\200\355\230\234\354\233\220/1-\353\263\200\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..2adb3ca --- /dev/null +++ "b/week2/15 \352\271\200\355\230\234\354\233\220/1-\353\263\200\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,7 @@ +let 이름: string = "혜원킴"; +let 나이: number = 20; +let 합격여부: boolean = true; + +console.log(이름); +console.log(나이); +console.log(합격여부); \ No newline at end of file diff --git "a/week2/15 \352\271\200\355\230\234\354\233\220/2-\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" "b/week2/15 \352\271\200\355\230\234\354\233\220/2-\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" new file mode 100644 index 0000000..ac71e58 --- /dev/null +++ "b/week2/15 \352\271\200\355\230\234\354\233\220/2-\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" @@ -0,0 +1,5 @@ +let favoriteFoods: Array = ["복숭아", "키위", "스파게티"]; +let univ: [string, number] = ["Ewha Womans University", 25]; + +console.log(favoriteFoods); +console.log(univ); \ No newline at end of file diff --git "a/week2/15 \352\271\200\355\230\234\354\233\220/3-\354\241\260\352\261\264\353\254\270.ts" "b/week2/15 \352\271\200\355\230\234\354\233\220/3-\354\241\260\352\261\264\353\254\270.ts" new file mode 100644 index 0000000..49a3e6d --- /dev/null +++ "b/week2/15 \352\271\200\355\230\234\354\233\220/3-\354\241\260\352\261\264\353\254\270.ts" @@ -0,0 +1,9 @@ +// 점수 입력받기 +let score: number = prompt("점수를 입력하세요:"); + +// 60점 넘었는지 확인 +if (score > 60) { + console.log("합격"); +} else { + console.log("불합격"); +} \ No newline at end of file diff --git "a/week2/15 \352\271\200\355\230\234\354\233\220/4-\355\225\250\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \352\271\200\355\230\234\354\233\220/4-\355\225\250\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..1266b89 --- /dev/null +++ "b/week2/15 \352\271\200\355\230\234\354\233\220/4-\355\225\250\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,18 @@ +// 숫자 2개 입력받기 +let num1: number = Number(prompt("첫 번째 숫자를 입력하세요:")); +let num2: number = Number(prompt("두 번째 숫자를 입력하세요:")); + +/** +* number 2개의 합 구하기 +* +* @param x 첫 번째 숫자 +* @param y 두 번째 숫자 +* @returns 두 숫자의 합 +*/ +//저번 세션에서 배웠던 주석규칙을 활용해 보았습니당. 이거 맞나요? +function add(x: number, y: number): number { + return x + y; +} + +// num1+num2 값을 콘솔에 출력 +console.log(add(num1, num2)); \ No newline at end of file diff --git "a/week2/15 \352\271\200\355\230\234\354\233\220/week2.md" "b/week2/15 \352\271\200\355\230\234\354\233\220/week2.md" new file mode 100644 index 0000000..90f9d48 --- /dev/null +++ "b/week2/15 \352\271\200\355\230\234\354\233\220/week2.md" @@ -0,0 +1,54 @@ +# Week 2: 변수, 타입, 조건문, 함수 + +## 새로 알게 된 개념 +1. TypeScript Playground 에디터로 웹에서 코드 작성 및 실행이 가능하다. +2. TS는 변수 선언 시 타입을 같이 명시할 수 있다. + let 변수명: 자료형 = 값; + *타입을 쓰지 않아도 타입 추론 기능이 있어서 초기 값이 있다면 그 값에 따라 변수 타입을 자동으로 유추해 준다. + +**배열 선언** + let 숫자들: number[] = [1, 2, 3]; + let 문자열들: Array = ["a", "b", "c"]; +**튜플 선언** + let 좌표: [number, string] = [123, "px"]; +**unknown** + 선언 시 아무 타입이든 상관없음, 타입 체크를 하면 사용할 수 있음 +**union** + 복수 타입 허용 + let ID: string | number; +**literal type** + 정해진 값만 허용 + let 방향: "left" | "right" | "up" | "down"; + 방향 = "left"; // 가능 + + +## 헷갈렸던 부분 +**enum 타입을 어떤 상황에 쓰는 건지 감이 잘 안 온다.** +--> 찾아보니 서로 관련 있는 상수들의 집합을 정의할 때 사용한다고 한다. + +ex) GameState라는 자료형을 하나 더 만드는 느낌인 듯 하다! + enum GameState { + Start, + Playing, + GameOver + } + let state: GameState = GameState.Playing; + +**TS에서는 변수명을 한글로도 쓸 수 있는 것인가??** +앗... 생각해 보니 파이썬도 가능했던 것 같다. +--> 찾아보니 C, C++ 말고는 거의 다 가능하다고 한다. (+ TS는 JS 기반이어서 유니코드 완전 지원) + +**unknown, any는 언제 쓸까?** +입력받을 값의 타입이 확실하지 않을 때 쓰는 걸까? + + + +## Playground 사용해 본 소감 +뭔가를 추가로 깔아야 하는 게 귀찮아서 항상 코랩 replit 주피터 등등 웹 에디터를 애용했던 사람으로서... 편하고 좋아요! + + +## 느낀 점 +1. 명석한 1학년둥이로 봐 주셔서 감사합니다 기대를 충족시키기 위해 분발할게요 노력할게요 아자아자 +2. JS를 정말 오랜만에 봐서 반가웠다. 변수 선언할 때 데이터형을 지정할 필요가 없다는 것도 까먹고 있었다가 오늘 다시 알았다. +3. 읽는 내내 JS에 타입을 지정하고 확인하는 절차가 빠져 있는 게 너무너무 의아하고 신기했다. +(+ 질문&답 부분 넘 유익해용) \ No newline at end of file diff --git "a/week2/15 \353\260\225\354\204\234\354\230\201/.gitkeep" "b/week2/15 \353\260\225\354\204\234\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week2/15 \353\260\225\354\204\234\354\230\201/reflection.md" "b/week2/15 \353\260\225\354\204\234\354\230\201/reflection.md" new file mode 100644 index 0000000..e2b232a --- /dev/null +++ "b/week2/15 \353\260\225\354\204\234\354\230\201/reflection.md" @@ -0,0 +1,29 @@ +# Week2. TypeScript 기초 (변수타입, 조건문, 함수) + +## 1. 새로 알게된 점 +- 기본 타입: string, number(정수, 실수), boolean (참, 거짓), null, undefined, bigint, symbol (number로 정수/실수 구분 없이 사용하는게 새로운 느낌..?) +- 튜플(tuple): 고정된 길이 + **각 요소의 타입이 다른 배열** + let t : [number, string, string]; //이렇게 하나의 배열안에서 타입이 다른 자료형 +- any: 아무 타입이든 허용 (지양할 것) +- union: 복수타입허용 + let u : string | number; //복수 타입을 가질 수 있는게 신기한 느낌..? +- 객체-객체 선언 방식이 달라서 헷갈릴 것 같은 느낌..? (은근히 비슷한 것도 있지만) + let 학생 {이름: string, 나이: number} = { + 이름: "이화", + 나이: 20, + }; +- 조건문 = C언어/자바와 상당히 비슷한 느낌! + +- 함수 = 큰 틀은 비슷하지만, **타입 선언** (매개변수, 반환타입) 등이 다른 느낌 + function f1 (type1 : number, type2: string) : string{ + return type2 + ":" + type1; + } +-출력문: console.log("출력할 내용");의 형식으로 + +## 2. 실습 중 헷갈렸던 부분 +- 타입 선언하는 방식이 평소에는 (타입) (변수명) 순서였는데, 순서도 달라지면 :(콜론)을 사용하는게 익숙하지 않아 헷갈렸던 것 같습니다! +- 튜플이라는 타입을 처음 제대로 접해 선언이나 사용에 신경 쓴 점? +- 함수의 반환 타입 명시가 매개변수 뒤에 :(콜론)을 사용해서 진행하는걸 계속 까먹게 되었다... + +## 3. Playground 사용 소감 +기존 IDE에서 사용하는 다른 방법이 궁금해서, vscode에서 typescript 작성후에 실행시켜보기도 했는데, 이 경우 추가적으로 다른 것들을 설치하지 않으면, node.js 설치 후 터미널 명령어를 통해 ts를 컴파일, js로 실행시켜야하는 번거로움이 존재했습니다.. 결론적으로 그냥 코드만 타이핑해서 실행시켜볼 수 있는 Playground의 편리성을 느끼게되었습니다! \ No newline at end of file diff --git "a/week2/15 \353\260\225\354\204\234\354\230\201/\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" "b/week2/15 \353\260\225\354\204\234\354\230\201/\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" new file mode 100644 index 0000000..f24bd01 --- /dev/null +++ "b/week2/15 \353\260\225\354\204\234\354\230\201/\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" @@ -0,0 +1,12 @@ +//2. 배열과튜플: 좋아하는 음식 3개 string 배열로 선언 +// (학교이름, 학년)을 담은 튜플 선언 + +//배열 number[] 또는 Array +let 좋아하는음식 : Array = ["샌드위치", "떡볶이", "파스타"]; + +//튜플: 고정된 길이+각 요소의 타입이 달라도 됨 +// : [string, number, boolean] 이래도 되는 것 +let 정보 : [string, number] = ["이화여자대학교", 22]; + +console.log(좋아하는음식); +console.log(정보); \ No newline at end of file diff --git "a/week2/15 \353\260\225\354\204\234\354\230\201/\353\263\200\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \353\260\225\354\204\234\354\230\201/\353\263\200\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..5f6022b --- /dev/null +++ "b/week2/15 \353\260\225\354\204\234\354\230\201/\353\263\200\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,11 @@ +//1.변수선언: 이름, 나이, 합격여부 변수 타입 명시해서 선언하기 + +//타입: string-문자열, number -정수/실수, boolean-참/거짓 +let 이름 : string = '박서영'; +let 나이 : number = 22; +let 합격여부 : boolean = true; + +//console.log -> print같은 느낌? +console.log(이름); +console.log(나이); +console.log(합격여부); \ No newline at end of file diff --git "a/week2/15 \353\260\225\354\204\234\354\230\201/\354\241\260\352\261\264\353\254\270.ts" "b/week2/15 \353\260\225\354\204\234\354\230\201/\354\241\260\352\261\264\353\254\270.ts" new file mode 100644 index 0000000..eb93903 --- /dev/null +++ "b/week2/15 \353\260\225\354\204\234\354\230\201/\354\241\260\352\261\264\353\254\270.ts" @@ -0,0 +1,11 @@ +//3. 조건문: 점수 선언, 60점 이상이면 합격, 미만이면 불합격 + +let 점수 : number = 30; + +//조건문은 다른 문법들과 유사 +if(점수 >= 60){ + console.log("합격"); +} +else{ + console.log("불합격"); +} \ No newline at end of file diff --git "a/week2/15 \353\260\225\354\204\234\354\230\201/\355\225\250\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \353\260\225\354\204\234\354\230\201/\355\225\250\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..84c1f7a --- /dev/null +++ "b/week2/15 \353\260\225\354\204\234\354\230\201/\355\225\250\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,11 @@ +//4.함수선언: 두 숫자를 입력받아 합을 반환. + +//함수: 매개변수에 타입 명시, 함수 반환타입 명시 +function addition (x: number, y: number) : number { + return x+y; +} + +let a : number = 10; +let b : number = 20; + +console.log(addition(a,b)); //함수 호출 diff --git "a/week2/15 \353\260\225\354\261\204\354\234\244/.DS_Store" "b/week2/15 \353\260\225\354\261\204\354\234\244/.DS_Store" new file mode 100644 index 0000000..d82875b Binary files /dev/null and "b/week2/15 \353\260\225\354\261\204\354\234\244/.DS_Store" differ diff --git "a/week2/15 \353\260\225\354\261\204\354\234\244/.gitkeep" "b/week2/15 \353\260\225\354\261\204\354\234\244/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week2/15 \353\260\225\354\261\204\354\234\244/reflection.md" "b/week2/15 \353\260\225\354\261\204\354\234\244/reflection.md" new file mode 100644 index 0000000..9cf660b --- /dev/null +++ "b/week2/15 \353\260\225\354\261\204\354\234\244/reflection.md" @@ -0,0 +1,26 @@ +# week2 : Variable, Type, Condition and Function + +## 기본 개념 +1. 자료형 +-bigint, number, undefined, symbol 등 새로운 타입을 알게됨 +-배열, 튜플: 길이가 고정 +-unknown, any, union, literal type +2. 조건문 +-형태 자체는 파이썬과 유사하다고 느껴짐 +-결과값이 무조건 T 혹은 F +3. 함수 선언 +-매개변수 지정 + +## 알게 된 점 +1. 위의 자료형에서 정리한 모든 타입들과 그 의미를 처음 알게됨 +2. 기존에 알고 있던 유일한...언어인 파이썬과 유사한 문법을 공유하면서도 타입이나 모양에서 차이를 보임 + +## 헷갈렸던 점 +1. 조건문에서 타입을 맞춰주어야함 +2. 습관적으로 파이썬에서 쓰던 자료형을 선언함 + +## 느낀 점 (Playground or StackBlitz 포함) +1. 생각보다는 변수 선언이나 조건문이 파이썬과 비슷해서 어렵지는 않았다. +2. 그러나 잔실수로 인한 에러가 많아서 주의가 필요하다. +3. 명령어가 조금씩 달라 잘 기억해야 할 것 같다. + diff --git "a/week2/15 \353\260\225\354\261\204\354\234\244/\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" "b/week2/15 \353\260\225\354\261\204\354\234\244/\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" new file mode 100644 index 0000000..9b0b82a --- /dev/null +++ "b/week2/15 \353\260\225\354\261\204\354\234\244/\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" @@ -0,0 +1,7 @@ +//배열: 좋아하는 음식 출력 +let food : string[] = ["초밥", "샌드위치", "밀크티"]; +console.log(food) + +//튜플 선언: 학교, 학년 출력 +let studID : [string, number] = ["이화여자대학교", 1]; //학교, 학년 +console.log(studID) \ No newline at end of file diff --git "a/week2/15 \353\260\225\354\261\204\354\234\244/\353\263\200\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \353\260\225\354\261\204\354\234\244/\353\263\200\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..9e0b80f --- /dev/null +++ "b/week2/15 \353\260\225\354\261\204\354\234\244/\353\263\200\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,7 @@ +let 이름 : string = '박채윤'; +let 나이 : number = 23; +let 합격여부 : boolean = true; + +console.log("이름은 " + 이름); +console.log("나이는 " + 나이); +console.log("합격여부는 " + 합격여부); \ No newline at end of file diff --git "a/week2/15 \353\260\225\354\261\204\354\234\244/\354\241\260\352\261\264\353\254\270.ts" "b/week2/15 \353\260\225\354\261\204\354\234\244/\354\241\260\352\261\264\353\254\270.ts" new file mode 100644 index 0000000..4261d7c --- /dev/null +++ "b/week2/15 \353\260\225\354\261\204\354\234\244/\354\241\260\352\261\264\353\254\270.ts" @@ -0,0 +1,9 @@ +//점수 선언 +let score : number = 25; + +//조건문 +if (score >= 60) { + console.log("합격"); +} else { + console.log("불합격"); +} \ No newline at end of file diff --git "a/week2/15 \353\260\225\354\261\204\354\234\244/\355\225\250\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \353\260\225\354\261\204\354\234\244/\355\225\250\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..2629a1e --- /dev/null +++ "b/week2/15 \353\260\225\354\261\204\354\234\244/\355\225\250\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,3 @@ +function add(a: number, b: number): number { + return a + b; + } \ No newline at end of file diff --git "a/week2/15 \354\240\225\354\234\244\354\225\204/.gitkeep" "b/week2/15 \354\240\225\354\234\244\354\225\204/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week2/15 \354\240\225\354\234\244\354\225\204/reflection.md" "b/week2/15 \354\240\225\354\234\244\354\225\204/reflection.md" new file mode 100644 index 0000000..c0559c2 --- /dev/null +++ "b/week2/15 \354\240\225\354\234\244\354\225\204/reflection.md" @@ -0,0 +1,8 @@ +이번 주 학습에서 새롭게 알게 된 점은 무엇인가요? +- any, unknwon, union, enum을 다루어보았다 + +실습하면서 헷갈렸던 부분이 있었나요? +-세미콜론을 안 찍는 게 어색했다. 자바스크립트를 안 써본 게 오히려 배울 때 안 헷갈리고 좋은 것 같음. + +Playground(또는 StackBlitz) 사용해본 소감은 어떤가요? +-편리해서 좋았다. 코랩이 생각났다. \ No newline at end of file diff --git "a/week2/15 \354\240\225\354\234\244\354\225\204/\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" "b/week2/15 \354\240\225\354\234\244\354\225\204/\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" new file mode 100644 index 0000000..30f065b --- /dev/null +++ "b/week2/15 \354\240\225\354\234\244\354\225\204/\353\260\260\354\227\264\352\263\274\355\212\234\355\224\214.ts" @@ -0,0 +1,6 @@ +let 숫자들: number[] = [1, 2, 3, 4, 5]; +let 문자들: string[] = ["안녕", "반가워"]; +let 부울들: boolean[] = [true, false]; +let 튜플: [number, string] = [1, "안녕"]; +let 튜플2: [number, string, boolean] = [1, "안녕", true]; +let 튜플3: [number, string, boolean] = [1, "안녕", true]; \ No newline at end of file diff --git "a/week2/15 \354\240\225\354\234\244\354\225\204/\353\263\200\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \354\240\225\354\234\244\354\225\204/\353\263\200\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..0b3f5a8 --- /dev/null +++ "b/week2/15 \354\240\225\354\234\244\354\225\204/\353\263\200\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,19 @@ +let 이름: string = "정윤아"; +let 나이: number = 23; +let 생일: number = 1216; +let 뭐든지: any = "문자열"; +뭐든지 = 123; + +let 입력값: unknown = "문자열"; +let ID: string | number; +ID = "user123"; +ID = 456; +let 방향: "left" | "right" | "up" | "down"; +방향 = "left"; + +enum 요일 { + 월 = "Mon", + 화 = "Tue" +} + +let 오늘: 요일 = 요일.월; diff --git "a/week2/15 \354\240\225\354\234\244\354\225\204/\354\241\260\352\261\264\353\254\270.ts" "b/week2/15 \354\240\225\354\234\244\354\225\204/\354\241\260\352\261\264\353\254\270.ts" new file mode 100644 index 0000000..a3d5a81 --- /dev/null +++ "b/week2/15 \354\240\225\354\234\244\354\225\204/\354\241\260\352\261\264\353\254\270.ts" @@ -0,0 +1,8 @@ +let score: number = 85; + +if (score>=60) { + console.log("합격입니다."); +} else { + console.log("불합격입니다."); +} + diff --git "a/week2/15 \354\240\225\354\234\244\354\225\204/\355\225\250\354\210\230\354\204\240\354\226\270.ts" "b/week2/15 \354\240\225\354\234\244\354\225\204/\355\225\250\354\210\230\354\204\240\354\226\270.ts" new file mode 100644 index 0000000..07bdc78 --- /dev/null +++ "b/week2/15 \354\240\225\354\234\244\354\225\204/\355\225\250\354\210\230\354\204\240\354\226\270.ts" @@ -0,0 +1,6 @@ +function add(x: number, y: number): number { + return x+y; +} + +let result = add(10, 20); +console.log(result); diff --git a/week3/.DS_Store b/week3/.DS_Store new file mode 100644 index 0000000..c980f48 Binary files /dev/null and b/week3/.DS_Store differ diff --git "a/week3/15 \352\263\265\354\204\270\354\230\201/.gitkeep" "b/week3/15 \352\263\265\354\204\270\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week3/15 \352\263\265\354\204\270\354\230\201/1-object.ts" "b/week3/15 \352\263\265\354\204\270\354\230\201/1-object.ts" new file mode 100644 index 0000000..d2a0864 --- /dev/null +++ "b/week3/15 \352\263\265\354\204\270\354\230\201/1-object.ts" @@ -0,0 +1,18 @@ +// person 객체 생성. 이름, 나이, 이메일의 타입은 명시하지 않았음. +let person = { + name: "공세영", + age: 22, + email: "skong302@ewhain.net" +}; + +// 정보를 출력하기 위해 함수 생성. +// 이름, 나이, 이메일 타입을 정의하고 출력하게 함. +function printInfo(p: { name: string; age: number; email: string; }) { + console.log(`이름: ${p.name}`); + console.log(`나이: ${p.age}세`); + if (p.email !== undefined) { + console.log(`이메일: ${p.email}`); + } +} + +printInfo(person); \ No newline at end of file diff --git "a/week3/15 \352\263\265\354\204\270\354\230\201/2-interface.ts" "b/week3/15 \352\263\265\354\204\270\354\230\201/2-interface.ts" new file mode 100644 index 0000000..b60ada1 --- /dev/null +++ "b/week3/15 \352\263\265\354\204\270\354\230\201/2-interface.ts" @@ -0,0 +1,30 @@ +// 프로덕트 각 변수 타입 정의와 함께 인터페이스 작성 +interface Product { + name: string, + price: number, + description?: string +}; + +// 상품 정보 출력 함수 정의. Product 인터페이스에 부합하는 객체만 p로 인정. +// description이 있을 때만 if로 받아와서 출력. +function printProduct(p:Product){ + console.log(p.name); + console.log(p.price); + if(p.description) console.log(p.description); +}; + +// 상품 정보가 있는 예제 +const product1: Product = { + name: "딸기", + price: 10000, + description: "딸기입니다." +}; + +// 상품 정보가 없는 예제 +const product2: Product = { + name: "황금 바나나", + price: 999999999, +}; + +printProduct(product1); +printProduct(product2); \ No newline at end of file diff --git "a/week3/15 \352\263\265\354\204\270\354\230\201/3-class.ts" "b/week3/15 \352\263\265\354\204\270\354\230\201/3-class.ts" new file mode 100644 index 0000000..91a22e3 --- /dev/null +++ "b/week3/15 \352\263\265\354\204\270\354\230\201/3-class.ts" @@ -0,0 +1,24 @@ +class Animal{ + name: string; + + constructor(name:string){ + this.name = name; + } + + move(distance: number){ + console.log(distance + "미터 만큼 이동."); + } +}; + + +class Dog extends Animal{ + bark(){ + console.log("Woof!"); + } +} + + +const pup = new Dog("pup"); + +pup.bark(); +pup.move(30); \ No newline at end of file diff --git "a/week3/15 \352\263\265\354\204\270\354\230\201/4-challenge.ts" "b/week3/15 \352\263\265\354\204\270\354\230\201/4-challenge.ts" new file mode 100644 index 0000000..ef5e137 --- /dev/null +++ "b/week3/15 \352\263\265\354\204\270\354\230\201/4-challenge.ts" @@ -0,0 +1,36 @@ +interface Shape{ + getArea():number +}; + + +class Rect implements Shape{ + width: number; + height: number; + + constructor(width: number, height: number){ + this.width = width; + this.height = height; + } + + getArea(){ + return this.width*this.height; + } +}; + +class Circle implements Shape{ + radius: number; + + constructor(radius: number){ + this.radius = radius; + } + + getArea(){ + return 3.14*(this.radius^2) + } +}; + +const rect = new Rect(10, 10); +const circle = new Circle(10); + +console.log(rect.getArea()); +console.log(circle.getArea()); \ No newline at end of file diff --git "a/week3/15 \352\263\265\354\204\270\354\230\201/reflection.md" "b/week3/15 \352\263\265\354\204\270\354\230\201/reflection.md" new file mode 100644 index 0000000..e7b8439 --- /dev/null +++ "b/week3/15 \352\263\265\354\204\270\354\230\201/reflection.md" @@ -0,0 +1,20 @@ +다음 질문에 짧게 답해주세요: + +1. 이번 주 학습에서 새로 알게 된 점은 무엇인가요? +템플릿 리터럴에서 ${...} 구문을 사용하려면 작은따옴표(')나 큰따옴표(")가 아닌 **백틱()** 을 써야 한다는 걸 알게 되었다. +또한 const`로 선언한 객체는 속성은 수정할 수 있지만 전체 재할당은 불가능하다는 점도 새롭게 알았다. + + +2. 헷갈렸거나 어려웠던 부분은 무엇인가요? +this 키워드의 동작 방식과, 클래스 내부에서 상위 클래스(super class)의 생성자나 메서드를 호출할 때의 문법이 종종 헷갈렸음.... +this는 상황에 따라 참조 대상이 달라질 수 있어서, 함수 문맥과 클래스 문맥을 잘 구분해야 한다는 점이 정신 안 차리면 오류 뜨게 만드는 주 원인. +any 타입 오류가 자꾸만 반겨줌. +그리고 자바처럼 Dog dog = new Dog("개이름"); 같이 썼다가 한참 고민함. +변수: 타입 형태로 써야 한다............... +let dog: Dog = new Dog();나 const dog = new Dog(); 이건 타입 추론. + +3. (선택) 심화 과제를 해본 소감은 어떤가요? +자프실 생각나서 재밌었음. +그때 배운 객체 지향 개념들이 TypeScript에서도 그대로 적용된다는 점이 흥미로웠다. +시험 문제로 자바에서 사용하는 8개 타입은 무엇일까요. 같은 질문도 나왔었는데... +number만 있으면 byte, short, int 같은 거 몰라도 된다니 편하다~ \ No newline at end of file diff --git "a/week3/15 \352\263\265\354\204\270\354\230\201/summary.md" "b/week3/15 \352\263\265\354\204\270\354\230\201/summary.md" new file mode 100644 index 0000000..eb3927c --- /dev/null +++ "b/week3/15 \352\263\265\354\204\270\354\230\201/summary.md" @@ -0,0 +1,151 @@ +# WEEK 03 : TypeScript 문법 개념 이해 + +## 배운 것 요약 + +01. 객체 / Object +>> 속성(property)들의 집함. 사용자 지정으로 여러 값을 key-value 형태로 저장할 때 사용. +>> 데이터와 기능은 객체로 구현. +>> 객체도 타입이 있음 = object type +>> 컴파일 시 속성 및 타입 검사를 위해 + +``` +// 캐릭터의 위치 정보를 출력하는 함수 +function printPosition(pos: {x: number; y: number}) { + console.log(`캐릭터의 위치 - X: ${pos.x}, Y: ${pos.y}`); +} +printPosition({x:120, y:340}); // 실제 사용 - 캐릭터의 위치를 출력한다. +``` + + +02. 선택적 속성(Optional Properties) +>> 이 속성은 있을 수도, 없을 수도 있습니다. +>> 물음표(?)를 이용해 표시한다. + +``` +function printWeaponInfo(weapon: { name: string; upgradeLevel?: number }) { + console.log(`무기 이름: ${weapon.name}`); + if (weapon.upgradeLevel !== undefined) { // 속성이 없을 경우를 고려해서 코드를 작성할 것 + console.log(`강화 레벨: +${weapon.upgradeLevel}`); + } +} + +// 실제 사용 - 입력에 따라 강화 레벨 출력 여부가 다름. +printWeaponInfo({ name: "핸드건" }); +// 출력: 무기 이름: 핸드건 + +printWeaponInfo({ name: "매그넘", upgradeLevel: 3 }); +// 출력: 무기 이름: 매그넘, 강화 레벨: +3 +``` + + + +03. 객체 타입 명시 vs 타입 추론 +>> 타입을 직접 선언하느냐, TypeScript에게 초기값 기반 타입 판단을 맡기느냐. + +``` +// 타입 추론의 경우 +let player = { + name: "Leon", + level: 10, + weapon: "Matilda" +}; +``` + + + +04. 인터페이스(Interface) +>> 객체의 구조/모양을 정의하는 방법 +>> 일종의 규약. 컴파일 타임 타입 검사 용도로 사용. +>> 객체의 형태만 맞으면 인터페이스로 간주 - 구조적 타이핑(structural typing) +>> implement 선언도 필요 없다. 헉. 진짜냐. + +``` +interface Character { + name: string; + weapon?: string; +} + +function introduce(c: Character) { // 타입이 Character인 변수 c를 이용해 함수를 정의 + console.log(`이름: ${c.name}`); + if (c.weapon) console.log(`무기: ${c.weapon}`); +} + +introduce({ name: "Leon", weapon: "Matilda" }); +introduce({ name: "Claire" }); +``` + + + +05. 클래스(Class) +>> 객체를 생성하기 위한 청사진 +>> 필드(속성)와 메소드(기능)을 정의하고, constructor로 초기화. +>> 이것으로 객체를 반복해서 찍어낼 수 있다. +>> this.어쩌구는 현재 인스턴트를 가리킴. + +``` +class Character { + name: string; + + constructor(name: string) { + this.name = name; + } + + attack() { + return `${this.name}이(가) 공격했다!`; + } +} + +const leon = new Character("Leon"); +console.log(leon.attack()); // Leon이(가) 공격했다! +``` + + + +06. 상속(extends)과 구조적 가이드/제약?(implements) +>> 상속이란? super class의 필드 및 메소드를 물려받아 사용할 수 있다. +>> implements를 사용하면? 클래스가 인터페이스에 적힌 구조를 따르게 강제함. + +``` +// 무기 인터페이스: 모든 무기는 이름과 공격 메서드를 가져야 함 +interface Weapon { + name: string; + attack(): void; +} + +// 총기 클래스: Weapon 인터페이스를 구현 +class Gun implements Weapon { + name: string; + + constructor(name: string) { + this.name = name; + } + + attack() { + console.log(`${this.name}에서 탄환을 발사합니다.`); + } +} + +// 칼 클래스: Weapon 인터페이스를 구현 +class Knife implements Weapon { + name: string; + + constructor(name: string) { + this.name = name; + } + + attack() { + console.log(`${this.name}로 적을 벱니다.`); + } +} + +function useWeapon(w: Weapon) { + console.log(`플레이어가 ${w.name}을(를) 사용합니다.`); + w.attack(); +} + +const matilda = new Gun("Matilda"); +const combatKnife = new Knife("Combat Knife"); + +useWeapon(matilda); // Matilda에서 탄환을 발사합니다. +useWeapon(combatKnife); // Combat Knife로 적을 벱니다. +``` \ No newline at end of file diff --git "a/week3/15 \352\270\260\354\247\200\355\230\204/.gitkeep" "b/week3/15 \352\270\260\354\247\200\355\230\204/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week3/15 \352\270\260\354\247\200\355\230\204/1-object.ts" "b/week3/15 \352\270\260\354\247\200\355\230\204/1-object.ts" new file mode 100644 index 0000000..1f8b702 --- /dev/null +++ "b/week3/15 \352\270\260\354\247\200\355\230\204/1-object.ts" @@ -0,0 +1,11 @@ +let person = { + name : "지현", + age : 23, + email : "micekj0904@ewhain.net" +}; + +console.log(`이름: ${person.name}`); +console.log(`나이: ${person.age}`); +if (person.email) { + console.log(`이메일: ${person.email}`); +} diff --git "a/week3/15 \352\270\260\354\247\200\355\230\204/2-interface.ts" "b/week3/15 \352\270\260\354\247\200\355\230\204/2-interface.ts" new file mode 100644 index 0000000..b23f1fd --- /dev/null +++ "b/week3/15 \352\270\260\354\247\200\355\230\204/2-interface.ts" @@ -0,0 +1,26 @@ +//인터페이스 생성 +interface Product { + name : string; + price : number; + description? : string; +} + +//프린트함수 +function printProduct(p : Product) : void{ + console.log(`name : ${p.name}`); + console.log(`price : ${p.price}`); + if(p.description){ + console.log(`description : ${p.description}`); + } + console.log("\n"); +} + +//물건1 : descriptoin 있음 +const p1 : Product = {name : "물건1", price : 10000, description : "아이들이 선호함."}; + +//물건2 : description 없음 +const p2 : Product = {name : "물건2", price : 50000}; + +//물건 1, 2 차례대로 출력 +printProduct(p1); +printProduct(p2); \ No newline at end of file diff --git "a/week3/15 \352\270\260\354\247\200\355\230\204/3-class.ts" "b/week3/15 \352\270\260\354\247\200\355\230\204/3-class.ts" new file mode 100644 index 0000000..1f19263 --- /dev/null +++ "b/week3/15 \352\270\260\354\247\200\355\230\204/3-class.ts" @@ -0,0 +1,17 @@ +class Animal{ + name : string; + + move(distance: number){ + console.log('이동 거리는 ' + distance); + } +} + +class Dog extends Animal { + bark(){ + console.log("Woof! Woof!"); + } +} + +const dog = new Dog(); +dog.bark(); +dog.move(10); \ No newline at end of file diff --git "a/week3/15 \352\270\260\354\247\200\355\230\204/reflection.md" "b/week3/15 \352\270\260\354\247\200\355\230\204/reflection.md" new file mode 100644 index 0000000..1349039 --- /dev/null +++ "b/week3/15 \352\270\260\354\247\200\355\230\204/reflection.md" @@ -0,0 +1,6 @@ +회고록 +1.이번 주 학습에서 새로 알게 된 점은 무엇인가요? +TypeScript에서는 꼭 존재하지 않아도 되는 속성을 '?'로 표현할 수 있게 해두었다는 것을 새롭게 알 수 있었다. if문으로 처리하는 것보다 효율적인 방법이라는 생각을 했다. 꼭 TypeScript에서뿐 아니라, 이처럼 여러 언어에서 조금만 찾아보면 편리한 기능을 제공하는 것을 자주 볼 수 있는데 내가 평소에 쓰는 방법 외에 더 좋은 방법이 있는지를 종종 찾아봐야겠다는 생각을 할 수 있었다. + +2. 헷갈렸거나 어려웠던 부분은 무엇인가요? +이번 주에는 크게 헷갈린다고 생각되는 부분은 없었다. \ No newline at end of file diff --git "a/week3/15 \352\271\200\354\234\240\353\246\254/.gitkeep" "b/week3/15 \352\271\200\354\234\240\353\246\254/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week3/15 \352\271\200\354\234\240\353\246\254/1-object.ts" "b/week3/15 \352\271\200\354\234\240\353\246\254/1-object.ts" new file mode 100644 index 0000000..b870ccb --- /dev/null +++ "b/week3/15 \352\271\200\354\234\240\353\246\254/1-object.ts" @@ -0,0 +1,27 @@ + +// 1. 객체 실습 +// `Person` 객체를 선언하고, 이름(`name`: string), 나이(`age`: number), 이메일(`email`: string, 선택적)을 속성으로 가진다. +// 콘솔에 이름과 나이를 출력한다. 이메일이 있다면 이메일도 출력한다. + + +//객체를 선언 +//타입을 객체에 직접 지정 +let person : {name : string; age : number; email? : string }= { + //이때 email 뒤에 ?를 붙여 선택적 속성으로 + name : "youri", + age : 23, + email : "2376064@ewhain.ac.kr" +}; + +console.log("Your name is " + person.name); +console.log("Your age is " + person.age); +if (person.email !== undefined) { + //if문을 통해 email이 있을 경우(undefined가 아닌) 출력 + console.log("Your email address is " + person.email); +} + +/* +[LOG]: "Your name is youri" +[LOG]: "Your age is 23" +[LOG]: "Your email address is 2376064@ewhain.ac.kr" +*/ \ No newline at end of file diff --git "a/week3/15 \352\271\200\354\234\240\353\246\254/2-interface.ts" "b/week3/15 \352\271\200\354\234\240\353\246\254/2-interface.ts" new file mode 100644 index 0000000..e69de29 diff --git "a/week3/15 \352\271\200\354\234\240\353\246\254/3-class.ts" "b/week3/15 \352\271\200\354\234\240\353\246\254/3-class.ts" new file mode 100644 index 0000000..d13b44c --- /dev/null +++ "b/week3/15 \352\271\200\354\234\240\353\246\254/3-class.ts" @@ -0,0 +1,53 @@ + +// 3. 클래스 실습 +// `Animal` 클래스를 정의한다. + + //속성: `name`: string + + //메서드: `move(distance: number)` → `console.log`로 이동 거리 출력 + +// `Dog` 클래스를 `Animal`에서 상속받아 + + //`bark()` 메서드(`console.log("Woof!")`)를 추가한다. + +// `Dog` 인스턴스를 만들어 `bark()`와 `move()`를 호출해본다 + + + +//Animal 클래스 정의 +class Animal { + //instance 속성 선언 + name : string; + + //외부의 name 값을 this.name에 저장 + constructor(name : string) { + this.name = name; + } + + //move 메서드에 distance 매개변수 + move(distance : number = 0) { //default 0 + console.log(this.name + " moved " + distance + " miles."); + } +} + + +//extends로 상속 +class Dog extends Animal { + //Dog만의 메서드 bark 추가 + bark() { + console.log("Woof!"); + } +} + +//Dog의 인스턴스 생성 +const dog = new Dog("Nero"); +//이때 this.name에 Nero 들어감 + +//호출 +dog.bark(); +dog.move(); + +/* +[LOG]: "Woof!" +[LOG]: "Nero moved 0 miles." +*/ \ No newline at end of file diff --git "a/week3/15 \352\271\200\354\234\240\353\246\254/4-challenge.ts" "b/week3/15 \352\271\200\354\234\240\353\246\254/4-challenge.ts" new file mode 100644 index 0000000..e42aa3a --- /dev/null +++ "b/week3/15 \352\271\200\354\234\240\353\246\254/4-challenge.ts" @@ -0,0 +1,63 @@ + +// 4. 인스턴스 -> 면적 출력 +// `Shape` 인터페이스 정의 (`getArea(): number`) +// `Rectangle` 클래스(`width`, `height`)와 `Circle` 클래스(`radius`)에서 `Shape` 인터페이스를 `implements`한다. +// 각 클래스에서 `getArea()` 메서드를 구현한다. +// 사각형과 원 인스턴스를 만들어 면적을 출력한다. + + + +//Shape 인터페이스 정의 +interface Shape { + //getArea() 메서드 + getArea() : number; +} + +//Rectangle 클래스 정의; Shape 인터페이스를 implements +class Rectangle implements Shape { + width : number; + height : number; + + //생성자는 weight와 height를 내부 속성으로 지정 + constructor(width : number, height : number) { + this.width = width; + this.height = height; + } + + getArea() : number { + //Rectangle의 Area 계산 + return this.width * this.height; + } +} + +class Circle implements Shape { + radius : number; + + constructor(radius : number) { + this.radius = radius; + } + + getArea() : number { + //PI 내장 상수 + return this.radius * this.radius * Math.PI; + } +} + +//getArea 출력해주는 함수 +function printArea(obj : Shape) : void { + //Shape 인터페이스가 타입; Rectangle, Cirlcle 모두 인자 가능 + console.log("The area is " + obj.getArea()); +} + +//Rectangle과 Circle 클래스 인스턴스 생성 +const Rectangle1 : Shape = new Rectangle(4,5); //생성자 호출 +printArea(Rectangle1); + +const Circle1 : Shape = new Circle(3); +printArea(Circle1); + + +/* +[LOG]: "The area is 20" +[LOG]: "The area is 28.274333882308138" +*/ \ No newline at end of file diff --git "a/week3/15 \352\271\200\354\234\240\353\246\254/reflection.md" "b/week3/15 \352\271\200\354\234\240\353\246\254/reflection.md" new file mode 100644 index 0000000..b356fdc --- /dev/null +++ "b/week3/15 \352\271\200\354\234\240\353\246\254/reflection.md" @@ -0,0 +1,39 @@ +# week3 : Object, Interface, and Class + + + +## 기본 개념 +1. 객체 = 속성 모음(키+값) +-객체 정의 시 속성의 이름과 타입 나열 +-Typescript는 속성들과 그 타입을 검사 +2. 선택적 속성 = Doesn't matter +-물음표를 속성 이름 뒤에 붙이기 +-속성이 없을 경우를 고려하기 +3. 타입 추론 = Typescript가 객체 타입을 추론 +4. 인터페이스 = 객체 타입 정의 +-특정 객체의 속성과 타입을 묶어 정의 +5. 클래스 = 객체 생성 틀 +-내부에 속성과 메서드 정의 +-생성자로 초기화 +6. implements = 클래스가 특정 인터페이스 구현 +7. extends = 상속 +-코드 재사용 +-객체들을 조직화 + + +## 알게 된 점 +1. 속성을 선택적으로 만들어 생략 가능한 값을 제시할 수 있다 +2. 인터페이스를 통해 객체가 갖는 쌍을 정리한다(재사용이 가능함) +3. 인터페이스는 런타임에 존재하지 않는다(컴파일 타임의 타입 검사만 함) +4. Typescript는 객체 형태만을 통해 인터페이스의 구현을 확인한다(구조적 타이핑) +5. 클래스는 실제 인스턴스 객체 생성 가능 <-> 인터페이스는 객체 형태만 정의 +6. Typescript에서는 클래스도 일종의 타입 + +## 헷갈렸던 점 +1. 클래스 인스턴스를 인터페이스 타입으로 정의할 때도 메서드를 포함해서(단순 객체 리터럴로 생성하면 X) 인터페이스의 조건을 충족해야 한다 +2. 선택적 속성 쓸 때 if로 조건 지정해줘서 출력하면 좋다 + +## 느낀 점 +1. Typescript도 상속이 가능해서 편하다 +2. 인터페이스를 타입으로 잡으면 implements된 클래스를 인자로 받을 수 있어서 좋다다 +3. + Math.PI를 쓰면 자동으로 PI값을 사용할 수 있어서 신기하다 \ No newline at end of file diff --git "a/week3/15 \353\260\225\354\204\234\354\230\201/.gitkeep" "b/week3/15 \353\260\225\354\204\234\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week3/15 \353\260\225\354\204\234\354\230\201/challenge.ts" "b/week3/15 \353\260\225\354\204\234\354\230\201/challenge.ts" new file mode 100644 index 0000000..aa2123f --- /dev/null +++ "b/week3/15 \353\260\225\354\204\234\354\230\201/challenge.ts" @@ -0,0 +1,34 @@ +interface Shape{ + getArea(): number; +} + +class Rectangle implements Shape{ + width: number; + height: number; + + constructor(width: number, height:number){ + this.width = width; + this.height = height; + } + + getArea(): number{ + return this.width*this.height; + } +} + +class Circle implements Shape { + radius : number; + + constructor(radius: number){ + this.radius = radius; + } + getArea(): number { + return this.radius*this.radius*3.14; + } +} + +let circle1 = new Circle(5); +let rect1 = new Rectangle(5,6); + +console.log(`원의 반지름: ${circle1.radius}, 원의 면적: ${circle1.getArea()}`); +console.log(`사각형의 높이: ${rect1.height}, 사각형의 가로: ${rect1.width}, 사각형의 넓이: ${rect1.getArea()}`); \ No newline at end of file diff --git "a/week3/15 \353\260\225\354\204\234\354\230\201/class.ts" "b/week3/15 \353\260\225\354\204\234\354\230\201/class.ts" new file mode 100644 index 0000000..4a15636 --- /dev/null +++ "b/week3/15 \353\260\225\354\204\234\354\230\201/class.ts" @@ -0,0 +1,21 @@ +class Animal { + name: string + + constructor (name: string){ + this.name = name; + } + + move (distance: number){ + console.log(`${this.name}이/가 ${distance}m만큼 이동합니다.`); + } +} + +class Dog extends Animal{ + bark(){ + console.log("Woof!"); + } +} + +let dog = new Dog("바둑이"); +dog.bark(); +dog.move(30); \ No newline at end of file diff --git "a/week3/15 \353\260\225\354\204\234\354\230\201/interface.ts" "b/week3/15 \353\260\225\354\204\234\354\230\201/interface.ts" new file mode 100644 index 0000000..9bd5275 --- /dev/null +++ "b/week3/15 \353\260\225\354\204\234\354\230\201/interface.ts" @@ -0,0 +1,21 @@ +interface Product { + name: string; + price: number; + description?: string; +} + +function printProduct(p: Product){ + console.log(`제품 이름: ${p.name}`); + console.log(`가격: ${p.price}`); + + if(p.description!==undefined){ + console.log(`설명 ${p.description}`); + } + console.log("------------------") +} + +let p1 :Product = {name: "초콜릿", price: 2300}; +printProduct(p1); +const p2 = {name: "사탕", price:200, description: "츄파츕스-딸기"}; +printProduct(p2); + diff --git "a/week3/15 \353\260\225\354\204\234\354\230\201/object.ts" "b/week3/15 \353\260\225\354\204\234\354\230\201/object.ts" new file mode 100644 index 0000000..26e8728 --- /dev/null +++ "b/week3/15 \353\260\225\354\204\234\354\230\201/object.ts" @@ -0,0 +1,9 @@ +let Person = { + name: "박서영", + age: 22, + email: "summeryoung@ewhain.net" +} + +console.log(`이름: ${Person.name}`); +console.log(`나이: ${Person.age}`); +console.log(`이메일: ${Person.email}`); \ No newline at end of file diff --git "a/week3/15 \353\260\225\354\204\234\354\230\201/reflection.md" "b/week3/15 \353\260\225\354\204\234\354\230\201/reflection.md" new file mode 100644 index 0000000..06b4fdf --- /dev/null +++ "b/week3/15 \353\260\225\354\204\234\354\230\201/reflection.md" @@ -0,0 +1,27 @@ +# Week3. 객체, 인터페이스, 클래스 + +## 1. 새롭게 알게된 점 + +### 객체 + : 타입 스크립트 내에서 객체 정의하고 사용하는 방법에 대해서 알게되었습니다! + 객체변수명: {객체 내용..} 이런 식으로 정의. +### 객체 타입명시와 타입추론 + : 타입을 명시할 때는 변수명 : {객체내 변수들 써주기} = {각 변수에 값들 입력} 이렇게 진행 + +### 선택적 속성 + : ?(물음표)를 뒤에 붙여서 표시. 객체의 속성 중 일부는 선택적으로 만들게할 수 있음. + last?:string 이런 식으로 만들기. + + +### 인터페이스 + : 자바와는 다르게 인터페이스 내에 메소드의 바디 구현하면 안됨. 컴파일의 과정에서만 사용되고 이후에 실행되면 폐기됨 + + +## 2. 헷갈리거나 어려웠던 부분 + - 이번 주차와는 무관하지만, 리턴타입이나 타입을 뒤에 적는 것이 아직 익숙하지 않아서 계속 실수한 점.. + - Q: 타입스크립트에서는 함수를 function 함수명.. 이렇게해서 만들었던 것 같은데, 클래스 내에서는 function을 딱히 붙이지 않는 것 같은데... 맞을까요?? 이 부분을 처음에 놓쳐서 오류 났었습니다.. + - 자바를 배우고 있지만 객체라는 개념이 아직 익숙하지 않아서 굉장히 헷갈렸습니다. + + +## 3. 심화과제 소감 + - 클래스 활용한 부분은 자프실 수업에서 진행하고 있는 내용이라 생각보다 재밌게 진행한 것 같습니다! 심화과제를 해보니까 생각보다 많은 부분을 놓치고 있었고, 타입스크립트 문법에 정말 안 익숙해있어서..문법 오류를 굉장히 많이 일으켰던 것 같습니다. 과제 진행하면서 부족한 부분을 알 수 있어서 너무 좋았습니다! 감사합니다. diff --git "a/week3/15 \353\260\225\354\261\204\354\234\244/.gitkeep" "b/week3/15 \353\260\225\354\261\204\354\234\244/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week3/15 \353\260\225\354\261\204\354\234\244/1-object.ts" "b/week3/15 \353\260\225\354\261\204\354\234\244/1-object.ts" new file mode 100644 index 0000000..a9b1262 --- /dev/null +++ "b/week3/15 \353\260\225\354\261\204\354\234\244/1-object.ts" @@ -0,0 +1,15 @@ + +// 1. 객체 실습 + +let person : {name : string; age : number; email? : string }= { + //이때 email 뒤에 ?를 붙여 선택적 속성으로 + name : "박채윤", + age : 20 +}; + +console.log("이름: " + person.name); +console.log("나이: " + person.age); +if (person.email !== undefined) { + //if문을 통해 email이 있을 경우(undefined가 아닌) 출력 + console.log("Your email address is " + person.email); +} \ No newline at end of file diff --git "a/week3/15 \353\260\225\354\261\204\354\234\244/2-interface.ts" "b/week3/15 \353\260\225\354\261\204\354\234\244/2-interface.ts" new file mode 100644 index 0000000..19247dd --- /dev/null +++ "b/week3/15 \353\260\225\354\261\204\354\234\244/2-interface.ts" @@ -0,0 +1,16 @@ +// 2-interface.ts + +interface Product { + name: string; price: number; description?: string; + } + + function printProduct(p: Product): void { + console.log(`상품명: ${p.name}`); + console.log(`가격: ${p.price}원`); + if (p.description) { + console.log(`설명: ${p.description}`); + } else { + console.log(`설명: 없음`); + } + } + \ No newline at end of file diff --git "a/week3/15 \353\260\225\354\261\204\354\234\244/3-class.ts" "b/week3/15 \353\260\225\354\261\204\354\234\244/3-class.ts" new file mode 100644 index 0000000..aca2b5c --- /dev/null +++ "b/week3/15 \353\260\225\354\261\204\354\234\244/3-class.ts" @@ -0,0 +1,25 @@ + +// 3. 클래스 실습 + +class Animal { + name : string; + constructor(name : string) { + this.name = name; + } + + move(distance : number = 0) { + console.log(this.name + " moved " + distance + " miles."); + } +} + + +class Dog extends Animal { + bark() { + console.log("Woof!"); + } +} + +const dog = new Dog("Nero"); + +dog.bark(); +dog.move(); diff --git "a/week3/15 \353\260\225\354\261\204\354\234\244/reflection.rtf" "b/week3/15 \353\260\225\354\261\204\354\234\244/reflection.rtf" new file mode 100644 index 0000000..be45eac --- /dev/null +++ "b/week3/15 \353\260\225\354\261\204\354\234\244/reflection.rtf" @@ -0,0 +1,13 @@ +{\rtf1\ansi\ansicpg949\cocoartf2761 +\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +{\*\expandedcolortbl;;} +\paperw11900\paperh16840\margl1440\margr1440\vieww11520\viewh8400\viewkind0 +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 + +\f0\fs24 \cf0 //\uc0\u49352 \u47196 \u50508 \u44172 \u46108 \u51216 \ +1. \uc0\u53440 \u51077 \u49828 \u53356 \u47549 \u53944 \u50640 \u49436 \u47588 \u44060 \u48320 \u49688 \u47484 \u54876 \u50857 \u54616 \u45716 \u48277 \u51012 \u50508 \u44172 \u46120 \ +2. \uc0\u48320 \u49688 \u49440 \u50616 \u54624 \u46412 ? \u47484 \u51060 \u50857 \u54616 \u50668 \u51312 \u44148 \u47928 \u51012 \u50756 \u49457 \u54616 \u45716 \u48277 \u51012 \u50508 \u44172 \u46120 \ +\ +//\uc0\u50612 \u47157 \u44144 \u45208 \u54775 \u44040 \u47160 \u45912 \u51216 \ +1. \uc0\u44284 \u51228 \u49688 \u54665 \u44284 \u51221 \u50640 \u49436 \u49828 \u53552 \u46356 \u51088 \u47308 \u50640 \u53356 \u44172 \u51032 \u51316 \u54616 \u50668 \u51060 \u54644 \u46020 \u44032 \u46504 \u50612 \u51652 \u44163 \u44057 \u50500 \u50500 \u49772 \u50880 \'85 \u48373 \u49845 \u50696 \u51221 !} \ No newline at end of file diff --git "a/week3/15 \354\240\225\354\234\244\354\225\204/.gitkeep" "b/week3/15 \354\240\225\354\234\244\354\225\204/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week3/15 \354\240\225\354\234\244\354\225\204/class.ts" "b/week3/15 \354\240\225\354\234\244\354\225\204/class.ts" new file mode 100644 index 0000000..d1f1378 --- /dev/null +++ "b/week3/15 \354\240\225\354\234\244\354\225\204/class.ts" @@ -0,0 +1,18 @@ +class Animal{ + name:string; + constructor(name:string){ + this.name = name; + } + move(distance:number):void{ + console.log(`${this.name}이 ${distance}m 움직였습니다.`); + } +} + +class Dog extends Animal{ + bark():void{console.log("Woof!");} +} + +let 배추 = new Dog("배추"); +배추.move(10); +배추.bark(); + diff --git "a/week3/15 \354\240\225\354\234\244\354\225\204/interface.ts" "b/week3/15 \354\240\225\354\234\244\354\225\204/interface.ts" new file mode 100644 index 0000000..94c320d --- /dev/null +++ "b/week3/15 \354\240\225\354\234\244\354\225\204/interface.ts" @@ -0,0 +1,25 @@ +interface Product { + name: string; + price: number; + description?: string; +} + +function printProduct(product: Product) { + console.log(`${product.name} - ${product.price}원 - ${product.description}`); +} + +let product1: Product = { + name: "치킨", + price: 10000, + description: "맛있는 치킨" +} + +printProduct(product1); + +let product2: Product = { + name: "치즈볼", + price: 5000, +} + +printProduct(product2); + diff --git "a/week3/15 \354\240\225\354\234\244\354\225\204/object.ts" "b/week3/15 \354\240\225\354\234\244\354\225\204/object.ts" new file mode 100644 index 0000000..285c495 --- /dev/null +++ "b/week3/15 \354\240\225\354\234\244\354\225\204/object.ts" @@ -0,0 +1,5 @@ +let person = { + name: "윤아", + age: 23, + email: "ellajya@ewha.ac.kr" + }; \ No newline at end of file diff --git "a/week3/15 \354\240\225\354\234\244\354\225\204/reflection.md" "b/week3/15 \354\240\225\354\234\244\354\225\204/reflection.md" new file mode 100644 index 0000000..d42850d --- /dev/null +++ "b/week3/15 \354\240\225\354\234\244\354\225\204/reflection.md" @@ -0,0 +1,5 @@ +1. 이번 주 학습에서 새로 알게 된 점은 무엇인가요? +-자바와 생성자가 약간 다르지만 대체적으로 OOP 개념이 비슷하다는 것을 알게 되었다. + +2. 헷갈렸거나 어려웠던 부분은 무엇인가요? +-생성자 초기화 부분이 헷갈렸다. 자바와 조금 다르게 생성자 초기화를 해야하는 것 같다. 인스턴스 생성할 때 약간 오류가 났다. \ No newline at end of file diff --git a/week3/2-interface.ts b/week3/2-interface.ts new file mode 100644 index 0000000..4a29e8a --- /dev/null +++ b/week3/2-interface.ts @@ -0,0 +1,45 @@ + +// 2. 인터페이스 실습 +// `Product` 인터페이스를 정의한다. + + //속성: `name`: string, `price`: number, `description?`: string (선택적) + +//`Product` 타입의 매개변수를 받아 정보를 출력하는 `printProduct()` 함수를 작성한다. +// 두 가지 예제(`description` 있음/없음)를 테스트한다. + + + +//Product 인터페이스 정의 +interface Product { + name : string; + price : number; + description? : string; + //description 속성은 선택적 +} + +function printProduct(p: Product) : void { + //매개변수에 타입 지정 + console.log("상품명: " + p.name); + console.log("가격 : " + p.price); + if(p.description) { + //if문을 통해 선택적 출력 + console.log("설명 : " + p.description); + } +} + +//product1 상수가 product 타입임을 명시 +const product1 : Product = { name : "팥붕", price : 1000}; +//함수 호출 +printProduct(product1); + +const product2 : Product = { name : "슈붕", price : 1500, description : "팥붕보다 500원 비싸다"}; +printProduct(product2); + + +/* +[LOG]: "상품명: 팥붕" +[LOG]: "가격 : 1000" +[LOG]: "상품명: 슈붕" +[LOG]: "가격 : 1500" +[LOG]: "설명 : 팥붕보다 500원 비싸다" +*/ \ No newline at end of file diff --git "a/week4/15 \352\263\265\354\204\270\354\230\201/.gitkeep" "b/week4/15 \352\263\265\354\204\270\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week4/15 \352\263\265\354\204\270\354\230\201/MyRoom.ts" "b/week4/15 \352\263\265\354\204\270\354\230\201/MyRoom.ts" new file mode 100644 index 0000000..df8c968 --- /dev/null +++ "b/week4/15 \352\263\265\354\204\270\354\230\201/MyRoom.ts" @@ -0,0 +1,36 @@ +// Colyseus에서 제공하는 Room 클래스를 확장하여 하나의 게임 방을 구성합니다. +import { Room, Client } from "colyseus"; +import { MyState, Player } from "./MyState"; + +// 방이다. +export class MyRoom extends Room { + + // 방이 생성될 때 호출됨. + // 기본 상태를 설정하고, 이후 이벤트 리스너 설정 가능. + onCreate(options: any) { + this.setState(new MyState()); + console.log(`[시스템] 방이 개설되었습니다.`); + } + + // 플레이어가 방에 입장했을 때 호출됨. + onJoin(client: Client, options: any) { + const player = new Player(); + player.name = `Survivor_${client.sessionId.slice(0, 5)}`; // 세션ID 기반 코드명 + player.x = 0; // 초기 위치 설정 (맵의 출발 지점) + player.y = 0; + + // 방 상태에 플레이어 등록 + this.state.players.set(client.sessionId, player); + console.log(`[입장] ${player.name} 플레이어가 방에 들어왔습니다.`); + } + + // 플레이어가 방에서 나갔을 때 호출됨. + onLeave(client: Client) { + const player = this.state.players.get(client.sessionId); + this.state.players.delete(client.sessionId); + + if (player) { + console.log(`[이탈] ${player.name} 플레이어가 구역을 벗어났습니다.`); + } + } +} diff --git "a/week4/15 \352\263\265\354\204\270\354\230\201/MyState.ts" "b/week4/15 \352\263\265\354\204\270\354\230\201/MyState.ts" new file mode 100644 index 0000000..00f3b1a --- /dev/null +++ "b/week4/15 \352\263\265\354\204\270\354\230\201/MyState.ts" @@ -0,0 +1,17 @@ +// 목적: 게임의 상태(state)를 정의 +// Colyseus의 Schema를 기반으로 서버와 클라이언트 간 상태를 자동으로 동기화한다고.... +import { Schema, type, MapSchema } from "@colyseus/schema"; + +// 목적: 개별 플레이어를 나타내는 클래스 +export class Player extends Schema { + @type("string") name: string = ""; // 이름 + @type("number") x: number = 0; // 현재 위치 X + @type("number") y: number = 0; // 현재 위치 Y +} + +// 목적: 게임 룸의 전체 상태(state)를 관리하는 클래스 +export class MyState extends Schema { + // 모든 플레이어를 저장하는 맵 + // 키는 sessionId, 값은 Player 객체 + @type({ map: Player }) players = new MapSchema(); +} diff --git "a/week4/15 \352\263\265\354\204\270\354\230\201/index.ts" "b/week4/15 \352\263\265\354\204\270\354\230\201/index.ts" new file mode 100644 index 0000000..4ff2da2 --- /dev/null +++ "b/week4/15 \352\263\265\354\204\270\354\230\201/index.ts" @@ -0,0 +1,16 @@ +// 목적: 엔트리 파일 - Colyseus 게임 서버를 생성 및 방을 등록 +import "reflect-metadata"; // 데코레이터 기능 활성화를 위한 필수 import << 이거 안 하니까 오류 뜸. +import { Server } from "colyseus"; +import { MyRoom } from "./MyRoom"; +import { createServer } from "http"; + +const port = 2567; +const httpServer = createServer(); +const gameServer = new Server({ server: httpServer }); + +// 'my_room'이라는 이름으로 방 정의 +gameServer.define("my_room", MyRoom); + +// 서버 시작 +gameServer.listen(port); +console.log(`방이 포트 ${port}에서 활성화되었습니다.`); diff --git "a/week4/15 \352\263\265\354\204\270\354\230\201/reflection.md" "b/week4/15 \352\263\265\354\204\270\354\230\201/reflection.md" new file mode 100644 index 0000000..1576406 --- /dev/null +++ "b/week4/15 \352\263\265\354\204\270\354\230\201/reflection.md" @@ -0,0 +1,31 @@ +1. Room / State / Client 각각 어떤 역할을 하나요? +Room: 방이다. 존재한다. 클라이언트들을 모아둔다. +State: Room의 상태다. 방 안의 플레이어 목록, 좌표, 아이템 등의 모든 게임 정보가 여기에 포함? 변화가 있으면 서버가 알아채고 다른 클라이언트들에게 알려준다. 동기화가 이루어진다. +Client: 플레이어다. 변화를 일으키는 주체 중 하나다. 서버가 주는대로 받기도 한다. + + +2. 직접 서버 코드를 실행해본 느낌은 어땠나요? +코드를 실행하면 터미널에 올라오는 Colyseus 아스키 아트가 고등학생 때 Zork 처음 플레이했던 향수를 불러일으켜서 좋았음! 로우레벨하게 직접 서버를 작동시킨다는 감각이 신선하네요. +아직 서버가 일하는 걸 직접 본 게 아니라서 실감이 나진 않지만요. + + +3. State가 어떻게 바뀌고, 클라이언트가 그걸 어떻게 받는 구조인지 이해가 되나요? +리얼타임으로 서버가 감시하는 구조. +Room 내부에서 상태에 변화가 있으면 서버가 캐치. +동기화를 위해 모든 클라이언트에게 뿌려준다. + + +4. 어려웠던 부분이나 헷갈린 부분은? +Mac 기준으로 작성된 실습 큐레이션을 처음 봤을 때는 경로라든가 실행 방식이 조금 달라 보여 당황했지만, 터미널을 쓴다는 점에서는 큰 차이는 없어서 곧 익숙해졌음. + + +5. 코드를 작성하면서 어떤 부분이 흥미로웠나요? +생각보다 구조가 간단하다? + + +6. 실습을 통해 Colyseus의 흐름이 어떻게 이해되었나요? +방을 만들고 거기에 상태를 넣으면서, 클라이언트가 접속을 하는... 흐름이 명확한 것 같아요. + + +7. Playground가 아닌 환경에서 코드를 돌려보니 어땠나요? +VS Code 환경이 익숙해서 오히려 Playground보다 편했어용. \ No newline at end of file diff --git "a/week4/15 \352\263\265\354\204\270\354\230\201/summary.md" "b/week4/15 \352\263\265\354\204\270\354\230\201/summary.md" new file mode 100644 index 0000000..e139319 --- /dev/null +++ "b/week4/15 \352\263\265\354\204\270\354\230\201/summary.md" @@ -0,0 +1,53 @@ +# WEEK 04 : Colyseus 서버 구조를 이해하자 - Room, State, Client를 중심으로 + +## 배운 것 요약 + +01. Colyseus란 무엇인가. +Colyseus는 Node.js 기반 프레임워크. +플레이어(n명): 접속할게. +서버: 상태 관리하면서 모든 클라이언트를 위해 리얼타임 정보 동기화 할게. +클라이언트: 감사합니다? + +Colyseus의 구조: Room, Client, State. +Room은 플레이어들이 게임하는 (개념적) 공간. +Client는 플레이어의 다른 이름. +State는 방의 현재 상태. 서버가 관리. + +``` +클라이언트 → Room 입장 → Room 안에서 상태 변경 → 모든 Client에게 실시간 전송 + +[Client] ──> [Room] ──> [State] + ↑ ↓ ↑ + └───── message ───────────┘ + +``` + +GM / PC1 / PC2 / PC3 / PC4가 Roll20 방에 입장 중. +PC2가 카드를 덱에 추가함. +서버: 음. Room의 State가 바뀌었군. 업데이트하고 공지를 때린다. +나머지 PC들: 아하. PC2가 카드를 추가했군요. 감사합니다. + +@type({ map: Player }) players = new MapSchema(); + + + + +02. 실습 + +준비할 것: node.js, vscode(혹은 터미널) + +``` +사견: 시소실 생각나고 정겨움. + +mkdir colyseus-test +cd colyseus-test +npm init -y + +npm install colyseus +npm install -D typescript ts-node @types/node +``` + +이후 MyRoom.ts와 index.ts를 생성하는데, 주의해야 할 점은 tsconfig.json에서 주석처리된 기능을 켜 줘야 정상 작동을 한다. +"experimentalDecorators": true, +"emitDecoratorMetadata": true, +이 두개를 켜야 Colyseus 타이틀이 터미널에 나타나는 듯. \ No newline at end of file diff --git "a/week4/15 \352\263\265\354\204\270\354\230\201/tsconfig.json" "b/week4/15 \352\263\265\354\204\270\354\230\201/tsconfig.json" new file mode 100644 index 0000000..2b59c85 --- /dev/null +++ "b/week4/15 \352\263\265\354\204\270\354\230\201/tsconfig.json" @@ -0,0 +1,113 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "libReplacement": true, /* Enable lib replacement. */ + "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git "a/week4/15 \352\270\260\354\247\200\355\230\204/.gitkeep" "b/week4/15 \352\270\260\354\247\200\355\230\204/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week4/15 \352\270\260\354\247\200\355\230\204/MyRoom.ts" "b/week4/15 \352\270\260\354\247\200\355\230\204/MyRoom.ts" new file mode 100644 index 0000000..c713555 --- /dev/null +++ "b/week4/15 \352\270\260\354\247\200\355\230\204/MyRoom.ts" @@ -0,0 +1,18 @@ +import { Room, Client } from "colyseus"; +import { MyState, Player } from "./MyState"; + +export class MyRoom extends Room { + onCreate(options: any) { + this.setState(new MyState()); + } + + onJoin(client: Client, options: any) { + const player = new Player(); + player.name = `Player_${client.sessionId}`; + this.state.players.set(client.sessionId, player); + } + + onLeave(client: Client) { + this.state.players.delete(client.sessionId); + } +} \ No newline at end of file diff --git "a/week4/15 \352\270\260\354\247\200\355\230\204/MyState.ts" "b/week4/15 \352\270\260\354\247\200\355\230\204/MyState.ts" new file mode 100644 index 0000000..5b12e57 --- /dev/null +++ "b/week4/15 \352\270\260\354\247\200\355\230\204/MyState.ts" @@ -0,0 +1,11 @@ +import { Schema, type, MapSchema } from "@colyseus/schema"; + +export class Player extends Schema { + @type("string") name: string = ""; + @type("number") x: number = 0; + @type("number") y: number = 0; +} + +export class MyState extends Schema { + @type({ map: Player }) players = new MapSchema(); +} \ No newline at end of file diff --git "a/week4/15 \352\270\260\354\247\200\355\230\204/index.ts" "b/week4/15 \352\270\260\354\247\200\355\230\204/index.ts" new file mode 100644 index 0000000..d805aa6 --- /dev/null +++ "b/week4/15 \352\270\260\354\247\200\355\230\204/index.ts" @@ -0,0 +1,12 @@ +import { Server } from "colyseus"; +import { MyRoom } from "./MyRoom"; +import { createServer } from "http"; + +const port = 2567; +const httpServer = createServer(); +const gameServer = new Server({ server: httpServer }); + +gameServer.define("my_room", MyRoom); + +gameServer.listen(port); +console.log(`서버 실행 중! 포트: ${port}`); \ No newline at end of file diff --git "a/week4/15 \352\270\260\354\247\200\355\230\204/reflection.md" "b/week4/15 \352\270\260\354\247\200\355\230\204/reflection.md" new file mode 100644 index 0000000..f6bd796 --- /dev/null +++ "b/week4/15 \352\270\260\354\247\200\355\230\204/reflection.md" @@ -0,0 +1,14 @@ +1. room은 말 그대로 '방'의 개념. 게임이 이루어질 수 있는 공간으로써의 역할을 한다. +client는 플레이어. state는 room의 전반적인 상태를 의미. + +2. 어렵다.. 내가 쓰는 서버들이 저런 코드로 만들어져서 관리되다가 없어지고 다시 생기고 했겠구나 + +3. this.onMessage("chat", (client, message) => { + console.log(받은 메시지: ${message}); + this.state.message = message; // 상태 업데이트 + }); + } + +이 코드를 통해 state가 바뀌는 것 같다. 클라이언트가 메시지를 보내면 그 내용으로 this.state.message가 변경되는 개념이라고 이해했다. 자동 동기화가 실행되고 있기에 서버와 클라이언트가 연결되어있다면 state의 변화를 클라이언트가 바로바로 받을 수 있다. + +4. export class 문이 무슨 의민지 제대로 이해하지 못했다. extends 부분도 어렵다. 왜 쓰는지 그 이유가 납득이 안 되는 느낌이다. \ No newline at end of file diff --git "a/week4/15 \352\271\200\354\234\240\353\246\254/.gitkeep" "b/week4/15 \352\271\200\354\234\240\353\246\254/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week4/15 \352\271\200\354\234\240\353\246\254/MyRoom.ts" "b/week4/15 \352\271\200\354\234\240\353\246\254/MyRoom.ts" new file mode 100644 index 0000000..3490294 --- /dev/null +++ "b/week4/15 \352\271\200\354\234\240\353\246\254/MyRoom.ts" @@ -0,0 +1,40 @@ +/* +**Room 클래스 (게임 방 역할)를 정의하는 파일이에요.** + +- `Room` 클래스를 만들고 아래 메서드를 포함해주세요: + - `onCreate`: Room이 만들어질 때 호출 + - `onJoin`: 플레이어가 입장할 때 호출 + - `onLeave`: 플레이어가 나갈 때 호출 +- 입장한 플레이어마다 **Player 객체를 만들고**, `state.players`에 추가해보세요. +- 나간 플레이어는 `state.players.delete()`로 제거해요. +*/ + + +//import 사용 +import { Room, Client } from "colyseus"; +import { MyState, Player } from "./MyState"; +//MyState로부터 MyState와 Player Class를 import + +export class MyRoom extends Room { + onCreate(options: any) { + //onCreate when first made + this.setState(new MyState()); + //init + } + + onJoin(client: Client, options: any) { + //onJoin whenever log-in happens + const player = new Player(); + //player instance + player.name = 'Player_${client.sessionId}'; + //Id appointing + this.state.players.set(client.sessionId, player); + //using sessionId as a key storing player + } + + onLeave(client: Client) { + //onLeave whenever log-out happens + this.state.players.delete(client.sessionId); + //deleting info of the player + } +} \ No newline at end of file diff --git "a/week4/15 \352\271\200\354\234\240\353\246\254/MyState.ts" "b/week4/15 \352\271\200\354\234\240\353\246\254/MyState.ts" new file mode 100644 index 0000000..7c786b8 --- /dev/null +++ "b/week4/15 \352\271\200\354\234\240\353\246\254/MyState.ts" @@ -0,0 +1,28 @@ +/* +**게임 상태(state)를 정의하는 파일이에요.** `MyState`라는 클래스는 `MyRoom.ts`에서 사용할 상태 정의입니다! + +- `Player` 클래스 안에 다음 세 가지 속성을 추가해주세요: + - `name: string`, `x: number`, `y: number` +- `MapSchema`를 사용해서 플레이어 목록을 관리하세요! +*/ + +//MyState = Defines state used in MyRoom + +import { Schema, type, MapSchema } from "@colyseus/schema"; +//Schema = Base Class used when defining State +//type = Decorator indicating what type is this field +//MapSchema = Auto-sync using network + +export class Player extends Schema { + //Player = becomes traceable object(extends) + @type("string") name: string = ""; + @type("number") x: number = 0; + @type("number") y: number = 0; + //3 properties in Player class +} + +export class MyState extends Schema { + @type({ map: Player }) players = new MapSchema(); +} +//players= multi-Player collection +//MapSchema = real time auto-sync \ No newline at end of file diff --git "a/week4/15 \352\271\200\354\234\240\353\246\254/index.ts" "b/week4/15 \352\271\200\354\234\240\353\246\254/index.ts" new file mode 100644 index 0000000..57af730 --- /dev/null +++ "b/week4/15 \352\271\200\354\234\240\353\246\254/index.ts" @@ -0,0 +1,27 @@ +/* +**서버를 실행하고, 방(Room)을 등록하는 파일이에요.** + +- Colyseus 서버를 만들고 `"my_room"`이라는 이름으로 `MyRoom`을 등록해주세요. +- 서버 포트는 2567로 설정! +*/ + + +import { Server } from "colyseus"; +//importing server class used when making game server +import { MyRoom } from "./MyRoom"; +import { createServer } from "http"; +//importing createServer to make basic http server + +const port = 2567; +//port number where server opens +const httpServer = createServer(); +//creating new http server which has colyseus game server on it +const gameServer = new Server({ server : httpServer }); +//making game server instance using newly made http server + +gameServer.define("my_room", MyRoom); +//room and room class appointed + +gameServer.listen(port); +//game server waiting for network request +console.log("서버 실행 중! 포트: ${port}"); \ No newline at end of file diff --git "a/week4/15 \352\271\200\354\234\240\353\246\254/reflection.md" "b/week4/15 \352\271\200\354\234\240\353\246\254/reflection.md" new file mode 100644 index 0000000..066e409 --- /dev/null +++ "b/week4/15 \352\271\200\354\234\240\353\246\254/reflection.md" @@ -0,0 +1,41 @@ +# week4 : Room, State and Client + + + +## 회고 질문 + + +1. Room / State / Client는 각각 어떤 역할을 하나요? +-Room = 교실 +-State = 자리 배치도(교수님이 관리) +-Client = 학생 +-이때 State는 Client끼리 모두 공유하게 됨 +-State 변경 -> Client에 자동 반영 + + +2. 직접 서버 코드를 실행해본 느낌은 어땠나요? +-타입스크립트를 vs code에서 실행해보니까 재밌었다 +-import export extends 를 활발하게 쓰니까 여러 파일들을 만들고 같은 구조는 재활용할 수 있는 게임 시스템이 더 잘 이해되는 것 같았다 + + +3. State가 어떻게 바뀌고, 클라이언트가 그걸 어떻게 받는 구조인지 이해가 되나요? +-server는 room class 내부에서 this.state라는 객체를 가진다 +-우리가 정의해놓은 properties가 바뀌면 colyseus가 그걸 자동으로 감지한다는 게 신기했다 +-colyseus는 모든 client에게 변경된 부분만을 전송해준다 + + +4. 어려웠던 부분이나 헷갈린 부분은? +-import 할 때 undefined라고 자꾸 에러가 났었는데 알고 보니까 tsconfig에서 데코레이터 관련 코드가 true가 아니라 주석 처리되어 있어서 에러가 났었다... +-방 log in과 log out에 따라 player 객체가 state에 추가되고 제거되는 것이 신기했다 +-처음에는 어떻게 "실시간"으로 정보를 주고 받는다는 건지 와닿지 않았다 + + +5. 실습을 통해 Colyseus의 흐름이 어떻게 이해되었나요? +-client는 room에 모여 state를 공유 +-그걸 중매해주는 것이 server +-코드를 직접접 써보니 room이 만들어지고 player가 오고가는 흐름이 잘 보여서 좋았다 + + +6. Playground가 아닌 환경에서 코드를 돌려보니 어땠나요? +-따로 파일을 설치해야 했지만 한 번 설치하고 나서부터는 Visual Studio Code에서 돌려볼 수 있어서 편했다 +-import 할 때 자꾸 에러 나서 새로 파일 깔고 또 파일 깔고 하는 게 까다로웠다 diff --git "a/week4/15 \352\271\200\355\230\234\354\233\220/.gitkeep" "b/week4/15 \352\271\200\355\230\234\354\233\220/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week4/15 \352\271\200\355\230\234\354\233\220/MyRoom.ts" "b/week4/15 \352\271\200\355\230\234\354\233\220/MyRoom.ts" new file mode 100644 index 0000000..c94f57a --- /dev/null +++ "b/week4/15 \352\271\200\355\230\234\354\233\220/MyRoom.ts" @@ -0,0 +1,39 @@ +import { Room, Client } from "colyseus"; +import { MyState, Player } from "./MyState"; + + +/* Room 상속 --> colyseus에 이미 있는 기능 사용 가능 + onCreate: room이 생성될 때 실행됨 + options: 클라이언트가 방을 만들 때 전달할 수 있는 추가 설정값 ex최대인원? + this.setState: 초기 상태 설정. new MyState()를 넘김-->room의 상태가 MyState 클래스의 구조를 따라감 */ +export class MyRoom extends Room { + onCreate(options: any) { + this.setState(new MyState()); + + // 클라이언트가 메시지 보내면 서버가 응답(보너스과제) + // (gpt가 써줬는데 사실 요거 이해를 못 하겠어요...) + this.onMessage("ping", (client, message) => { + console.log(`ping 메시지 수신: ${message}`); + client.send("pong", "pong 응답!"); + }); + } + + /* onJoin: 클라이언트가 입장할 때 실행되는 메서드 + (파라미터) client --> 고유한 sessionId 가짐 + 플레이어 객체 생성 + 플레이어 이름 설정(sessionId이용) + 현재 room의 상태에 플레이어 등록(키:client.sessionId, 값:player인스턴스) + --> 이 상태를 접속 중인 클라이언트 모두에게 실시간 동기화함 */ + onJoin(client: Client, options: any) { + const player = new Player(); + player.name = `Player_${client.sessionId}`; + this.state.players.set(client.sessionId, player); + } + + /* onLeave: 클라이언트가 나갈 때 실행되는 메서드 + room 상태에 플레이어 삭제 + --> 이 상태를 접속 중인 클라이언트 모두에게 실시간 동기화 */ + onLeave(client: Client) { + this.state.players.delete(client.sessionId); + } +} \ No newline at end of file diff --git "a/week4/15 \352\271\200\355\230\234\354\233\220/MyState.ts" "b/week4/15 \352\271\200\355\230\234\354\233\220/MyState.ts" new file mode 100644 index 0000000..104cd07 --- /dev/null +++ "b/week4/15 \352\271\200\355\230\234\354\233\220/MyState.ts" @@ -0,0 +1,26 @@ +import { Schema, type, MapSchema } from "@colyseus/schema"; + +/* Player 클래스 --> Colyseus의 Schema 상속 + (Schema: 실시간 상태를 정의할 때 사용하는 기본 클래스. + 상속하면 클래스 필드를 자동으로 클라이언트와 동기화해줌) + + @type: 데코레이터. 이 필드는 클라이언트와 동기화해야 한다고 알려주는 표시. + ex) @type("string") name: string = ""; + name 속성이 string 타입이고, 이건 클라이언트와 실시간 동기화가 필요함. */ +export class Player extends Schema { + @type("string") name: string = ""; + @type("number") x: number = 0; + @type("number") y: number = 0; + + // hp, score, color 속성 추가(보너스과제) + @type("number") hp: number = 100; + @type("number") score: number = 0; + @type("string") color: string = "red"; +} + +/* player라는 이름의 속성 생성 + MapSchema: 실시간 동기화 가능한 딕셔너리 구조 + */ +export class MyState extends Schema { + @type({ map: Player }) players = new MapSchema(); +} \ No newline at end of file diff --git "a/week4/15 \352\271\200\355\230\234\354\233\220/index.ts" "b/week4/15 \352\271\200\355\230\234\354\233\220/index.ts" new file mode 100644 index 0000000..80fbb14 --- /dev/null +++ "b/week4/15 \352\271\200\355\230\234\354\233\220/index.ts" @@ -0,0 +1,15 @@ +console.log("서버시작"); + +import { Server } from "colyseus"; +import { MyRoom } from "./MyRoom"; +import { createServer } from "http"; + + +const port = 2567; +const httpServer = createServer(); //이 HTTP 서버는 WebSocket 요청을 처리하는 데 사용됨...? 실시간 통신인가 보다 +const gameServer = new Server({ server: httpServer }); + +gameServer.define("my_room", MyRoom); + +gameServer.listen(port); +console.log(`게임 서버 실행 중! 포트: ${port}`); \ No newline at end of file diff --git "a/week4/15 \352\271\200\355\230\234\354\233\220/reflection.md" "b/week4/15 \352\271\200\355\230\234\354\233\220/reflection.md" new file mode 100644 index 0000000..be0e0b0 --- /dev/null +++ "b/week4/15 \352\271\200\355\230\234\354\233\220/reflection.md" @@ -0,0 +1,37 @@ +**1. Room, State, Client 각각이 어떤 역할을 하는지 설명해보세요.** +Room: 플레이어들이 들어오는 게임 공간 +State: Room의 상태(바뀌면 client한테 실시간 전송됨) +Client: 플레이어 + +**2. 코드를 작성하면서 어떤 부분이 흥미로웠나요?** +클래스 import/export 기능 덕에 관련된 코드를 주제별로 나누어 파일마다 정리할 수 있다는 것을 알았다. + +**3. 막히거나 헷갈렸던 점이 있었나요?** +node index.ts 입력했는데 파일 타입?이 안 맞는다고 함 +--> npm install -g ts-node typescript + +데코레이터 실행이 안 됨(@type이 undefined) +--> npm install @colyseus/schema로 패키지 추가설치 +--> gpt따라서 tsconfig.json 수정 +"experimentalDecorators": true, +"emitDecoratorMetadata": true, + +메세지핸들러 추가하는 보너스 과제 +// 클라이언트가 메시지 보내면 서버가 응답(보너스과제) + // gpt가 써줬는데 사실 요거 이해를 못하겠어요... 뭐지진짜 + this.onMessage("ping", (client, message) => { + console.log(`ping 메시지 수신: ${message}`); + client.send("pong", "pong 응답!"); + }); + } + +**4. 실습을 통해 Colyseus의 흐름이 어떻게 이해되었나요?** +Room = 게임 공간, 플레이 공간, 플레이어들이 들어오는 곳 +플레이어들/게임 공간의 상태(State)가 바뀌면 그 정보를 서버만 갖고 있지 말고 Client들한테도 전송 필요 +--> 이 과정을 쉽게 할 수 있도록 기능 제공하는 라이브러리가 Colyseus + +**5. Playground가 아닌 환경에서 코드를 돌려보니 어땠나요?** +어디에 뭘 입력해야 하는지 감이 하나도 안 잡혀서 gpt한테 모든 걸 꼬치꼬치 캐물어 겨우 성공했답니다. 하지만 평소 궁금했던 서버를 만드는 과정을 직접 해 볼 수 있어서 좋았어요! + + +PR 실수해서 죄송해요 앞으로 꼼꼼히 확인하겠습니다...... diff --git "a/week4/15 \353\260\225\354\204\234\354\230\201/.gitkeep" "b/week4/15 \353\260\225\354\204\234\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week4/15 \353\260\225\354\204\234\354\230\201/MyRoom.ts" "b/week4/15 \353\260\225\354\204\234\354\230\201/MyRoom.ts" new file mode 100644 index 0000000..6049c88 --- /dev/null +++ "b/week4/15 \353\260\225\354\204\234\354\230\201/MyRoom.ts" @@ -0,0 +1,24 @@ +import {Room, Client} from "colyseus"; +import {MyState, Player} from "./MyState"; + +export class MyRoom extends Room{ + onCreate(options: any){ + this.setState(new MyState()); + + this.onMessage("ping", (client, message) => { + console.log(client.sessionId, "sent ping: ", message); + client.send("pong", {received: true}); + }) + } + + onJoin(client: Client, options: any){ + const player = new Player(); + player.name = `Player_${client.sessionId}`; + this.state.players.set(client.sessionId,player); + } + + onLeave(client: Client){ + this.state.players.delete(client.sessionId); + } + +} \ No newline at end of file diff --git "a/week4/15 \353\260\225\354\204\234\354\230\201/MyState.ts" "b/week4/15 \353\260\225\354\204\234\354\230\201/MyState.ts" new file mode 100644 index 0000000..86c4240 --- /dev/null +++ "b/week4/15 \353\260\225\354\204\234\354\230\201/MyState.ts" @@ -0,0 +1,16 @@ +import {Schema, type, MapSchema} from "@colyseus/schema"; + +export class Player extends Schema{ + @type("string") name: string = ""; + @type("number") x: number = 0; + @type("number") y: number = 0; + @type("number") hp: number = 100; + @type("number") score: number = 0; + @type("string") color: string = ""; + +} + +export class MyState extends Schema{ + @type({map:Player}) players = new MapSchema(); + //여러 플레이어를 저장하는 맵, 각 플레이어는 고유한 키(sessionId)로 구분됨 +} \ No newline at end of file diff --git "a/week4/15 \353\260\225\354\204\234\354\230\201/index.ts" "b/week4/15 \353\260\225\354\204\234\354\230\201/index.ts" new file mode 100644 index 0000000..bf0801a --- /dev/null +++ "b/week4/15 \353\260\225\354\204\234\354\230\201/index.ts" @@ -0,0 +1,11 @@ +import {Server} from "colyseus"; +import {MyRoom} from "./MyRoom"; +import {createServer} from "http"; + +const port = 2567; +const httpServer = createServer(); +const gameServer = new Server({server: httpServer}); + +gameServer.define("my_room", MyRoom); +gameServer.listen(port); +console.log(`게임 서버 실행 중! 포트: ${port}`); diff --git "a/week4/15 \353\260\225\354\204\234\354\230\201/reflection.md" "b/week4/15 \353\260\225\354\204\234\354\230\201/reflection.md" new file mode 100644 index 0000000..2510dd9 --- /dev/null +++ "b/week4/15 \353\260\225\354\204\234\354\230\201/reflection.md" @@ -0,0 +1,19 @@ +## ✅ Room/State/Client의 역할? + Room: 플레이어의 접속 공간으로, 생성, 플레이어들의 접속, 접속종료, 메시지 등을 관리. 서버에 방을 구성할 때 상속받는 클래스. + +onMessage를 this로 호출?하는데, Room에 정의되어있는 메소드이기에 상속받은거라 가능함. + Client: 방에 접속한 플레이어 인스턴스. (=플레이어), 세션/고유id? 존재재 + State: 방의 상태 (플레이어 목록, 위치) + +## ✅ 코드를 작성하며 흥미로웠던 부분? + 함수 정의 형식? => 이런 방식으로 onMessage의 함수? 부분을 정의하게되는 것 같아서 신기했습니다... 사실 함수인지 처음에 모르고 괄호를 이상하게 쳐서 오류가 났었기에 기억에 남습니다. => (화살표 함수?)라고하고 익명함수, 콜백/이벤트 핸들러에 쓰는거라고..? 검색? 결과 알게되었습니다. + + 서버 포트 번호 설정해서, 뭔가 생각보다 간단하게? 만든 것 같아서 신기했습니다. + +## ✅ 막혔던/헷갈렸던 점? + 앞에서 말한 화살표 함수 쓰는 부분에서 조금 막혔었습니다..ㅎ Room이라는 거랑 MyRoom?의 개념에 대해 헷갈렸던것 같은데.. 여기에서 MyRoom이라는 클래스를 Colyseus에 이미 있는 Room 클래스를 상속받아서 만든 느낌...?이라고 이해하였습니다. + export를 통해서 그렇게 만든 것 내보내기.. 등등등 + +## ✅ 실습을 통해 이해한 Colyseus 흐름? + Room에 플레이어가 접속하게되면 플레이어의 state 정보들(위치, 이름, 체력 등등)을 통해서 서버에서 계속해서 플레이어의 정보를 가져오고, 이 정보를 다른 플레이어들에게도 공유할 수 있게되는...? 그런 흐름으로 이해하였는데 잘 이해했는지는 모르겠습니다.. + +## ✅ Playground가 아닌 환경에서 코드 돌려본 소감? + 이전에 VSCode에서 돌릴 때는, .ts파일을 node.js 통해서 실행할 수 있는 .js파일로 바꿔서..? 어찌저찌 돌렸던 거 같은데, ts파일 자체로 돌릴 수 있어서 신기했고, 뭔가 웹 서버를 만들 때 처럼 주소를 통해서 접속해보고싶었는데, 그런 식으로..페이지가 보여지는건 아닌 것 같았습니다..? diff --git "a/week4/15 \353\260\225\354\261\204\354\234\244/.gitkeep" "b/week4/15 \353\260\225\354\261\204\354\234\244/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week4/15 \354\240\225\354\234\244\354\225\204/.gitkeep" "b/week4/15 \354\240\225\354\234\244\354\225\204/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week4/15 \354\240\225\354\234\244\354\225\204/MyRoom.ts" "b/week4/15 \354\240\225\354\234\244\354\225\204/MyRoom.ts" new file mode 100644 index 0000000..c713555 --- /dev/null +++ "b/week4/15 \354\240\225\354\234\244\354\225\204/MyRoom.ts" @@ -0,0 +1,18 @@ +import { Room, Client } from "colyseus"; +import { MyState, Player } from "./MyState"; + +export class MyRoom extends Room { + onCreate(options: any) { + this.setState(new MyState()); + } + + onJoin(client: Client, options: any) { + const player = new Player(); + player.name = `Player_${client.sessionId}`; + this.state.players.set(client.sessionId, player); + } + + onLeave(client: Client) { + this.state.players.delete(client.sessionId); + } +} \ No newline at end of file diff --git "a/week4/15 \354\240\225\354\234\244\354\225\204/MyState.ts" "b/week4/15 \354\240\225\354\234\244\354\225\204/MyState.ts" new file mode 100644 index 0000000..a35ac4e --- /dev/null +++ "b/week4/15 \354\240\225\354\234\244\354\225\204/MyState.ts" @@ -0,0 +1,15 @@ +// MyState.ts + +import "reflect-metadata"; +import { Schema, type, MapSchema } from "@colyseus/schema"; + +export class Player extends Schema { + @type("string") name: string; + @type("number") x: number; + @type("number") y: number; +} + +export class MyState extends Schema { + @type("map", Player) + players = new MapSchema(); +} \ No newline at end of file diff --git "a/week4/15 \354\240\225\354\234\244\354\225\204/index.ts" "b/week4/15 \354\240\225\354\234\244\354\225\204/index.ts" new file mode 100644 index 0000000..a921a64 --- /dev/null +++ "b/week4/15 \354\240\225\354\234\244\354\225\204/index.ts" @@ -0,0 +1,12 @@ +import { Server } from "colyseus"; +import { MyRoom } from "./Myroom"; +import { createServer } from "http"; + +const port = 2567; +const httpServer = createServer(); +const gameServer = new Server({ server: httpServer }); + +gameServer.define("my_room", MyRoom); + +gameServer.listen(port); +console.log(`서버 실행 중! 포트: ${port}`); \ No newline at end of file diff --git "a/week4/15 \354\240\225\354\234\244\354\225\204/reflection.md" "b/week4/15 \354\240\225\354\234\244\354\225\204/reflection.md" new file mode 100644 index 0000000..b8d73bd --- /dev/null +++ "b/week4/15 \354\240\225\354\234\244\354\225\204/reflection.md" @@ -0,0 +1,16 @@ +1. Room, State, Client 각각이 어떤 역할을 하는지 설명해보세요. +- Room: 게임 서버의 기본 단위로, 게임 세계의 물리적 공간을 정의. 플레이어들이 참여하는 공간을 정의하고, 게임 로직을 적용. +- State: 게임의 상태를 저장하고 관리하는 객체. 게임 내의 모든 객체와 속성을 포함하며, 게임 로직에 따라 업데이트. +- Client: 게임 클라이언트를 나타내는 객체로, 플레이어의 입력을 받고 게임 상태를 표시. + +2. 코드를 작성하면서 어떤 부분이 흥미로웠나요? +-OOP를 제대로 적용하면서 코딩하는 느낌이라 재미 있었다 + +3. 막히거나 헷갈렸던 점이 있었나요? +-colyseus를 처음 import해서 사용하는 거라 오류가 여러 번 났다 + +4. 실습을 통해 Colyseus의 흐름이 어떻게 이해되었나요? +-client들의 state를 업데이트하고 공유하는 체계가 room을 통해 이루어진다 + +5. Playground가 아닌 환경에서 코드를 돌려보니 어땠나요? +- 많이 불편했지만 처음에 거쳐야 할 과정이라고 생각한다 \ No newline at end of file diff --git "a/week5/15 \352\263\265\354\204\270\354\230\201/.gitkeep" "b/week5/15 \352\263\265\354\204\270\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week5/15 \352\263\265\354\204\270\354\230\201/MyRoom.ts" "b/week5/15 \352\263\265\354\204\270\354\230\201/MyRoom.ts" new file mode 100644 index 0000000..c58ef51 --- /dev/null +++ "b/week5/15 \352\263\265\354\204\270\354\230\201/MyRoom.ts" @@ -0,0 +1,18 @@ +import { Room, Client } from "colyseus"; +import { MyState } from "./MyState"; + +export class MyRoom extends Room { + onCreate() { + this.setState(new MyState()); + + // 메시지 받았을 때 처리. + this.onMessage("scoreUp", (client) => { + this.state.score += 1; + console.log(`🎯 점수 +1! 현재 점수: ${this.state.score}`); + }); + } + + onJoin(client: Client) { + console.log(`😀 ${client.sessionId} 님이 입장했습니다!`); + } +} \ No newline at end of file diff --git "a/week5/15 \352\263\265\354\204\270\354\230\201/MyState.ts" "b/week5/15 \352\263\265\354\204\270\354\230\201/MyState.ts" new file mode 100644 index 0000000..f712a3e --- /dev/null +++ "b/week5/15 \352\263\265\354\204\270\354\230\201/MyState.ts" @@ -0,0 +1,5 @@ +import { Schema, type } from "@colyseus/schema"; + +export class MyState extends Schema { + @type("number") score: number = 0; +} \ No newline at end of file diff --git "a/week5/15 \352\263\265\354\204\270\354\230\201/client.ts" "b/week5/15 \352\263\265\354\204\270\354\230\201/client.ts" new file mode 100644 index 0000000..a1bf3c8 --- /dev/null +++ "b/week5/15 \352\263\265\354\204\270\354\230\201/client.ts" @@ -0,0 +1,37 @@ +import { Client } from "colyseus.js"; +import WebSocket from "ws"; +import readline from "readline"; + +// Node.js용 WebSocket을 전역으로 등록 +(global as any).WebSocket = WebSocket; + +const client = new Client("ws://localhost:2567"); + +async function main() { + const room = await client.joinOrCreate("my_room"); + console.log(`✅ 방에 입장했습니다. 세션 ID: ${room.sessionId}`); + console.log(`💡 Enter 키를 누르면 점수가 올라갑니다.`); + + room.onStateChange((state: any) => { + console.log(`🎯 현재 점수: ${state.score}`); + }); + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + rl.on("line", () => { + console.log("📤 scoreUp 메시지 전송"); + room.send("scoreUp"); + }); + + room.onLeave(() => { + console.log("❌ 서버와 연결 종료됨"); + rl.close(); + }); +} + +main().catch((err) => { + console.error("❌ 클라이언트 오류:", err.message); +}); diff --git "a/week5/15 \352\263\265\354\204\270\354\230\201/index.ts" "b/week5/15 \352\263\265\354\204\270\354\230\201/index.ts" new file mode 100644 index 0000000..94ffe99 --- /dev/null +++ "b/week5/15 \352\263\265\354\204\270\354\230\201/index.ts" @@ -0,0 +1,13 @@ +import "reflect-metadata"; +import { Server } from "colyseus"; +import { MyRoom } from "./MyRoom"; +import { createServer } from "http"; + +const port = 2567; +const httpServer = createServer(); +const gameServer = new Server({ server: httpServer }); + +gameServer.define("my_room", MyRoom); + +gameServer.listen(port); +console.log(`🧠 Colyseus 서버 실행 중! 포트 ${port}`); diff --git "a/week5/15 \352\263\265\354\204\270\354\230\201/reflection.md" "b/week5/15 \352\263\265\354\204\270\354\230\201/reflection.md" new file mode 100644 index 0000000..8cc3dbf --- /dev/null +++ "b/week5/15 \352\263\265\354\204\270\354\230\201/reflection.md" @@ -0,0 +1,23 @@ +1. `room.send()`와 `onMessage()`가 어떤 역할을 했는지 설명해봅시다! +room.sent()는 클라이언트 --> 서버 메시지 전송 메서드. +onMessage()는 괄호 안 메시지를 받았을 때 처리할 내용/동작을 담고 있음. + +2. `state.score`가 바뀌면 모든 사람이 똑같이 보게 되는 이유는? +state.score은 클라이언트들이 공유하는 상태 객체이며, 서버가 관리하는 상태에 포함되어 있기 때문. +점수를 바꾸면 서버가 score값을 올리고, colyseus가 변화를 감지해 다른 클라이언트들에게도 push해줌. + +3. 상태를 직접 화면에 반영하지 않아도 되는 이유는? +room.onStateChange()로 변화를 감지하기 때문. +Colyseus가 감시하고 있다가 변화를 뿌려줌. + +4. 내가 버튼을 누른 건데, 다른 사람 점수도 왜 바뀌는 걸까? +상태를 공유하고 있으니까. +클라이언트: 누를게. +서버: 어? 상태 바꿀게. +Colyseus: 어? 동기화할게. +다른 클라이언트들: 어? 감사합니다? + +5. 이번 실습에서 가장 신기하거나 재밌었던 포인트는? +클라이언트에서 만든 변화를 서버에서 감지하는 구조가 눈에 보이니까 신기했음. +room.send()랑 onStateChange()만 사용해도 되니 편하네요. +근데 client 여러 개 띄워두고 진짜 뿌려주는지, 실시간으로 변화가 반영되는지도 보고 싶음. \ No newline at end of file diff --git "a/week5/15 \352\263\265\354\204\270\354\230\201/summary.md" "b/week5/15 \352\263\265\354\204\270\354\230\201/summary.md" new file mode 100644 index 0000000..734c8db --- /dev/null +++ "b/week5/15 \352\263\265\354\204\270\354\230\201/summary.md" @@ -0,0 +1,15 @@ +# WEEK 05 : Colyseus 서버 구조를 이해하자 - 동기화와 메시지 처리를 중심으로 + +## 배운 것 요약 + +01. 서버-클라이언트는 어떻게 소통할까? +클라이언트 --> 서버 : "점수 올려줘!" room.send("scoreUp") +서버: "알았어. 처리할게." onMessage("scoreUp") +Colyseus: 어? 상태가 바뀌었네? 동기화해야지~ + +변경사항을 서버에게 알려주는 이유는? +클라이언트가 둘 이상이고, 클라이언트들은 정보를 공유하고 있기 때문에. += 서버 중심 구조를 취한다. += authoritative server + + diff --git "a/week5/15 \352\263\265\354\204\270\354\230\201/tsconfig.json" "b/week5/15 \352\263\265\354\204\270\354\230\201/tsconfig.json" new file mode 100644 index 0000000..2b59c85 --- /dev/null +++ "b/week5/15 \352\263\265\354\204\270\354\230\201/tsconfig.json" @@ -0,0 +1,113 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "libReplacement": true, /* Enable lib replacement. */ + "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git "a/week5/15 \352\270\260\354\247\200\355\230\204/.gitkeep" "b/week5/15 \352\270\260\354\247\200\355\230\204/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week5/15 \352\271\200\354\234\240\353\246\254/.gitkeep" "b/week5/15 \352\271\200\354\234\240\353\246\254/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week5/15 \352\271\200\354\234\240\353\246\254/MyRoom.ts" "b/week5/15 \352\271\200\354\234\240\353\246\254/MyRoom.ts" new file mode 100644 index 0000000..b408064 --- /dev/null +++ "b/week5/15 \352\271\200\354\234\240\353\246\254/MyRoom.ts" @@ -0,0 +1,32 @@ +//1. 클라이언트에서 `room.send("scoreUp")` 메시지 보내기 +//2. 서버가 `onMessage("scoreUp")`로 받고 +//3. 서버 상태에 있는 `score += 1` 하기 +//4. Colyseus가 모든 클라이언트에 `score` 자동 전파 +//5. 클라이언트는 `state.score` 변화 감지해서 화면에 보여줌 + + + +import { Room, Client } from "colyseus"; +import { MyState } from "./MyState"; + +export class MyRoom extends Room { + //defining room of colyseus + //using MyState as state + onCreate() { + this.setState(new MyState()); + //init + + this.onMessage("scoreUp", (client) => { + //out when client sends message + this.state.score += 1; + //score change + console.log(`score +1! score now : ${this.state.score}`); +}); + } + + onJoin(client: Client) { + //when client comes in + console.log(`${client.sessionId} is with us!`); + } +} + diff --git "a/week5/15 \352\271\200\354\234\240\353\246\254/MyState.ts" "b/week5/15 \352\271\200\354\234\240\353\246\254/MyState.ts" new file mode 100644 index 0000000..72d7ef1 --- /dev/null +++ "b/week5/15 \352\271\200\354\234\240\353\246\254/MyState.ts" @@ -0,0 +1,17 @@ +//1. 클라이언트에서 `room.send("scoreUp")` 메시지 보내기 +//2. 서버가 `onMessage("scoreUp")`로 받고 +//3. 서버 상태에 있는 `score += 1` 하기 +//4. Colyseus가 모든 클라이언트에 `score` 자동 전파 +//5. 클라이언트는 `state.score` 변화 감지해서 화면에 보여줌 + + + +import { Schema, type } from "@colyseus/schema"; + +//schema extending +export class MyState extends Schema { + //score defining + @type("number") score : number = 0; + //@type for pointing this field is synchronizing target of colyseus + //init value is 0 +} \ No newline at end of file diff --git "a/week5/15 \352\271\200\354\234\240\353\246\254/reflection.md" "b/week5/15 \352\271\200\354\234\240\353\246\254/reflection.md" new file mode 100644 index 0000000..289e99b --- /dev/null +++ "b/week5/15 \352\271\200\354\234\240\353\246\254/reflection.md" @@ -0,0 +1,29 @@ +# week5 : Message and Synchronizing + + + +## 회고 질문 + + +1. `room.send()`와 `onMessage()`가 어떤 역할을 했는지 설명해봅시다! +-room.send = 클라이언트가 서버에게 메시지 +-onMessage = 서버가 메시지 받고 + 처리(반응) + + +2. `state.score`가 바뀌면 모든 사람이 똑같이 보게 되는 이유는? +-서버는 state를 기억 + 변화 +-colyseus가 모든 접속자의 화면에 이를 반영시켜줌 + + +3. 상태를 직접 화면에 반영하지 않아도 되는 이유는? +-모든 상태를 서버(진행자)에서 변화시킴 +-클라이언트는 요청만 ! + + +4. 내가 버튼을 누른 건데, 다른 사람 점수도 왜 바뀌는 걸까? +-클라이언트 요청 -> 서버 반응 -> 모든 클라이언트에게 변화 적용 + + +5. 이번 실습에서 가장 신기하거나 재밌었던 포인트는? +-클라이언트는 요청만 할 수 있고 모든 변화는 서버에서 담당한다는 게 신기했다(근데 당연하게 협동 게임인데 혼자만 점수가 바뀌면 안되겠지) +-서버 동기화 과정이 서버와 클라이언트 간의 상호작용처럼 느껴져서 재밌었다(진짜 서버를 다루는 느낌 !) \ No newline at end of file diff --git "a/week5/15 \352\271\200\355\230\234\354\233\220/.gitkeep" "b/week5/15 \352\271\200\355\230\234\354\233\220/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week5/15 \352\271\200\355\230\234\354\233\220/MyRoom.ts" "b/week5/15 \352\271\200\355\230\234\354\233\220/MyRoom.ts" new file mode 100644 index 0000000..b46e26c --- /dev/null +++ "b/week5/15 \352\271\200\355\230\234\354\233\220/MyRoom.ts" @@ -0,0 +1,18 @@ +import { Room, Client } from "colyseus"; +import { MyState } from "./MyState"; + +export class MyRoom extends Room { + onCreate() { + this.setState(new MyState()); + + // 메시지 받았을 때 처리! + this.onMessage("scoreUp", (client) => { + this.state.score += 1; + console.log(`🎯 점수 +1! 현재 점수: ${this.state.score}`); + }); + } + + onJoin(client: Client) { + console.log(`😀 ${client.sessionId} 님이 입장했습니다!`); + } +} \ No newline at end of file diff --git "a/week5/15 \352\271\200\355\230\234\354\233\220/MyState.ts" "b/week5/15 \352\271\200\355\230\234\354\233\220/MyState.ts" new file mode 100644 index 0000000..bd6f0b6 --- /dev/null +++ "b/week5/15 \352\271\200\355\230\234\354\233\220/MyState.ts" @@ -0,0 +1,5 @@ +import { Schema, type } from "@colyseus/schema"; + +export class MyState extends Schema { + @type("number") score: number = 0; +} \ No newline at end of file diff --git "a/week5/15 \352\271\200\355\230\234\354\233\220/reflection.md" "b/week5/15 \352\271\200\355\230\234\354\233\220/reflection.md" new file mode 100644 index 0000000..ad2588a --- /dev/null +++ "b/week5/15 \352\271\200\355\230\234\354\233\220/reflection.md" @@ -0,0 +1,22 @@ +**1. `room.send()`와 `onMessage()`가 어떤 역할을 했는지 설명해봅시다!** +room.send() --> 클라이언트에서 서버에 보내는 요청(점수를 올려 달라) +onMessage() --> 클라이언트에서 보낸 요청을 수행함(점수를 올림) + +**2. `state.score`가 바뀌면 모든 사람이 똑같이 보게 되는 이유는?** +state라는 공유 데이터를 서버에서 관리함 --> 서버에 있는 점수(state.score) 값이 바뀌면 colyseus가 모든 클라이언트에 실시간으로 전달함 +--> 모든 사용자(클라이언트)에게 똑같은 점수가 보임 + +**3. 상태를 직접 화면에 반영하지 않아도 되는 이유는?** +colyseus는 state의 변화를 자동으로 감지하고, 그 내용을 클라이언트에게 자동으로 전달하기 때문. + +**4. 내가 버튼을 누른 건데, 다른 사람 점수도 왜 바뀌는 걸까?** +버튼을 누름 --> room.send()로 서버에 메세지 보냄 --> 메세지를 받은 서버가 서버에 저장된 데이터를 바꿈 +개인별로 저장된 점수가 아니라, 서버에 저장된 공유 데이터를 바꾸기 때문에 모두에게 보여지는 것. + +**5. 이번 실습에서 가장 신기하거나 재밌었던 포인트는?** +html로 웹페이지를 만들어서 버튼을 누르는 식의 인터페이스를 구현해 볼 수 있어서 재밌었다. 터미널에서만 입력하던 이전 방식과 달리 실제 게임처럼 작동하는 UI를 만들 수 있어서 흥미로웠다. + + + + 깃허브 때문에 혼자 끙끙대다가 늦게 제출합니다... + 번거롭게 해서 여러 모로 죄송해요...ㅠㅠ \ No newline at end of file diff --git "a/week5/15 \353\260\225\354\204\234\354\230\201/.gitkeep" "b/week5/15 \353\260\225\354\204\234\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week5/15 \353\260\225\354\261\204\354\234\244/.gitkeep" "b/week5/15 \353\260\225\354\261\204\354\234\244/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week5/15 \354\240\225\354\234\244\354\225\204/.gitkeep" "b/week5/15 \354\240\225\354\234\244\354\225\204/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week6/15 \352\263\265\354\204\270\354\230\201/.gitkeep" "b/week6/15 \352\263\265\354\204\270\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week6/15 \352\270\260\354\247\200\355\230\204/.gitkeep" "b/week6/15 \352\270\260\354\247\200\355\230\204/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week6/15 \352\271\200\354\234\240\353\246\254/.gitkeep" "b/week6/15 \352\271\200\354\234\240\353\246\254/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week6/15 \352\271\200\355\230\234\354\233\220/.gitkeep" "b/week6/15 \352\271\200\355\230\234\354\233\220/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week6/15 \352\271\200\355\230\234\354\233\220/6\354\243\274\354\260\250_\354\230\244\355\224\210\354\206\214\354\212\244_\353\266\204\354\204\235.md" "b/week6/15 \352\271\200\355\230\234\354\233\220/6\354\243\274\354\260\250_\354\230\244\355\224\210\354\206\214\354\212\244_\353\266\204\354\204\235.md" new file mode 100644 index 0000000..aa81774 --- /dev/null +++ "b/week6/15 \352\271\200\355\230\234\354\233\220/6\354\243\274\354\260\250_\354\230\244\355\224\210\354\206\214\354\212\244_\353\266\204\354\204\235.md" @@ -0,0 +1,114 @@ +## snowball fight +[GitHub 링크](https://github.com/decentraland-scenes/snowball-fight) + + +### 어떤 게임인지, 주요 기능 설명 (1~2문단) +실시간 멀티플레이 눈싸움 게임으로, red/blue 팀을 선택한 후 눈덩이를 던져 상대 팀 플레이어를 맞히면 팀 점수가 올라가는 시스템이다. 일정 시간 동안 더 높은 점수를 획득한 팀이 승리하며 게임 종료 후 MVP를 발표한다. + + +### 클라이언트와 서버 구조 요약 (간단한 도식 가능) +클라이언트: 팀 선택, 눈덩이 던지기, 플레이어 이동, 적 위치/점수/게임 경과시간 등 정보 수신 +서버(Colyseus Room): 플레이어 행동 정보 수신(눈덩이 던지기 등), 눈덩이 맞혔는지 유효 판정, 점수 업데이트, 게임 경과시간 관리, 발생한 이벤트 모든 플레이어에게 전달 (+결과를 Firebase에 저장) + + +### Colyseus가 사용된 부분 요약 + - 룸(Room) 관리: + MyRoom.ts에서 Room 클래스를 상속해 게임 방을 정의함 + onJoin, onLeave, onCreate, onDispose 메서드를 통해 플레이어 입장/퇴장, 방 생성/삭제 관리 + 같은 realm에 있는 플레이어끼리는 같은 Room에 자동 매칭됨 + - 상태 동기화(State Synchronization) + MyRoomState를 통해 서버가 관리하는 상태(점수, 플레이어 정보, 시간 등)를 정의함 + 서버 상태 변경 사항(점수, 경기 시작/종료 등) --> 자동으로 클라이언트와 동기화됨! + 클라이언트는 직접 서버 상태 관리x, Colyseus가 전달하는 메시지 통해서 최신 상태 유지 + - 메시지 처리(Message Handling) + onMessage() 메서드를 통해 클라이언트가 보낸 행동을 서버에서 처리 + + +### 우리가 수업에서 배운 개념과 연결되는 부분 +**서버-클라이언트 구조**: 클라이언트가 눈덩이 던지기, 팀 선택 등의 입력을 하고, 서버는 그 입력을 받아 계산 결과를 모든 클라이언트에 broadcast --> 실시간 동기화가 가능해짐 +**Room, State**: +MyRoom.ts --> Room 클래스. 플레이어 입장/퇴장, 게임 로직 처리 +MyRoomState.ts --> State 클래스. 점수, 플레이어 목록 등 Room의 State 처리 + + +### 개인 소감 & 회고 + - 이 프로젝트에서 특히 흥미롭거나 배운 점 + 이때까지는 코드의 부분부분만 뜯어보다가, 실제로 구동되는 오픈소스 게임의 폴더 구조와 전체 코드 흐름을 살펴볼 수 있어서 좋았다. 코드 외에도 이펙트나 모델링? 같은 그래픽 요소들은 파일이 어떻게 구성되는지 궁금했었는데 그 부분까지 직접 볼 수 있어 흥미로웠다. + - Colyseus 또는 TypeScript를 활용한 서버 개발에 대한 느낌 + 멀티플레이어 게임 제작의 개념을 새롭게 접할 수 있어서 흥미로웠다. 대체 어떻게 여러 디바이스의 동작이 실시간으로 반영되는 것인지에 대한 궁금증이 조금은 풀렸다. + 다만 콜리세우스를 활용해서 실제로 게임을 어떻게 만드는지는 아직 감이 잘 안 와서 아쉽다. 아무래도 프레임워크를 가볍게 다뤄보는 찍먹 목적의 스터디다 보니 그럴 수밖에 없는 것 같다. 나중에 게임 서버 공부를 좀 더 하고, 다른 프로젝트로 게임 제작 과정을 한 번 겪어 보면 이걸 어떻게 활용해야 할지 감이 잡히지 않을까 생각이 든다. 이번에 제대로 실습을 못 돌려 본 5주차 코드도 그때 다시 만져보고 싶다! + 공부할 키워드를 많이 알게 된 스터디였다ㅎㅎ. 좋은 스터디 기획하고 끝까지 잘 이끌어 주신 윤아언니 무한 감사합니다 넘 수고하셨습니다~~!!! + + + + + + + + + +// 코드분석 + +**scene-blue/src/connection.ts** +colyeseus 서버에 연결 +룸 생성 --> 같은 realm에 속한 플레이어끼리 같은 룸에 모이게 함 +(+연결 관련 기본 기능: +테스트모드 실서버 구분해서 서버 연결, 연결상태 메시지, 기존 연결 상태 확인) +=> 서버랑 통신하는 코드 + +**scene-blue/src/game.ts** +언제 connect()함수 호출할지 관리 (서버 연결 타이밍) +Colyseus 서버 연결 관리 + realm 이동 시 다시 연결 + +cf. realm: 같은 서버 구역에 있는 플레이어끼리 같은 룸에 넣는 용도 + +**scene-blue/src/onConnect.ts** +서버 연결되면 내 플레이어 매니저 초기화 +다른 플레이어 접속 여부 관리 +서버에서 보내는 메시지 처리 +주기적으로 플레이어 상태 체크 +=> 서버 연결 후, 게임 상태 동기화 및 이벤트 처리 (메인 로직인 듯) + +**scene-blue/src/cones.ts** +팀 색깔 결정하는 콘 생성 + +cf. 씬: 하나의 Parcel(서버의 부분적 공간?)에 배치된 독립적 코드 + +**scene-blue/src/MyRoom.ts** +서버에서 눈싸움 방 생성 +팀 나누기 +공 던지기/맞히기 처리 +점수 및 경기 타이머 관리 +경기 결과 Firebase에 저장 + +MyRoom 클래스: 경기/로비 시간 설정, 현재까지 경과 시간 저장 + +OnCreate(): +- 방이 처음 만들어질 때 실행됨 --> 방 상태 초기화 +- this.onMessage()를 이용해 클라이언트가 보내는 메시지를 어떻게 처리할지 정의 +pickColor 팀 선택 +throwBall 눈덩이 던짐(다른 플레이어한테 눈덩이가 날아가는 이벤트 전달) +enemyHit 적 맞혔다고 알림(이후 점수 계산 및 MVP 체크) +getUserColor 특정 적의 팀 색 반환 +startMatch/endMatch 경기 시작/종료 처리 +score 실시간 점수 업데이트 +bestPlayer MVP 클라이언트에 전달 + +update(): +1초마다 실행 반복 +- 경기 중이면 남은 시간 계속 브로드캐스트하기 +- 경기가 끄타면 점수 비교 후 승패 결정 --> 클라이언트에 알리기 +- 로비 중이면 로비 타이머 돌리기, 타이머 끝나면 경기 시작 + +onJoin(): +플레이어가 입장하면 실행됨 +- 새 플레이어 객체 생성 +- 팀 배정 +- 기존 플레이어 목록을 새 유저에게 전송 +- 점수와 경기 상태 동기화 + +onLeave(): 나간 플레이어 제거 + +saveScores(): 점수 저장 + +onDispose(): 방 사라질 때 실행됨 diff --git "a/week6/15 \353\260\225\354\204\234\354\230\201/.gitkeep" "b/week6/15 \353\260\225\354\204\234\354\230\201/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week6/15 \353\260\225\354\261\204\354\234\244/.gitkeep" "b/week6/15 \353\260\225\354\261\204\354\234\244/.gitkeep" new file mode 100644 index 0000000..e69de29 diff --git "a/week6/15 \354\240\225\354\234\244\354\225\204/.gitkeep" "b/week6/15 \354\240\225\354\234\244\354\225\204/.gitkeep" new file mode 100644 index 0000000..e69de29