반응형

챗GPT 갑자기 먹통이 된 것처럼 아무런 반응이 없을 때 해결 방법

 평소 잘 사용하던 챗GPT가 어떤 말을 하여도 아무런 반응이 없습니다. 새로고침(F5)을 눌러봐도, 새 창이나 새 탭에서 다시 페이지를 열어봐도 여전히 아무 변화가 없습니다. 이럴 때 어떻게 하면 챗GPT를 다시 잘 사용할 수 있을까요? 

 

이런 문제는 무료 버전이건, 유료 버전이든 상관없이 가끔씩 발생합니다.

 

아래의 순서대로 하면 아주 쉽게 해결이 됩니다.

 

1. 챗GPT 화면에서 키보드의 F12를 누릅니다.

챗GPT 화면에서 F12 펑션키를 누른 모습

 

2. 우측에 나타나는 메뉴에서 Application을 선택합니다. 해당 메뉴가 보이지 않는다면  >> 표시를 클릭하면 나타납니다.

 

 

 

3. 좌측 메뉴바에서 Local Storage를 클릭합니다.

Clear를 선택하면 끝

 

4. 나타나는 URL 사이트 위에서 마우스를 우클릭한 후 Clear을 누릅니다. 다른 곳에서는 Salesforce, Lightning 및 Visualforce 관련 사이트의 URL을 Clear하라고 설명되어 있지만, 그게 무엇인지 잘 모르니 그냥 다 클리어해 줍니다.

 

5. 다시 챗GPT 사이트를 열어서 명령이나 대화를 시작하면 정상 작동을 합니다.

반응형
반응형

3DMP

 

 

현업 프로그래머분들이 찾아와 레벨업을 하는 과외

이직/자기개발 경쟁력을 갖추고 싶은 분을 위한 과외

 

현업 3D게임엔진 프로그래머가 전수해드리는 고농축 고퀄리티의 과외를 만나 보실 수 있습니다

 

 

 

대기업 또는 중견을 목표로 제대로 해보고 싶으신 분을 위한 프로 과외

 

 

 

[ 실무경력 10y+ ]

  • 넥슨, 크래프톤, NHN, 넷마블 다수 동종/타종 근무경험 및 다년간 현업 개발자
  • 3D,2D게임 엔진,컨텐츠 프로그래머/ 언리얼4,5/네트워크 동기화 프로그래머/애니메이션 메인, 물리엔진개발/
    다수와 다양한 장르의 모바일, PC 게임개발/서버/Shader 등등의 개발 경험/ UE,Unity
  • 미들웨어(엔진)/ 일반 프로그램 프로젝트 제작
  • 고차원 수학/ 물리 들의 알고리즘과 관련된 특수 프로그램 제작
  • 게임업계 대기업 넥슨, 크래프톤, NC, 펄어비스, 중견 B사,  등등의 기업에 실제 합격시킨 노하우
  • 다수, 다양한 분들에 대한 다수의 과외 경험으로 축적된 노하우 보유
  • 기타 일반 프로젝트 경험
  • 과외 등록 완료, 남,  성범죄 "완전 무(無)"!
  • 노하우를 아낌없이 알려드려 한층 더 레벨을 올려드립니다
  • 과외경력 10y+

 

[실무 레벨에서 수강하신 분들]

  • 수강하셨던 분들 : 넥슨 코리아, NC, LG, 넷마블, 넷게임즈, EA 코리아, 크래프톤, XL게임즈, 라이온하트, 펄어비스, 블루홀, 스마일게이트, NHN 등등 의 - 프로그래머/TA/UI, 마이크로소프트 프로그래머 인턴
    , 프로그래머 직군 다수(클라,엔진, 서버) 및 TA 직군
  • 대학생(or 취준생) : 서울대, SKY, 포항공대, 게이오대(일본), 한국외대 등등 유학생 포함

[취직 준비를 위해 수강하신 분들]

  • 최근실적 : N사중 게임프로그래머 부문 1위로 입사 프로그래머를 교육 후  연봉 1600만원+ 인상 이직
  • 무엇이 맞는 것이지 왜 쓰는지에 대한 정확한 답변이 필요 한 분, 이해가 되고난 이후 진행 되길 원하는 분
  • 타교육기관에서 한계를 느끼신 분
  • 비 전공이지만 게임이나 프로그램쪽으로 진로를 희망하시는 분들
    (비전공자 일지라도 노력하신 대다수 분들이 원하는 성과를 이루고 가셨습니다)
  • 전문 프로그래머로 진로를 희망하시는 분들
  • 초심자로 프로그램 배워보려 했지만 진입 장벽때문에 포기하신 분들
  • 그룹(단체) 배워보고자 하시는 분들
  • 게임/비게임 임직원(CEO포함)

 

 [ 통합 진행 과목 ]

 

목표에 맞춰 필요한것들을 함께 선정하여 진행

지속적인 상담을 방향성에 따른 과목들은 상담을 통해 설정됩니다 
과목 추가 대한 추가 비용은 들지 않으며 상담이나 수업 중간 중간 희망하는 방향에 따라 과목 변경 가능


과목들:

  • 실무 3D/2D 실전 수학, 일반 수학, 미들웨어, STL 기반 자료구조와 알고리즘
  • 최적화 알고리즘, 대규모 디자인패턴(설계-디자인)
  • 멀티쓰레드 프로그래밍, 기초 물리(Physics), 캐릭터, 지형 엔진 & Tools,
  • 멀티플레이 게임프로그래밍, 물리시뮬레이션 전문 동기화(난이도 최상)
  • 그래픽스 : shader(HLSL), Unity URP, ShaderLab, 3D 애니메이션
  • 다이렉트X : DirectX9/ DirectX12, Direct 를 통한 실전 수학 활용
  • 게임 엔진 : Unreal 4, Unity3D
  • C언어/C++,C++ 11~,C# 현업 디버깅 스킬 , 시스템 베이스
  • 포트폴리오 과정, 면접 준비
  • 기타 등등..

 

[ 과외 기본 방향성 : 개인 맞춤형 최적화 설정]

 

수업은 수강생 속도의 맞춤형으로 진행됩니다

수업은 수강자분의 현재 상태에 맞는 최적화된 맞춤 방식으로 진행되는 것을 원칙으로 합니다
모든 과목은 목적지는 
혼자만의 생각으로 수학/프로그램을 만들어 나가는 것을 목표로 합니다

현업/면접 또는 프로젝트 에서 중요한 부분 또한 조언 및 컨설팅이 같이 병행되며 경쟁력을 높입니다

 

 [ 과외 안내 ]

  • 진행 방식 :  개인 과외 1:1 진행 방식 / 1:N 방식
    수업은 대부분 수강생이 직접 코드를 작성 하고 대화를 계속 하면서 오프라인과 거의 차이 없이 진행 됩니다
  • 1 Section 기준 = 4주/4회, 추가 섹션은 및 진행은 별도 문의주시면 답변해드립니다
  • 요일 : 협의, 신청자가 많은 경우 대기 기간이 발생 할 수 있습니다
  • 1 회당(1 섹션) 시간 : 1:00, 섹션당 추가에 따른 누적 할인 적용
  • 수강 기간 : 한 달 단위로 수강, 수강생이 희망하는 달까지 진행되며 한달 단위 결제방식입니다
  • 과외 장소/방식 : 원격으로만 진행 (카메라 Off ) 
    원격과 오프라인과의 차이는 다년간 노하우로 거의 없다고 보시면 됩니다
  • 납부 방식 : 한 달 단위, 한달 단위로 연장 하는 방식
  • 그룹 과외  : 그룹(2인 이상은) 별도 문의 , 추가 할인 적용

 

[ 과외 진도 ]

  • 수업은 수강생 속도의 맞춤형으로 진행됩니다
  • 진도는 기본적으로 수강자 분들의 현재 수준을 고려하여 사전 상담을 통하여 
    수강자의 현재 실력기준으로 부터 부족한 부분은 채워 나가고 수강생의 목표에 따라 추가해야할 부분은
    함께 논의 후 진행하는 방향
  • 수강생 수준이 고려된 최적의 선에서 시작, 이미 알고 있는 부분은 중복해서 설명하지 않습니다
  • 진도만을 위한 도서 위주의 수업 또는 깊은 이해가 동반되지 않는 방식 즉 겉핥기로는
    게임/엔진 프로그래머의 문턱에 조차 도달 할 수 없습니다는 점을 기억하세요

 

[수강 특전]  : 3DMP 네트워크

  • 현업 게임 프로그래머들과 함께 지속적인 '3DMP 네트워크 데이' 에서 정보 교류활동의 기회

[문의]

상담시 최대한 오픈마인으로 임해주시면 좋은 시너지 효과가 나올 수 있습니다!

상담전용 카톡 플러스 친구 (별도의 친구 추가 없이 상담 가능)

 

https://pf.kakao.com/_uxabzK

 

상담 전용 카톡 아이디 : 게임프로그래밍 과외

 

 

반응형
반응형

The error message you're encountering indicates that there's an issue with a Git Large File Storage (LFS) object that is preventing your push to GitLab. Specifically, a file (AI/pyvideotrans-main/ffmpeg/you-get.exe) is marked as missing in the LFS, which is causing the push to fail. Here's how you can address this issue:

  1. Verify LFS Objects: First, ensure that all the necessary files are tracked and uploaded correctly in LFS. You can list LFS objects that are supposed to be uploaded with:이you-get.exe file or any other files are indeed missing or incorrectly tracked.
  2. bashCopy code
    git lfs ls-files
  3. Check for Missing Files: If the you-get.exe file is missing from your local repository, you'll need to obtain it again. This could mean downloading it from a source or finding the correct version of the file that should be there.
  4. Re-upload LFS Objects: After you've added or corrected the file in your local repository, you'll need to re-upload the LFS objects. You can do this with:
  5. bashCopy code
    git lfs push --all origin
  6. Consider LFS Locking: The message about LFS locking is a suggestion for managing LFS files, especially in a team environment. Enabling LFS file locking can prevent accidental overwrites when multiple people are working with large files. You can enable it with:
  7. bashCopy code
  8. Retry Pushing: Once you've addressed the missing file and re-uploaded the LFS objects, try pushing to the repository again:
  9. bashCopy code
    git push origin
  10. Consider allowincompletepush Option: As a last resort, if you're certain that the missing LFS object is not critical and you need to push the other changes, you can disable the check that's causing the push to fail. However, this should be used with caution as it can lead to incomplete data in your repository:
  11. bashCopy code
    git config lfs.allowincompletepush true git push origin

Remember, it's important to resolve these issues correctly to ensure that your repository remains consistent and all necessary files are available for you and your team.

 

 

반응형
반응형

 

 

 

In Python, the error message "Defaulting to user installation because normal site-packages is not writeable" usually appears when you try to install packages without having the necessary permissions to write to the system-wide site-packages directory.

Occurrence

This commonly occurs in virtual environments or when you lack administrative access.

Error-resolving approaches

To resolve this error, you can use the following approaches:

  1. Using a virtual environment

First, create a virtual environment using tools like venv or virtualenv using this command in your terminal:

 
 

Now, activate the virtual environment using these commands:

 
 

Finally, install packages within the virtual environment using this command in your terminal:

 
 

This way, you will have write access to the virtual environment's site-packages directory without requiring system-wide permissions.

  1. Specifying a user installation

You can install the package in the user’s local site-packages directory using this command:

 
 

Use the --user flag when installing packages to specify a user installation instead of a system-wide installation.

  1. Using a package manager

If you're using Linux, you can use system-wide package management like apt or yum to install Python packages. Typically, administrative rights are needed for this.

 
 

Hence, these approaches provide you with the necessary write permissions to install packages in a controlled and isolated manner.

 

 

 

ref : https://www.educative.io/answers/how-to-resolve-normal-site-packages-is-not-writable-in-python

반응형
반응형

 

 

현업 게임프로그래밍 과외

 

 

본 과외는 현업 대기업 계열 프로그래머들이 듣는 과외이기도 하며
프로그래머가 프로그래머를 가리치는 성장을 원하는 분들을 위한 과외 입니다

Feat.초보/취준생이신 분들 가능합니다

 

  • 이직시 코어 경쟁력을 갖춰드립니다
  • 현업에 종사하는 직원이지만 자신의 역량을 한층 더 레벨업 시키고 싶으신 분들에게 역량을 키워드립니다
  • 취준생으로 취직을 목표로 하시는 분
  • 노베이스지만 취직에 열망이 있는 분들에겐 신입으로서 충분한 경쟁력을 갖춰드립니다

 

[ 실무경력 10y+ ]

  • 넥슨, 크래프톤, NHN, 넷마블 다수 동종/타종 근무경험 및 다년간 현업 개발자
  • 3D,2D게임 엔진,컨텐츠 프로그래머/ 언리얼4,5/네트워크 동기화 프로그래머/애니메이션 메인, 물리엔진개발/
    다수와 다양한 장르의 모바일, PC 게임개발/서버/Shader 등등의 개발 경험/ UE,Unity
  • 미들웨어(엔진)/ 일반 프로그램 프로젝트 제작
  • 고차원 수학/ 물리 들의 알고리즘과 관련된 특수 프로그램 제작
  • 게임업계 대기업 넥슨, 크래프톤, NC, 펄어비스, 중견 B사,  등등의 기업에 합격시킨 노하우
  • 다수, 다양한 분들에 대한 다수의 과외 경험으로 축적된 노하우 보유
  • 기타 일반 프로젝트 경험
  • 과외 등록 완료, 남,  성범죄 "완전 무(無)"!
  • 노하우를 아낌없이 알려드려 한층 더 레벨을 올려드립니다
  • 실무경력 10y+ , 과외경력 10y+

 

[이직/취직/수강하신 분들의 회사]

  • 현직이지만 자신의 역량을 한층 더 레벨업 시키고 싶으신 분
  • 수강하셨던 분들 : 넥슨 코리아, NC, 넷마블, 넷게임즈, EA 코리아, 크래프톤, XL게임즈, 라이온하트
    프로그래머 다수(클라,엔진, 서버) 및 TA
    펄어비스, 블루홀, 스마일게이트, NHN 등등 의 - 프로그래머/TA/UI,
    마이크로소프트 프로그래머 인턴
  • 대학생(or 취준생) : 서울대, SKY, 포항공대, 게이오대(일본), 포함 상위 10% 이상 대학생, 유학생
    그리고 중/하위권 에서도 포기하지 않고 노력했을때 대기업 또는 목표치를 이룬 선례들이 다수
  • 전공자/컴퓨터 관련 전공자가 아니지만 게임이나 프로그램쪽으로 진로를 희망하시는 분들
    (비전공자 일지라도 대다수 분들이 원하는 성과를 이루고 가셨습니다)
  • 전문 프로그래머로 진로를 희망하시는 분들
  • 초심자로 프로그램 배워보려 했지만 진입 장벽때문에 포기하신 분들
  • 그룹(단체) 배워보고자 하시는 분들
  • 게임/비게임 임직원(CEO포함)
  • 무엇이 맞는 것이지 왜 쓰는지에 대한 정확한 답변이 필요 한 분, 이해가 되고난 이후 진행 되길 원하는 분
  • 타교육기관에서 한계를 느끼신 분
  • 최근실적 : N사중 게임프로그래머 부문 1위로 입사 프로그래머를 교육 후  연봉 1600만원+ 인상 이직


 
[ 통합 진행 과목 ] : 수강생 위주로 필요한 과목을 협의하여 진행

방향에 따른 과목들은 상담을 통해 함께 설정 합니다
추가 과목에 대한 추가 비용은 들지 않으며 상담이나 수업 중간 수강생의 희망 목표에 따른 과목 변경이 가능합니다

  • 현업 3D 실전 수학, 일반 수학, 2D수학, 미들웨어, STL 기반 자료구조와 알고리즘
  • 최적화 알고리즘, 대규모 디자인패턴(설계-디자인)
  • 멀티쓰레드 프로그래밍, 기초 물리(Physics), 캐릭터, 지형 엔진 & Tools,
  • 멀티플레이 게임프로그래밍, 물리시뮬레이션 전문 동기화(난이도 최상)
  • 그래픽스 : shader(HLSL), Unity URP, ShaderLab, 3D 애니메이션
  • 다이렉트X : DirectX9/ DirectX12, Direct 를 통한 실전 수학 활용
  • 게임 엔진 : Unreal 4, Unity3D
  • C언어/C++,C++ 11~,C# 현업 디버깅 스킬 , 시스템 베이스
  • 포트폴리오 과정
  • 면접대비 및 준비


 [ 과외 안내 ]

  • 진행 방식 :  개인 과외 1:1 진행 방식 / 1:N 방식
    원격제어로 진행 되며 수업은 대부분 수강생이 직접 코드를 작성 하고 대화를 계속 하면서 오프라인과 거의 차이 없이 진행 됩니다
  • 1 Section 기준 = 4주/4회, 추가 섹션은 및 진행은 별도 문의주시면 답변해드립니다
  • 요일 : 협의, 신청자가 많은 경우 대기 기간이 발생 할 수 있습니다
  • 1 회당(1 섹션) 시간 : 1:00, 섹션당 추가에 따른 누적 할인 적용
  • 수강 기간 : 한 달 단위로 수강, 수강생이 희망하는 달까지 진행되며 한달 단위 결제방식입니다
  • 과외 장소/방식 : 원격으로만 진행 (카메라 Off ) 
    원격과 오프라인과의 차이는 다년간 노하우로 거의 없다고 보시면 됩니다
  • 납부 방식 : 한 달 단위, 한달 단위로 연장 하는 방식
  • 그룹 과외  : 그룹(2인 이상은) 별도 문의 , 추가 할인 적용

 

[ 과외 기본 방향성 : 개인 맞춤형 최적화 설정]

 

수업은 수강자분의 현재 상태에 맞는 최적화된 맞춤 방식으로 진행되는 것을 원칙으로 합니다
모든 과목은 목적지는 
혼자만의 생각으로 수학/프로그램을 만들어 나가는 것을 목표로 합니다

현업/면접 또는 프로젝트 에서 중요한 부분 또한 조언 및 컨설팅이 같이 병행되며 경쟁력을 높입니다

  • 진도는 기본적으로 수강자 분들의 현재 수준을 고려하여 사전 상담을 통하여 
    수강자의 현재 실력기준으로 부터 부족한 부분은 채워 나가고 수강생의 목표에 따라 추가해야할 부분은
    함께 논의 후 진행하는 방향으로 진행됩니다
    • 수강자 분의 수준이 고려된 최적의 선에서 시작, 이미 알고 있는 부분은 중복해서 설명하지 않습니다
    • 진도만을 위한 도서 위주의 수업 또는 깊은 이해가 동반되지 않는 방식 즉 겉핥기로는
      게임/엔진 프로그래머의 문턱에 조차 도달 할 수 없습니다는 점을 기억하세요
  • 수업은 수강생 속도의 맞춤형으로 진행됩니다
    • 이해가 빠른 편이라면 그만큼 빠르게 진행될수 있고 반대의 케이스도 마찬가지 입니다
      단, 이해의 속도가 따라가지 못한다면 역효과가 날수 있다는 점만 주의하시면 되고 그에 속도에 맞춰이해가 될 때까지 다양한 방식으로 설명하고 넘어가는 방식을 추구 합니다
    • 수강생의 수준에 따라 해당 과목에서의 적절한 수준을 찾거나 과목이 변경 될 수는 있지만
      끝까지 해내겠다는 자세를 갖는다면 결국엔 희망하는 곳까지 갈 수 있습니다

 

 

[수강 특전]  : 3DMP 네트워크 [선택]

  • 현업 게임 프로그래머들과 함께 지속적인 '3DMP 네트워크 데이' 에서 정보 교류활동의 기회

[문의]

3DMP

 

 

상담시 최대한 오픈마인으로 임해주시면 좋은 시너지 효과가 나올 수 있습니다!

상담전용 카톡 플러스 친구 (별도의 친구 추가 없이 상담 가능)

 

https://pf.kakao.com/_uxabzK

 

상담 전용 카톡 아이디 : 게임프로그래밍 과외

 

 

반응형
반응형

3DMP

 

 

현업 프로그래머분들이 찾아와 레벨업을 하는 과외

이직 경쟁력을 갖추고 싶으신 분을 위한 과외 입니다

 

 

 

현업 3D게임엔진 프로그래머가 전수해드리는 고농축 고퀄리티의 과외를 만나 보실 수 있습니다

 

 

 

 

[ 실무경력 10y+ ]

  • 넥슨, 크래프톤, NHN, 넷마블 다수 동종/타종 근무경험 및 다년간 현업 개발자
  • 3D,2D게임 엔진,컨텐츠 프로그래머/ 언리얼4,5/네트워크 동기화 프로그래머/애니메이션 메인, 물리엔진개발/
    다수와 다양한 장르의 모바일, PC 게임개발/서버/Shader 등등의 개발 경험/ UE,Unity
  • 미들웨어(엔진)/ 일반 프로그램 프로젝트 제작
  • 고차원 수학/ 물리 들의 알고리즘과 관련된 특수 프로그램 제작
  • 게임업계 대기업 넥슨, 크래프톤, NC, 펄어비스, 중견 B사,  등등의 기업에 합격시킨 노하우
  • 다수, 다양한 분들에 대한 다수의 과외 경험으로 축적된 노하우 보유
  • 기타 일반 프로젝트 경험
  • 과외 등록 완료, 남,  성범죄 "완전 무(無)"!
  • 노하우를 아낌없이 알려드려 한층 더 레벨을 올려드립니다
  • 과외경력 10y+

 

[이직/취직/수강하신 분들의 회사]

  • 현직이지만 자신의 역량을 한층 더 레벨업 시키고 싶으신 분
  • 수강하셨던 분들 : 넥슨 코리아, NC, 넷마블, 넷게임즈, EA 코리아, 크래프톤, XL게임즈, 라이온하트
    프로그래머 다수(클라,엔진, 서버) 및 TA
    펄어비스, 블루홀, 스마일게이트, NHN 등등 의 - 프로그래머/TA/UI,
    마이크로소프트 프로그래머 인턴
  • 대학생(or 취준생) : 서울대, SKY, 포항공대, 게이오대(일본), 포함 상위 10% 이상 대학생, 유학생
    그리고 중/하위권 에서도 포기하지 않고 노력했을때 대기업 또는 목표치를 이룬 선례들이 다수
  • 전공자/컴퓨터 관련 전공자가 아니지만 게임이나 프로그램쪽으로 진로를 희망하시는 분들
    (비전공자 일지라도 대다수 분들이 원하는 성과를 이루고 가셨습니다)
  • 전문 프로그래머로 진로를 희망하시는 분들
  • 초심자로 프로그램 배워보려 했지만 진입 장벽때문에 포기하신 분들
  • 그룹(단체) 배워보고자 하시는 분들
  • 게임/비게임 임직원(CEO포함)
  • 무엇이 맞는 것이지 왜 쓰는지에 대한 정확한 답변이 필요 한 분, 이해가 되고난 이후 진행 되길 원하는 분
  • 타교육기관에서 한계를 느끼신 분
  • 최근실적 : N사중 게임프로그래머 부문 1위로 입사 프로그래머를 교육 후  연봉 1600만원+ 인상 이직

 [ 통합 진행 과목 ] : 목표에 맞춰 필요하거나  희망하는 것들을 함께 선택하여 진행합니다

방향에 따른 과목들은 상담을 통해 설정됩니다 
추가 과목에 대한 추가 비용은 들지 않으며 상담이나 수업 중간 중간 희망하는 진로/방향에 따라 과목들이 변경될 수 있습니다

  • 현업 3D 실전 수학, 일반 수학, 2D수학, 미들웨어, STL 기반 자료구조와 알고리즘
  • 최적화 알고리즘, 대규모 디자인패턴(설계-디자인)
  • 멀티쓰레드 프로그래밍, 기초 물리(Physics), 캐릭터, 지형 엔진 & Tools,
  • 멀티플레이 게임프로그래밍, 물리시뮬레이션 전문 동기화(난이도 최상)
  • 그래픽스 : shader(HLSL), Unity URP, ShaderLab, 3D 애니메이션
  • 다이렉트X : DirectX9/ DirectX12, Direct 를 통한 실전 수학 활용
  • 게임 엔진 : Unreal 4, Unity3D
  • C언어/C++,C++ 11~,C# 현업 디버깅 스킬 , 시스템 베이스
  • 포트폴리오 과정

 

[ 과외 기본 방향성 : 개인 맞춤형 최적화 설정]

 

수업은 수강자분의 현재 상태에 맞는 최적화된 맞춤 방식으로 진행되는 것을 원칙으로 합니다
모든 과목은 목적지는 
혼자만의 생각으로 수학/프로그램을 만들어 나가는 것을 목표로 합니다

현업/면접 또는 프로젝트 에서 중요한 부분 또한 조언 및 컨설팅이 같이 병행되며 경쟁력을 높입니다

  • 진도는 기본적으로 수강자 분들의 현재 수준을 고려하여 사전 상담을 통하여 
    수강자의 현재 실력기준으로 부터 부족한 부분은 채워 나가고 수강생의 목표에 따라 추가해야할 부분은
    함께 논의 후 진행하는 방향으로 진행됩니다
    • 수강자 분의 수준이 고려된 최적의 선에서 시작, 이미 알고 있는 부분은 중복해서 설명하지 않습니다
    • 진도만을 위한 도서 위주의 수업 또는 깊은 이해가 동반되지 않는 방식 즉 겉핥기로는
      게임/엔진 프로그래머의 문턱에 조차 도달 할 수 없습니다는 점을 기억하세요
  • 수업은 수강생 속도의 맞춤형으로 진행됩니다
    • 이해가 빠른 편이라면 그만큼 빠르게 진행될수 있고 반대의 케이스도 마찬가지 입니다
      단, 이해의 속도가 따라가지 못한다면 역효과가 날수 있다는 점만 주의하시면 되고 그에 속도에 맞춰이해가 될 때까지 다양한 방식으로 설명하고 넘어가는 방식을 추구 합니다
    • 수강생의 수준에 따라 해당 과목에서의 적절한 수준을 찾거나 과목이 변경 될 수는 있지만
      끝까지 해내겠다는 자세를 갖는다면 결국엔 희망하는 곳까지 갈 수 있습니다

 

 [ 과외 안내 ]

  • 진행 방식 :  개인 과외 1:1 진행 방식 / 1:N 방식
    수업은 대부분 수강생이 직접 코드를 작성 하고 대화를 계속 하면서 오프라인과 거의 차이 없이 진행 됩니다
  • 1 Section 기준 = 4주/4회, 추가 섹션은 및 진행은 별도 문의주시면 답변해드립니다
  • 요일 : 협의, 신청자가 많은 경우 대기 기간이 발생 할 수 있습니다
  • 1 회당(1 섹션) 시간 : 1:00, 섹션당 추가에 따른 누적 할인 적용
  • 수강 기간 : 한 달 단위로 수강, 수강생이 희망하는 달까지 진행되며 한달 단위 결제방식입니다
  • 과외 장소/방식 : 원격으로만 진행 (카메라 Off ) 
    원격과 오프라인과의 차이는 다년간 노하우로 거의 없다고 보시면 됩니다
  • 납부 방식 : 한 달 단위, 한달 단위로 연장 하는 방식
  • 그룹 과외  : 그룹(2인 이상은) 별도 문의 , 추가 할인 적용

 

[수강 특전]  : 3DMP 네트워크

  • 현업 게임 프로그래머들과 함께 지속적인 '3DMP 네트워크 데이' 에서 정보 교류활동의 기회

[문의]

상담시 최대한 오픈마인으로 임해주시면 좋은 시너지 효과가 나올 수 있습니다!

상담전용 카톡 플러스 친구 (별도의 친구 추가 없이 상담 가능)

 

https://pf.kakao.com/_uxabzK

 

상담 전용 카톡 아이디 : 게임프로그래밍 과외

 

 

반응형
반응형

 

3DMP

 

 

 

 

[ 실무경력 10y+ ]

  • 넥슨, 크래프톤, NHN, 넷마블 다수 동종/타종 근무경험 및 다년간 현업 개발자
  • 3D,2D게임 엔진,컨텐츠 프로그래머/ 언리얼4,5/네트워크 동기화 프로그래머/애니메이션 메인, 물리엔진개발/
    다수와 다양한 장르의 모바일, PC 게임개발/서버/Shader 등등의 개발 경험/ UE,Unity
  • 미들웨어(엔진)/ 일반 프로그램 프로젝트 제작
  • 고차원 수학/ 물리 들의 알고리즘과 관련된 특수 프로그램 제작
  • 게임업계 대기업 넥슨, 크래프톤, NC, 펄어비스, 중견 B사,  등등의 기업에 합격시킨 노하우
  • 다수, 다양한 분들에 대한 다수의 과외 경험으로 축적된 노하우 보유
  • 기타 일반 프로젝트 경험
  • 과외 등록 완료, 남,  성범죄 "완전 무(無)"!
  • 노하우를 아낌없이 알려드려 한층 더 레벨을 올려드립니다
  • 과외경력 10y+

 

[이직/취직/수강하신 분들의 회사]

  • 현직이지만 자신의 역량을 한층 더 레벨업 시키고 싶으신 분
  • 수강하셨던 분들 : 넥슨 코리아, NC, 넷마블, 넷게임즈, EA 코리아의 - 프로그래머 다수/TA/기획자
    크래프톤, 펄어비스, 블루홀의 - 프로그래머/TA/기획자
    스마일게이트, NHN, 기타 등등 의 - 프로그래머/TA/기획자/UI
  • 대학생(or 취준생) : 서울대, SKY, 포항공대, 게이오대(일본),
    포함 상위 10% 이상 대학생,그 외 글로벌 학교 및 유학생 포함
    그리고 중/하위권 에서도 포기하지 않고 노력했을때 대기업에 간 선례들이 다수
  • 전공자/컴퓨터 관련 전공자가 아니지만 게임이나 프로그램쪽으로 진로를 희망하시는 분들
    (비전공자 일지라도 대다수 분들이 원하는 성과를 이루고 가셨습니다)
  • 전문 프로그래머로 진로를 희망하시는 분들
  • 초심자로 프로그램 배워보려 했지만 진입 장벽때문에 포기하신 분들
  • 그룹(단체) 배워보고자 하시는 분들
  • 게임/비게임 임직원(CEO포함)
  • 무엇이 맞는 것이지 왜 쓰는지에 대한 정확한 답변이 필요 한 분, 이해가 되고난 이후 진행 되길 원하는 분
  • 타교육기관에서 한계를 느끼신 분

 

 [ 통합 진행 과목 ] : 과목은 수강생의 니즈에 맞게 협의 선택하여 진행


방향에 따른 과목들은 상담을 통해 설정됩니다 
추가 과목에 대한 추가 비용은 들지 않으며 상담이나 수업 중간 중간 희망하는 진로/방향에 따라 과목들이 변경될 수 있습니다

  • 현업 3D 실전 수학, 일반 수학, 2D수학, 미들웨어, STL 기반 자료구조와 알고리즘
  • 최적화 알고리즘, 대규모 디자인패턴(설계-디자인)
  • 멀티쓰레드 프로그래밍, 기초 물리(Physics), 캐릭터, 지형 엔진 & Tools,
  • 멀티플레이 게임프로그래밍, 물리시뮬레이션 전문 동기화(난이도 최상)
  • 그래픽스 : shader(HLSL), Unity URP, ShaderLab, 3D 애니메이션
  • 다이렉트X : DirectX9/ DirectX12, Direct 를 통한 실전 수학 활용
  • 게임 엔진 : Unreal 4, Unity3D
  • C언어/C++,C++ 11~,C# 현업 디버깅 스킬 , 시스템 베이스
  • 포트폴리오 과정
  • 실무/인사 면접 대비 과정

 

[ 과외 기본 방향성 : 개인 맞춤형 최적화 설정]

    • 원격제어 진행되고 수강생의 PC 에서 수업이 진행 되고 오프라인과 거의 차이가없는 방식으로 진행 됩니다
    • 진도만 빠르게 나가는 것은 실력향상에 도움이 전혀 되지 않기 때문에 수업 도중 궁금한것이 생기면 이해 할 수 있을때까지 반복하여 설명이 진행 됩니다(화를 내지 않는 스타일)
    • 현재 잘 알고 있는 것은 빠르게 지나가고 잘 모르는 부분이나 오개념에 대해선 바로 잡으며 진행 됩니다
    • 대기업에 간 선례들이 많은 만큼 이를 베이스로 실력이 차오르는 단계에 맞게 노하우를 모두 전수해 드립니다

 

 [ 과외 안내 ]

  • 진행 방식 :  원격제어, 1:1 진행 방식 / 1:N 방식
    수업은 대부분 수강생이 직접 코드를 작성 하고 대화를 계속 하면서 오프라인과 거의 차이 없이 진행 됩니다
  • 1 Section 기준 = 4주/4회, 추가 섹션은 및 진행은 별도 문의주시면 답변해드립니다
  • 요일 : 협의, 신청자가 많은 경우 대기 기간이 발생 할 수 있습니다
  • 1 회당(1 섹션) 시간 : 1:00, 섹션당 추가에 따른 누적 할인 적용
  • 수강 기간 : 한 달 단위로 수강, 수강생이 희망하는 달까지 진행되며 한달 단위 결제방식입니다
  • 과외 장소/방식 : 원격으로만 진행 (카메라 Off ) 
    원격과 오프라인과의 차이는 다년간 노하우로 거의 없다고 보시면 됩니다
  • 납부 방식 : 한 달 단위, 한달 단위로 연장 하는 방식
  • 그룹 과외  : 그룹(2인 이상은) 별도 문의 , 추가 할인 적용

[문의]

상담시 최대한 오픈마인으로 임해주시면 좋은 시너지 효과가 나올 수 있습니다!

상담전용 카톡 플러스 친구 (별도의 친구 추가 없이 상담 가능)

https://pf.kakao.com/_uxabzK

상담 전용 카톡 아이디 : 게임프로그래밍 과외

 

 

반응형
반응형

 

Hash Motivation

 

Direct Access Array

Hashing

 

Chaining

chaining 은 해쉬 테이블이 아닌 다른 자료 구조에 아이템을 저장해서 충돌을 해결하는 방법입니다. 해쉬 테이블의 각 인덱스에는 아이템이 아닌 또 다른 자료 구조를 가르키는 포인터가 저장됩니다. 이 자료 구조는 insert, search, 그리고 delete(k) 를 상수 비용으로 지원하는 Set Interface 이고 chain 이라고 합니다. linked list 나 array 를 생각하면 됩니다.

충돌이 발생해도 모든 해쉬 테이블의 chain 의 크기가 상수 이면 insert, serach, 그리고 delete(k) 를 O(1) 으로 수행할 수 있게 되고 이것이 hashing 을 통해 달성하고자 하는 목표인데요. 이를 위해서는 해쉬 함수가 중요합니다. 만약에 아주 안좋은 해쉬 함수를 선택해서 모든 키의 해쉬 값이 동일한 인덱스를 가진다면, 해당 인덱스 chain 의 크기는 O(n) 이 되고 insert, serach, 그리고 delete(k) 의 비용도 O(n) 이 되버립니다. 최악이죠.

 

이상적으로 어떠한 키 유니버스가 주어져도 충돌의 개수를 줄여서 테이블의 가장 큰 chain 의 크기가 상수인 해쉬 함수를 선택해야 합니다.

 

Universal Hashing

좋은 해쉬 함수의 기준을 다시 한번 정리하겠습니다.

 

어떠한 키 유니버스가 주어져도 충돌의 개수를 줄여서 가장 큰 chain 의 크기를 상수로 만드는 해쉬 함수

Dot Product Hash Family

 

참조

https://www.youtube.com/watch?v=Nu8YGneFCWE&list=PLUl4u3cNGP63EdVPNLG3ToM6LaEUuStEY&index=5

https://www.youtube.com/watch?v=z0lJ2k0sl1g&list=PLTV7NgF818VXpV2P-Px2H74hvEBL2o-G2&index=11 

 

 

ref : https://klioop.tistory.com/43

반응형
반응형

ComPtr com 객체들을 받아주는 스마트포인터 레퍼런스카운트가 0 이 될때 삭제된다

 

IID_PPV_ARGS

id 와 포인터를 만들어내는 define

위 함수가 IID_PPV_ARGS 를 받고 있는데 이것은 id 와 이중 포인터를 받고 있는 인자임을 알수 있고 IID_PPV_ARGS는 두개의인자를 넘겨주기 수월하게 만드는 define 이다

 

 

IDXGIFactory 디바이스 출력과 관련된 부분들 ex 스왑체인

 

DX12 에선 View ==  Descriptor 과 같은 의미로 통용된다

 

ID3D12DesecriptorHeap 디스크립터 힙 

 

D3D12_CPU_DESCRIPTOR_HANDLE 리소스 버퍼의 정보를 담고 있는 핸들(리소스를 gpu 에 알려주기 위한 리소스 주소 등을 담고 있음)

 

 

 

 

반응형
반응형

 

Multithreading your game has the potential to massively increase performance. You are getting an entire new thread to work with, so you can offload heavy tasks to your worker thread while your game thread keeps running at a high framerate.

Getting started with multithreading is daunting, at least it was for me, so I'm writing this to help out any fellow devs who might be interested in using this for your own project.

First off: is multithreading the right solution for you?The use case is quite limited, since there are a lot of things you can't do on the other thread. You can't read or write data that lives on your game thread, this includes modifying any UObjects. Doing so will result in undefined behavior - usually access violation errors.

So, what can you use it for? Think of it like an isolated part of your code, you can give it inputs, and then it will go off on it's own and calculate something only using the inputs you gave it. You can only (easily) multithread parts of your code that can be decoupled from the rest of the program.

For example, the way I used it was to calculate a custom lightmap using a recursive flood fill algorithm, the result is a dynamic 2D lightmap like the one in Terraria. First, the game thread prepares the data for the worker thread; it gets the details of every light on screen. Then, it gives the data to the worker thread, which performs the expensive calculations. Finally, the worker thread gives back the finished lightmap, and the cycle continues.

If you think your expensive task can be performed like this, you're in luck, because this guide will show you exactly how to implement it - without worrying about locking variables and mutexes.

Flip flop threading paradigm

I have no idea if that's a real paradigm, but that's what I'm going to call this system I created. This paradigm can be summarized by one video:

 

The game thread is the person flicking the switch, and the worker thread is the thing that switches it off. The switch itself is a boolean.

Basically, when the switch is off, the worker thread does nothing but check if the switch is on. This is when the game thread reads and writes to the worker thread. Once the game thread has finished, it flips the switch to on. When the worker thread sees that it's on, it will perform all of the calculations using the fresh data is received, and flips the switch off once it has finished.

Let's see how that looks in code.

First, let's lay out the foundations to get a FRunnable thread up and running. It won't do anything yet, other than log messages so we can see it working.

Header File

// Copyright notice

#pragma once

#include "CoreMinimal.h"
#include "HAL/Runnable.h"

/**
 *
 */
class GAMENAME_API FMyWorker : public FRunnable
{
public:

	// Constructor, create the thread by calling this
	FMyWorker();

	// Destructor
	virtual ~FMyWorker() override;


	// Overriden from FRunnable
	// Do not call these functions youself, that will happen automatically
	bool Init() override; // Do your setup here, allocate memory, ect.
	uint32 Run() override; // Main data processing happens here
	void Stop() override; // Clean up any memory you allocated here


private:

	// Thread handle. Control the thread using this, with operators like Kill and Suspend
	FRunnableThread* Thread;

	// Used to know when the thread should exit, changed in Stop(), read in Run()
	bool bRunThread;
};

C++ File

// Copyright notice

#include "MyWorker.h" // Change this to reference the header file above


#pragma region Main Thread Code
// This code will be run on the thread that invoked this thread (i.e. game thread)


FMyWorker::FMyWorker(/* You can pass in inputs here */)
{
	// Constructs the actual thread object. It will begin execution immediately
	// If you've passed in any inputs, set them up before calling this.
	Thread = FRunnableThread::Create(this, TEXT("Give your thread a good name"));
}


FMyWorker::~FMyWorker()
{
	if (Thread)
	{
		// Kill() is a blocking call, it waits for the thread to finish.
		// Hopefully that doesn't take too long
		Thread->Kill();
		delete Thread;
	}
}


#pragma endregion
// The code below will run on the new thread.


bool FMyWorker::Init()
{
	UE_LOG(LogTemp, Warning, TEXT("My custom thread has been initialized"))

	// Return false if you want to abort the thread
	return true;
}


uint32 FMyWorker::Run()
{
	// Peform your processor intensive task here. In this example, a neverending
	// task is created, which will only end when Stop is called.
	while (bRunThread)
	{
		UE_LOG(LogTemp, Warning, TEXT("My custom thread is running!"))
		FPlatformProcess::Sleep(1.0f);
	}

	return 0;
}


// This function is NOT run on the new thread!
void FMyWorker::Stop()
{
	// Clean up memory usage here, and make sure the Run() function stops soon
	// The main thread will be stopped until this finishes!

	// For this example, we just need to terminate the while loop
	// It will finish in <= 1 sec, due to the Sleep()
	bRunThread = false;
}

Creating and controlling the thread

This part is easy, just create an instance of the class above using regular C++:

FMyWorker* Worker = new FMyWorker();

The example is set up to run straight away, so if you want to pass it some data, you can either use the constructor, or move the Create() command to another function you can invoke when ready.

Remember to hold onto the pointer so you can delete it when no longer needed.

When finished, use the normal delete command:

delete Worker;

When you've done this, you should have a new thread that announces when it has been created, and will print a message to the log every second. The code should be stable and crash free.

Implementing the Flip Flop

Now that we have a basic thread, we can give it more functionality.

We need to create the boolean that acts as the switch, and the loop that reads this bool.

It's fairly straightforward, here is the code you need to add:

Header File

class GAMENAME_API FMyWorker : public FRunnable
{
public:

	[...]

	// ----------


	// The boolean that acts as the main switch
	// When this is false, inputs and outputs can be safely read from game thread
	bool bInputReady = false;


	// Declare the variables that are the inputs and outputs here.
	// You can have as many as you want. Remember not to use pointers, this must be
	// plain old data
	// For example:
	int ExampleIntInput = 0;
	float ExampleFloatOutput = 0.0f;


private:

	[...]
};

C++ File

uint32 FMyWorker::Run()
{
	// Peform your processor intensive task here. In this example, a neverending
	// task is created, which will only end when Stop is called.
	while (bRunThread)
	{
		if (bInputReady)
		{
			// Do your intensive tasks here. You can safely modify all input variables.
			// For the example, I'm just going to convert the input int to a float
			ExampleFloatOutput = ExampleIntInput;
			FPlatformProcess::Sleep(1.0f); // Simulate a heavy workload

			// Do this once you've finished using the input/output variables
			// From this point onwards, do not touch them!
			bInputReady = false;

			// I hear that it's good to let the thread sleep a bit, so the OS can utilise it better or something.
			FPlatformProcess::Sleep(0.01f);
		}

	}

	return 0;
}

That's all you need on the thread side of things. To access it from the game thread, do something like this:

 

// You need a function like Tick. You can also use timers
void AMyClass::Tick(float DeltaTime)
{
	if (Worker)
	{
		if (!Worker->bInputReady)
		{
			// Read and write the variables here
			Worker->ExampleIntInput = 3141592;
			UE_LOG(LogTemp, Warning, TEXT("Game thread: value gotten from worker thread: %f"), Worker->ExampleFloatOutput)


			// Flick the switch to let the worker thread run
			// Do not read/write variables from thread from here on!
			Worker->bInputReady = true;
		}
	}
}

Note: the variable Worker needs to be initialized before, like in BeginPlay(). Scroll up a bit to see how to initialize it.

 

ref : https://unrealcommunity.wiki/multithreading-with-frunnable-2a4xuf68

 

반응형
반응형

 [정원초과 마감]

3DMP

 

 

수학/프로그램은 샤프를 들기 이전에 상상에서부터 시작되어야 합니다!!

 

현업 3D게임엔진 프로그래머가 전수해드리는 고농축 고퀄리티의 과외를 만나 보실 수 있습니다

 

 

※ 제대로 된 곳이 아닌 다른곳에서 어설프게 배웠거나 이해가 완벽하게 가지 않은 상태에서 계속 진행이 되어
시간과 비용만 낭비만 한채 또는 중도에 포기한 채 이곳을 찾아 93% 이상 분들이 원하는 결과를 얻고 가셨습니다
요행 길이라는 것은 없으며 제대로 된 곳에서 제대로된 진로를 고민해보시기 바랍니다.

 

 

이력 ]

  • 넥슨, 크래프톤, NHN, 넷마블 다수 동종/타종 근무경험 및 다년간 현업 개발자
  • 3D,2D게임 엔진,컨텐츠 프로그래머/ 언리얼4,5/네트워크 동기화 프로그래머/애니메이션 메인, 물리엔진개발/
    다수와 다양한 장르의 모바일, PC 게임개발/서버/Shader 등등의 개발 경험/ UE,Unity
  • 미들웨어(엔진)/ 일반 프로그램 프로젝트 제작
  • 고차원 수학/ 물리 들의 알고리즘과 관련된 특수 프로그램 제작
  • 게임업계 대기업 넥슨, 크래프톤, NC, 펄어비스, 중견 B사,  등등의 기업에 합격시킨 노하우
  • 다수, 다양한 분들에 대한 다수의 과외 경험으로 축적된 노하우 보유
  • 기타 일반 프로젝트 경험
  • 과외 등록 완료, 남,  성범죄 "완전 무(無)"!

 

[이직/취직/수강하신 분들의 회사]

  • 넥슨 코리아, NC, 넷마블, 넷게임즈, EA 코리아의 - 프로그래머 다수/TA/기획자
  • 크래프톤, 펍지, 펄어비스, 블루홀의 - 프로그래머/TA/기획자
  • 스마일게이트, NHN, 기타 등등 의 - 프로그래머/TA/기획자/UI
  • 게임/비게임 임직원, CEO포함
  • 대학생(취준생), SKY, 포함 상위 10% 이상 대학생, 그 외 글로벌 학교 및 유학생
    하위권 에서도 포기하지 않고 노력했을때 대기업에 간 선례들이 많이 있습니다, 포기하지 마세요!
  • 전문 프로그래머로 진로를 희망하시는 분들
  • 전공자/컴퓨터 관련 전공자가 아니지만 게임이나 프로그램쪽으로 진로를 희망하시는 분들
    (비전공자 일지라도 많은 상당수의 분들이 원하는 성과를 이루고 가셨습니다)
  • 프로그램을 혼자 하기엔 막연하여 그룹(단체) 배워보고자 하시는 분들
  • 초심자로 프로그램 배워보려 했지만 진입 장벽때문에 포기하신 분들
  • 질문에 대한 답 : 무엇이 맞는지 모르겠는 것에 대한 정확한 답변이 필요 한 분
  • 타교육기관에서 한계를 느끼신 분
  • 현직이지만 자신의 역량을 한층 더 레벨업 시키고 싶으신 분
  • 최근실적 : N사중 게임프로그래머 부문 1위로 입사
    프로그래머를 교육 후  연봉 1600만원 인상 이직
  • 노하우를 아낌없이 알려드려 한층 더 레벨을 올려드립니다

 [ 통합 진행 과목] : 필요한 것들을 협의 선택하여 진행

방향에 따른 과목들은 상담을 통해 설정됩니다 
추가 과목에 대한 추가 비용은 들지 않으며 방향에 따라 과목들이 정해집니다

  • 현업 3D 실전 수학, 일반 수학, 2D수학, 미들웨어, STL 기반 자료구조와 알고리즘
  • 최적화 알고리즘, 대규모 디자인패턴(설계-디자인)
  • 멀티쓰레드 프로그래밍, 기초 물리(Physics), 캐릭터, 지형 엔진 & Tools,
  • 그래픽스 : shader(HLSL), Unity URP, ShaderLab, 3D 애니메이션
  • 다이렉트X : DirectX9/ DirectX12, Direct 를 통한 실전 수학 활용
  • 게임 엔진 : Unreal 4, Unity3D
  • C언어/C++,C++ 11~,C# 현업 디버깅 스킬 , 시스템 베이스
  • 포트폴리오 과정

 

 

[ 과외 기본 방향성 : 개인 맞춤형 최적화 설정]

 

수업은 수강자분의 현재 상태에 맞는 최적화된 맞춤 방식으로 진행되는 것을 원칙으로 하며 모든 과목은

최종적으로는 혼자만의 생각으로 수학/프로그램 을 만들어 나가는 것을 진행시 가장 우선적인 목표 진행합니다

현업/면접 또는 프로젝트 에서 중요한 부분 또한 조언 및 컨설팅이 같이 병행 됩니다

 

  • 진도만을 위한 도서 위주의 수업 또는 깊은 이해가 동반되지 않는 방식 즉 겉핥기로는
    게임/엔진 프로그래머의 문턱에 조차 도달 할 수 없습니다는 점을 기억해주시면 될 것 같습니다
  •  과목은 위에 나열한 과목들이 있지만 원하는 과목 또는 수준에 따라 먼저 시작하게 되는 과목이
    변경되니 과목 란에서 이런 과목이 있다는 것만 참고하시면 됩니다
  • 철저히 수강생 맞춤형으로 진행됩니다(수강자 분의 수준이 고려된 최적의 선에서 시작, 중복X)
  • 진도는 기본적으로 수강자 분들의 현재 수준을 고려하여 사전 상담을 통하여 
    수강자의 현재 실력기준으로 부터 부족한 부분은 채워 나가고 수강생의 목표에 따라 추가해야할 부분은
    함께 논의 후 진행하는 방향으로 진행됩니다(과목 추가는 별도비용 X)
  • 수업은 수강자가 직접 코드를 작성 하면서 진행됩니다
  • 과외 기본 목표 : 혼자만의 힘으로 희망하는 수준까지의 프로그램실력을 쌓는 것을 기본 목표로 합니다

 

 [ 과외 안내 ]

   진행 방식 :  개인 과외 1:1 진행 방식 / 1:N 방식

  • 1 Section 기준 = 4주/4회, 추가 섹션은 및 진행은 별도 문의주시면 답변해드립니다
  • 요일 : 협의, 신청자가 많은 경우 대기 기간이 발생 할 수 있습니다
  • 1 회당(1 섹션) 시간 : 1:00, 섹션당 추가에 따른 누적 할인 적용
  • 수강 기간 : 한 달 단위로 수강, 수강생이 희망하는 달까지 진행되며 한달 단위 결제방식입니다
  • 과외 장소/방식 : 원격으로만 진행 (카메라 Off ) 
    원격과 오프라인과의 차이는 다년간 노하우로 거의 없다고 보시면 됩니다
  • 납부 방식 : 한 달 단위, 한달 단위로 연장 하는 방식
  • 그룹 과외  : 그룹(2인 이상은) 별도 문의 , 추가 할인 적용

 

[수강 특전]  : 3DMP 네트워크

  • 현업 게임 프로그래머들과 함께 지속적인 '3DMP 네트워크 데이' 에서 정보 교류활동의 기회

[문의]

상담시 최대한 오픈마인으로 임해주시면 좋은 시너지 효과가 나올 수 있습니다!

상담전용 카톡 플러스 친구 (별도의 친구 추가 없이 상담 가능)

https://pf.kakao.com/_uxabzK

 

상담 전용 카톡 아이디 : 게임프로그래밍 과외

 

 

반응형
반응형

 

3DMP

 

 

광고 문의는 아래 연락처로 문의주시면 되겠습니다

 

광고는 광고카테고리에 게시글 하나를 자유형식으로 올려드립니다

 

https://pf.kakao.com/_uxabzK

3dmpengines@gmail.com 

 

반응형

'광고' 카테고리의 다른 글

IT 회사, 대학 초청 강연  (0) 2023.04.23
반응형

ET Core 간단하게 알아보기

데이터 모델링은 3가지 방법으로 가능하다.

-Convention(관례)

-Data Annotation(데이터 주석)

-Fluent API(직접 정의)

1. 관례의 경우에는 Id나 [클래스]Id가 붙을 경우 자동적으로 priamry key로 인식이 된다.

2. Attribute 즉, 속성을 활용하여 가능하다.

ex)

public int Id{ {get; set;}

[MaxLength(32)]

public string UserName {get; set;}

3. API를 사용하여 직접정의한다.

protected overrid void OnModelCreationg(ModelBuilder builder)

{

builder.Entity<GameResult>().Properity(x => x.UserName).ISUnicode(false);

}

지금까지는 1번 즉 관례의 방식으로 데이터 모델링을 사용했으나

2번과 3번도 유용하게 사용되는 경우가 있기에 모든 경우에 대하여 간단하게 알아보고자 한다.

[1]Entity Class에 대한 관례

정의) EF가 참조하는 클래스를 Entity Class라고 한다.

관례)

-public

-static이면 안됨

-생성자가 없거나, 인자 없는 생성자가 있어야 함

-id혹은 [클래스]id 형태의 property를 테이블의 Primary Key로 간주한다.

Column에 대한 관례

관례)

-property의 이름이 테이블 컴럼 이릅으로 사용

-propery의 타입을 해당하는 SQL타입으로 변환

-C# 형식의 기본 nullable이 테이블 컬럼 nullable에 영향

[2]Data Annotation

Blazor의 Form 검증 때 사용한 모든 Annotation이 모두 해당한다.

ex) Required, MaxLength...

[3] FLUENT API

Convention이나 Data Annotation으로 할 수 없는 모든 경우

Fluent Api로 처리가 가능하다.

ex)

protected override void OnModelCreating(ModelBuilder builder)

{

builder.Entity<GameResult>().Property(x => x.UserName).IsUnicode(false);

//UserId를 Index로 추가하는 코드 Has라 헷갈릴수 있지만 추가하는 코드이다.

builder.Entity<GameResult>().HasIndex(x => x.UserId);

//Score가 1000을 넘는 경우에만 필터링을 처리해서 추출하는 코드

builder.Entity<GameResult>().HasQueryFilter(p => p.Score > 1000);

}

이번엔 DB에서 특정 부분만을 제거해도록 하겠다.

Data Annotation에서는 [NotMapped] 속성을 사용하면 된다.

이를 만약에 class에다가 사용할 경우 DB생성에서 제외가 된다.

그리고 Fluent Api에서 사용할 경우

builder.Entity<GameResult>().Ignore(g => g.Excluded);

를 사용하면 된다.

다음으로 DB의 칼럼에서 TYPE과 SIZE 그리고 NULL여부를 제어하고 싶은 경우를 알아보겠다.

[Data Annotation의 경우]

Not null => [Required]

Set String Size => [MaxLength(123)]

Set String VarChar => X

[Fluent Api의 경우]

Not null => .IsRequired();

Set String Size => .HasMaxLength(123)

Set String Varchar => .IsUnicode(false)

테이블의 Primary Key를 설정하는 방식을 알아보겠다.

이 경우에는 위의 3가지 방식이 모두 가능하다.

[관례의 경우]

property의 이름에 Id가 들어가 있으면 기본 키로 인식해준다.

[속성의 경우]

[Key] 속성 값을 프로퍼티위에 붙여준다.

만약 [Key]의 값이 2개 이상의 프로퍼티에 지정되어 있는 복합 키라면

복합 키로 사용되므로 [Column(Order =0)]의 속성을 붙여서

순서를 지정해준다.

[Api의 경우]

.HasKey()로 지정해준다.

다음으로 테이블의 Index를 설정하는 경우는

Fluent Api만 가능하다.

builder.Entity<GameResult>().HasIndex(x => x.UserId);

그리고 테이블의 이름을 설정하는 것은 관례,속성,Api 모든 경우가 가능하다.

[관례]

public DbSet<GameResult> GameResults { get; set: }

[속성]

[Table("GameResultTable")] 을 class위에 붙여준다.

[Api]

builder.Entity<GameResult>().ToTable("GameResultTable");

마지막으로 결론을 정리해보자면

1)기본 상태로 충분하다면 관례방식으로

2)속성으로 가능하다면, 속성방식을 무조건 사용하자!

속성은 낮우에 Blazor Form Validation등 다른 곳에서도 재사용이 가능하기 때문이다.

3)위의 2가지 경우가 안되는 정말 절망적인 상황이라면 Fluent Api를 사용하자!

ex) Index관련...

 

반응형
반응형

REST ( Representational State Trasnfer) 공식표준 스펙은 아닌데

원래 있던 HTTP 통신에서  재사용 하여 데이터 송수신 규칙을 만든것 => CRUD

 

 

CRUD 를 Blazor API 서버를 활용하여 작업 한것 

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SharedData.Models;
using System.Collections.Generic;
using System.Linq;
using WebAPI.Data;

namespace WebAPI.Controllers
{
    //www.google.com 이건 전체 주소
    //www.google.com/api/ranking   이건 세부 라우팅 주소로 전체 주소에서 구분지어 들어갈 때 사용됨
    //그 다음 어떤걸 하는지 지정 할 수 있다 GET, POST, PUT...)
    //Create : POST 방식 api/ranking => body 에 실제 정보를 담는다 , 아이템 생성 요청

    //Read 시에는 보통 Get 을 사용한다
    //GET : api/ranking => 전체 아이템을 가져온다 , api/ranking/1 id가 1인 아이템을 가져온다
    //get /api/ranking  모든 id를 주세요
    //get /api/ranking/1   id 중에서 1전을 주세요 라는 요청 
    //get 은 body 에 정보를 넣지 않고 이 요청 하나로 처리 된다

    //update : put 으로 사용됨
    //PUT : api/ranking  (put은 보안 무제로 일반적인 웹에서 사용되지 않는다)
    //put 은 body 에 정보를 넣어서 요청을 보낸다

    //delete /api/ranking/1   이렇게 삭제 할 수 있는데 보안 문제로 웹에서 쓰지 않는다
    //id=1 번인 아이템 삭제 요청

    //api controller 는 c# 객체를 반환하는 것이 가능하다
    //null 반환하면 클라에서 204 Response (No Content) 를 받는다
    //String을 반호나하면 => text/plain 타입으로 반환한다
    //나머지는 json 형태로 반환한다

    //localhost:portnumber/api/ranking 이 요청이라는 얘기인데 RankingController 이것이 [controller] 부분에서 ranking 으로 변환됨
    [Route("api/[controller]")]
    [ApiController]
    public class RankingController : ControllerBase
    {
        ApplicationDbContext _context;
        public RankingController(ApplicationDbContext context)
        {
                _context = context;

        }

        //create, body 에 보낼때는 [frombody 를 넣어주면 된다]
        [HttpPost]
        public GameResult AddGameResult([FromBody] GameResult gameResult)
        {
            _context.GameResultList.Add(gameResult);
            _context.SaveChanges();
            return gameResult;
        }


        //update
        [HttpPut]
        public bool UpdateGameResult([FromBody] GameResult gameResult)
        {
            var findResult = _context.GameResultList.Where(item => item.Id == gameResult.Id).FirstOrDefault();
            if(findResult == null)
            {
                return false;
            }

            findResult.UserName = gameResult.UserName;
            findResult.Score = gameResult.Score;
            _context.SaveChanges();

            return true;
        }

        //read
        //ranking
        [HttpGet]
        public List<GameResult> GetGameResults()
        {
            List<GameResult> results = _context.GameResultList.OrderByDescending(item=> item.Score).ToList();
            return results;
        }

        //ranking/1
        [HttpGet("{id}")]
        public GameResult GetGameResult(int id)
        {
            GameResult result = _context.GameResultList.Where(item => item.Id == id).FirstOrDefault();
            return result;
        }


        //delete
        [HttpDelete("{id}")]
        public bool DeleteGameResult(int id)
        {
            var findResult = _context.GameResultList.Where(item => item.Id == id).FirstOrDefault();
            if (findResult == null)
            {
                return false;
            }
            _context.GameResultList.Remove(findResult);
            _context.SaveChanges();

            return true;
        }
    }
}

 

아래 예시는 모든 데이터를 json 으로 갖고 오는 예시이다

반응형
반응형
Console.WriteLine(default(int));  // output: 0
Console.WriteLine(default(object) is null);  // output: True

void DisplayDefaultOf<T>()
{
    var val = default(T);
    Console.WriteLine($"Default value of {typeof(T)} is {(val == null ? "null" : val.ToString())}.");
}

DisplayDefaultOf<int?>();
DisplayDefaultOf<System.Numerics.Complex>();
DisplayDefaultOf<System.Collections.Generic.List<int>>();
// Output:
// Default value of System.Nullable`1[System.Int32] is null.
// Default value of System.Numerics.Complex is (0, 0).
// Default value of System.Collections.Generic.List`1[System.Int32] is null.

 

 

ref : https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/operators/default

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

linq (3)  (0) 2023.04.10
linq (2)  (0) 2023.04.09
Linq (1)  (0) 2023.04.08
async/await & 커피와 베이컨  (0) 2023.04.07
C# - ArraySegment  (0) 2023.01.04
반응형

우선 테이블 명을 GameResultList 로 변경하였다

 

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
using RankingApp.Data.Models;
using System;
using System.Collections.Generic;
using System.Text;

namespace RankingApp.Data
{
    public class ApplicationDbContext : IdentityDbContext
    {
        //DbSet 으로 추가해줘야지 GameReesult.cs 에서 GameResultList 내용을 DB에 저장할 수 있다. ORM
        //즉 서버를띄울때 자동으로 DB를 갱신하여 띄워주게 된다
        //PM> add-migration RankingService  명령어르 NeGet 콘솔에서 실행하여 DB를 갱신해준다
        //PM> update-database 명령어르 NeGet 콘솔에서 실행하여 DB를 갱신해준다
        //주의!! GameResultList 이 테이블 명과 동일해야 한다 dbo.GameResultList 에서 dbo 빼고   GameResultList 만
        public DbSet<GameResult> GameResultList { get; set; }

        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
}

 

그런다음 패키지 관리자 콘솔에서 다음 처럼 입력한다음 다음 처럼 적용한다

PM> Add-Migration GameResultList

PM> Update-Database

이 처럼 마이그레이션이 변경된다

테이블명도 자동으로 바뀌게 된다

 

서비스 쪽에 갱신과 삭제에 대한 로직을 추가 하고 테이블에도 값이 삭제/갱신이 적용 될수 있게 처리 한다

       public Task<bool> UpdateGameResult(GameResult gameResult)
        {
            var result = _context.GameResultList.Where(x => x.Id == gameResult.Id).FirstOrDefault();
            if(result==null)
            {
                return Task.FromResult(false);
            }
            result.UserName = gameResult.UserName;
            result.Score = gameResult.Score;
            _context.SaveChanges();

            return  Task.FromResult(true);
        }

        public Task<bool> DeleteGameResult(GameResult gameResult)
        {
            var result = _context.GameResultList.Where(x => x.Id == gameResult.Id).FirstOrDefault();
            if (result == null)
            {
                return Task.FromResult(false);
            }
            _context.GameResultList.Remove(gameResult);
            _context.SaveChanges();
            return Task.FromResult(true);
        }

 

 

Ranking.razor 

갱신과 삭제 버튼이 보여 내용을 수정 할수 있도록 하고

추가팝업과 갱신 팝업은 동일한 형태로 사용한다

그리고 로그인이 되어 있지 않다면 유저 정보는 보이지 않도록 Autorized, NotAutorized 처리를 해 놓았다

@page "/ranking"
@using RankingApp.Data.Models;
@using RankingApp.Data.Services;

@inject RankingService RankingService

<h3>Ranking</h3>

<AuthorizeView>
    <Authorized>
        
        @if (_gameResultList == null)
        {
            <p>Loading...</p>
        }else{
            <table class="table">
                <thead>
                    <tr>
                        <th>UserName</th>
                        <th>Score</th>
                        <th>Date</th>
                        <th></th>
                        <th></th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (var gameResult in _gameResultList)
                    {
                        <tr>
                            <td>@gameResult.UserName</td>
                            <td>@gameResult.Score</td>
                            <td>@gameResult.Date</td>
                            <td>
                                <button class="btn btn-primary" @onclick="() => EditGameResult(gameResult)">
                                    Edit
                                </button>
                            </td>
                            <td>
                                <button class="btn btn-primary" @onclick="() => DeleteGameResult(gameResult)">
                                    Delete
                                </button>
                            </td>
                        </tr>
                    }
                </tbody>
            </table>
            <p>
                <!-- make button to add gameresult -->
                <button class="btn btn-primary" @onclick="AddGameResult">
                    Add
                    </button>
            </p>


            @if(_showPopup)
            {
                //dialog to show gameresult
                <div class="modal" style="display:block" role="dialog">
                    <div class="modal-dialog">
                        <div class="modal-content">
                            <div class="modal-header"></div>
                            <h3 class="modal-title">Add/Update Game Result</h3>

                            <button type="button" class="close" @onclick="ClosePopup">
                                <span aria-hidden="true">X</span>
                            </button>
                            <div class="modal-body">
                                <label for="UserName"> UserName</label>
                                <input class="form-control" type="text" placeholder="UserName" @bind-value="_gameResult.UserName" />
                                <label for="Score"> Score</label>
                                <input class="form-control" type="text" placeholder="Score" @bind-value="_gameResult.Score" />

                                <button class="btn-primary" @onclick="SaveGameResult">Save</button>
                            </div>
                        </div>
                    </div>
                </div>
            
            }
        }

    </Authorized>
    <NotAuthorized>
        인증 안됨(로그인 안됨)
    </NotAuthorized>

</AuthorizeView>

@code {
    List<GameResult> _gameResultList;

    GameResult _gameResult;

    bool _showPopup = false;

    protected override async Task OnInitializedAsync()
    {
        //db 에서 데이터 긁어와서 _gameResults 에 넣어줌
        await readGameResultList();
    }

    public async Task<List<GameResult>> readGameResultList()
    {
        //db 에서 데이터 긁어와서 _gameResultList 에 넣어줌
        _gameResultList = await RankingService.GetGameResultAsync();
        return _gameResultList;
    }


    public void AddGameResult()
    {
        _showPopup = true;
        //Add new gameresult to
        _gameResult = new GameResult()
        {
            Id = 0
        };
    }

    //ClosePopup
    void ClosePopup()
    {
        _showPopup = false;
    }


    void EditGameResult(GameResult gameResult)
    {
        _showPopup = true;
        _gameResult = gameResult;
    }

    async Task DeleteGameResult(GameResult gameResult)
    {
        var result = RankingService.DeleteGameResult(gameResult);
        await readGameResultList();
    }


    async Task SaveGameResult()
    {
        //새로 만드는 상태
        if(_gameResult.Id==0)
        {
            //save to db
            _gameResult.Date = DateTime.Now;
            var result = RankingService.AddGameResult(_gameResult);
            //close popup
            ClosePopup();
        }
        else
        {
            //수정하고 있는 상태
            var result = RankingService.UpdateGameResult(_gameResult);
        }

        await readGameResultList();
        _showPopup = false;
    }


}

 

결과는 다음과 같고

 

Edit 버튼을 눌러 값 또한 수정 반영 할 수 있다

 

 

로그인 안했을대 로그인 안된다는 문자표시

반응형
반응형

3DMP

 

 

 [ 강연 분야 ]  

  • IT 개발 분야 전망, 투자,  컨설팅, 대학, 회사 강연 특강
  •  프로그램, 코딩, 취직, 동기부여, IT 산업, 실무 이야기, 면접, 면접과 면접자의 관점
  • 과외문의는 받지 않습니다

 

이력 -1 ]

  • 크래프톤, 넥슨, 넷마블, NHN Games  및 다수 동종/타종 근무경험 및 다년간 현업 개발자
  • 3D게임 엔진/클라이언트 프로그래머/언리얼, 유니티 엔진/게임 메인 게임 개발 /모바일,PC/네트워크 프로그램 개발
  • 고차원 수학/ 물리 들의 알고리즘과 관련된 특수 프로그램 제작, 미들웨어(엔진)/ 일반 프로그램 프로젝트 제작
  • 게임업계 대기업 넥슨, NC, 펄어비스, 중견 B사, 등등의 기업에 합격시킨 노하우
  • 다수, 다양한 분들에 대한 다수의 과외 경험으로 축적된 노하우 보유
  • 기타 일반 프로젝트 경험
  • 컨설팅


[ 이력-2 : 이직/취직/ 과외를 수강하신 분들의 회사
]

  • 과외 분야 : https://3dmpengines.tistory.com/2331
  • 넥슨 코리아, NC, 넷마블, 넷게임즈, EA 코리아의, 마이크로소프트 - 프로그래머 다수/TA/기획자
  • 크래프톤, 펍지, 펄어비스, 블루홀의 - 프로그래머/TA/기획자
  • 스마일게이트, NHN, 기타 등등 의 - 프로그래머/TA/기획자/UI
  • 게임/비게임 임직원, CEO포함 수강
  • 대학생(취준생), SKY, 포함 상위 10% 이상 대학생, 그 외 글로벌 학교 및 유학생
  • 전문 프로그래머로 진로를 희망하시는 분들들을 위한 강의 및 컨설팅
  • 전공자/컴퓨터 관련 전공자가 아니지만 게임이나 프로그램쪽으로 진로를 희망하시는 분들
    (비전공자 일지라도 많은 상당수의 분들이 원하는 성과를 이루고 가셨습니다)
  • 프로그램을 혼자 하기엔 막연하여 그룹(단체) 수강
  • 초심자로 프로그램 배워보려 했지만 진입 장벽때문에 포기하신 분들 또한 수강

 

[문의]

강연 문의 : 3dmpengines@gmail.com

https://pf.kakao.com/_uxabzK

 

상담 전용 카톡 아이디 : 게임프로그래밍 과외

 

 

반응형

'광고' 카테고리의 다른 글

광고문의  (0) 2023.05.07
반응형

 

랭킹 데이터를 추가 하고 다시 가져오는 코드이다

 

Ranking.razor

@page "/ranking"
@using RankingApp.Data.Models;
@using RankingApp.Data.Services;

@inject RankingService RankingService

<h3>Ranking</h3>

<AuthorizeView>
    <Authorized>
        
        @if (_gameResultLIst == null)
        {
            <p>Loading...</p>
        }else{
            <table class="table">
                <thead>
                    <tr>
                        <th>UserName</th>
                        <th>Score</th>
                        <th>Date</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (var gameResult in _gameResultLIst)
                    {
                        <tr>
                            <td>@gameResult.UserName</td>
                            <td>@gameResult.Score</td>
                            <td>@gameResult.Date</td>
                        </tr>
                    }
                </tbody>
            </table>
            <p>
                <!-- make button to add gameresult -->
                <button class="btn btn-primary" @onclick="AddGameResult">
                    Add
                    </button>
            </p>


            @if(_showPopup)
            {
                //dialog to show gameresult
                <div class="modal" style="display:block" role="dialog">
                    <div class="modal-dialog">
                        <div class="modal-content">
                            <div class="modal-header"></div>
                            <h3 class="modal-title">Add/Update Game Result</h3>

                            <button type="button" class="close" @onclick="ClosePopup">
                                <span aria-hidden="true">X</span>
                            </button>
                            <div class="modal-body">
                                <label for="UserName"> UserName</label>
                                <input class="form-control" type="text" placeholder="UserName" @bind-value="_gameResult.UserName" />
                                <label for="Score"> Score</label>
                                <input class="form-control" type="text" placeholder="Score" @bind-value="_gameResult.Score" />

                                <button class="btn-primary" @onclick="SaveGameResult">Save</button>
                            </div>
                        </div>
                    </div>
                </div>
            
            }
        }

    </Authorized>
    <NotAuthorized>
        인증 안됨(로그인 안됨)
    </NotAuthorized>

</AuthorizeView>

@code {
    List<GameResult> _gameResultLIst;

    GameResult _gameResult;

    bool _showPopup = false;

    protected override async Task OnInitializedAsync()
    {
        //db 에서 데이터 긁어와서 _gameResults 에 넣어줌
        await readGameResults();
    }

    public async Task<List<GameResult>> readGameResults()
    {
        //db 에서 데이터 긁어와서 _gameResults 에 넣어줌
        _gameResultLIst = await RankingService.GetGameResultAsync();
        return _gameResultLIst;
    }

    
    public void AddGameResult()
    {
        _showPopup = true;
        //Add new gameresult to
        _gameResult = new GameResult()
        {
            Id = 0
        };
    }

    //ClosePopup
    void ClosePopup()
    {
        _showPopup = false;
    }

    async Task SaveGameResult()
    {
        //새로 만드는 상태
        if(_gameResult.Id==0)
        {
            //save to db
            _gameResult.Date = DateTime.Now;
            var result = RankingService.AddGameResult(_gameResult);
            //close popup
            ClosePopup();
        }
        else
        {
            //수정하고 있는 상태
            
        }
        
        _gameResultLIst = await readGameResults();
    }
}

 

 

RankingService.cs

using RankingApp.Data.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace RankingApp.Data.Services
{
    public class RankingService
    {
        ApplicationDbContext _context;

        public RankingService(ApplicationDbContext context)
        {
            _context = context;
        }

        public Task<List<GameResult>> GetGameResultAsync()
        {
            //DB 에서 GameResult 테이블의 모든 데이터를 가져온다
            List<GameResult> results = _context.GameResult.ToList();
            return Task.FromResult(results);
        }

        public Task<GameResult> AddGameResult(GameResult gameResult)
        {
            _context.GameResult.Add(gameResult);
            _context.SaveChanges(); //db 에 실제 저장이 됨
            return Task.FromResult(gameResult);
        }

    }
}

AddGameResult() 함수가 추가 되었고  

_context.GameResult.Add(gameResult);
_context.SaveChanges(); //db 에 실제 저장이 됨

이 코드가 실제 DB 에 저장하는 부분이 된다

이후 결과는 Task 로 리턴해주고 결과를 담을때는 FromResult 로 결과를 담아줘서 리턴하면 await 으로 결과를 기다 릴 수 있다

    public async Task<List<GameResult>> readGameResults()
    {
        //db 에서 데이터 긁어와서 _gameResults 에 넣어줌
        _gameResultLIst = await RankingService.GetGameResultAsync();
        return _gameResultLIst;
    }

 

 

 

 

유저를 하나 추가하면 다음처럼 보이게 된다 (test1 유저 추가)

DB 데이터에 추가된 내용

반응형
반응형

Blazor Server 생성 단계에서 인증 유형을 개별 계정으로 선택한다

이러면 가입, 로그인 등을 할 수 있게 된다

 

RankingDB 이름으로 DB를 하나 추가 한다

그리고 appsettings.json 부분에서 다음 처럼 변경한다

{
  "ConnectionStrings": {
    //"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-RankingApp-b997cf13-bc70-49dd-9313-05e01b61b6ab;Trusted_Connection=True;MultipleActiveResultSets=true"
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=RankingDB;Trusted_Connection=True;MultipleActiveResultSets=true"

  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

DefaultConnectino 부분에서 Database=RankingDB 로 변경해준다

PM> add-migration anyName 으로 DB를 마이그레이션 할때 DefaultConnection 에 잇는 Database=RankingDB 

RankingDB 이 이름으로 DB 가 생성되게 된다

 

 

 

먼저 최종 작업한 화면은 다음과 같다

여기서 Entity = EntityFramework 는 ORM 같은 것이다

 

기본 Blazor Server 에서 Ranking 부분을 추가 한것이도 Fetch data 와는 다르게 데이터는 db 에서 읽어온다 그래서 미리 DB 에 데이터를 넣어 놓어 놓는 것으로 시작한다

 

오른쪽 상단 Register 를 눌러 가입을 하나 하면 dbo.AspNetUsers 에 추저가 하나 추가 된다

그런 다음 EmailConfirmed 를 수동으로 true 로 변경한다 (이 부분은 이메일 확인 부분이다)

유저는 기본으로 제공된느 db 를 사용한다

 

 

GameResult.cs 를 Modes 폴더를 하나 만들어추가한다(Services 로 마찬가지로 추가한다)

using System;

namespace RankingApp.Data.Models
{
    public class GameResult
    {
        public int Id { get; set; }
        public int UserID { get; set; }
        public string UserName { get; set; }
        public int Score { get; set; }
        public DateTime Date { get; set; }
    }
}

 

 

원래는 GameResult 테이블을 하나 추가해 다음 처럼 데이터를 추가해야 하지만 

이렇게 하지 않고 Entity 를 활용해 자동으로 테이블을 등록하게한다 => db에 대한 버전 관리도 되고 .cs 와 코드적으로 자동 연동 됨으로 이것이 편하다

 

ApplicationDbContext.cs 가 db 와 .cs 같에 연결해주는 핵심적인 부분이다

 

먼저 IdentityDbContext 를 상속받는다 

DbSet<GameResult> 이 부분이 DB와 코드의 연결 부분이다

IdentityDbContext : Base class for the Entity Framework database context used for identity.

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
using RankingApp.Data.Models;
using System;
using System.Collections.Generic;
using System.Text;

namespace RankingApp.Data
{
    public class ApplicationDbContext : IdentityDbContext
    {
        //DbSet 으로 추가해줘야지 GameReesult.cs 에서 GameResult 내용을 DB에 저장할 수 있다. ORM
        //즉 서버를띄울때 자동으로 DB를 갱신하여 띄워주게 된다
        //PM> add-migration RankingService  명령어르 NeGet 콘솔에서 실행하여 DB를 갱신해준다
        //PM> update-database 명령어르 NeGet 콘솔에서 실행하여 DB를 갱신해준다
        public DbSet<GameResult> GameResult { get; set; }

        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
}

이렇게 추가해주고 

 

Startup.cs 에서 

public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));

이 부분을 통해 DB 로 연결하게 된다 (Identity 를 사용하기 때문에 이 부분이 자동으로 등록 되어 있긴 하지만 안되어 있으면 추가해준다)

 

 

 

Nuget 패키지 관리자 > 패키지 관리자 콘솔 에서 다음 명령어들을 실행한다

 

PM> add-migration RankingService  명령어로 NeGet 콘솔에서 실행하여 DB를 마이그레이션 해준다
PM> update-database 명령어로 NeGet 콘솔에서 실행하여 DB를 업데이트해준다

 

이렇게 까지 하면 GameResult DB 가 자동으로 만들어진다

그럼 다음과 같이 Migratoinos 폴더에 코드로 옮겨진 것을 볼 수 있다

20230408233459_RankingService.cs (자동 생성된 파일)

using System;
using Microsoft.EntityFrameworkCore.Migrations;

namespace RankingApp.Data.Migrations
{
    public partial class RankingService : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "GameResult",
                columns: table => new
                {
                    Id = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    UserID = table.Column<int>(nullable: false),
                    UserName = table.Column<string>(nullable: true),
                    Score = table.Column<int>(nullable: false),
                    Date = table.Column<DateTime>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_GameResult", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "GameResult");
        }
    }
}

즉 GameResult 를 알아서 해석하여 테이블을 만드는 코드가 된다

Up 일때는 들어가고 Down 일때는 Drop 하게된다 (버전 관리 개념)

 

이렇게 하지 않으면 DB 에서 Field 를 변경하거나 코드에서 Field 명등 수정 사항이 안맞는 상황들이 발생 할수 있기 때문에 번거로운 작업이 계속 될 수 있는데 이것을 코드수준에서 DB로 맞춰만들어주기 때문에 작업의 효율을 높여준다

이에 더해 버전 관리 또한 가능해지게 된다

 

 

 

데이터는 우선 수동으로 다음처럼 추가한다

 

데이터를 읽어오는 코드를 먼저 작업하자

 

그러기 위해서  ApplicationDbContext.cs 에서 다음 처럼 

DbSet 을 통해서 DB 와 연결된 부분을 읽어와야 하는데 이미 위에서 다음과 같은 코드를 통해 연결을 해놨으니

public DbSet<GameResult> GameResult { get; set; }

 

RankingService.cs 서비스를 만들어 데이터를 가져올 수 있는 코드를 만들어 준면 된다

using RankingApp.Data.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace RankingApp.Data.Services
{
    public class RankingService
    {
        ApplicationDbContext _context;

        public RankingService(ApplicationDbContext context)
        {
            _context = context;
        }

        public Task<List<GameResult>> GetGameResultAsync()
        {
            //DB 에서 GameResult 테이블의 모든 데이터를 가져온다
            List<GameResult> results = _context.GameResult.ToList();
            return Task.FromResult(results);
        }

    }
}

이 코드를 통해 db 에 있는 GameResult 테이블 내용을 읽어올 수 있게 된다

 

그리고 RankingService 를 사용하기 위해서 Startup.cs 에 AddScoped 로 RankigService 를 추가해준다

  public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));
            services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<ApplicationDbContext>();
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
            services.AddSingleton<WeatherForecastService>();
            services.AddScoped<RankingService>();
        }

 

AddScoped() 이 내용은 (https://3dmpengines.tistory.com/search/AddScoped) 이 글을 참고

 

 

 

이제 이 내용을 Blazor 를 통해서 보여주면 된다

 

Ranking.razor

@page "/ranking"
@using RankingApp.Data.Models;
@using RankingApp.Data.Services;

@inject RankingService RankingService

<h3>Ranking</h3>

@if (_gameResults == null)
{
    <p>Loading...</p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>UserName</th>
                <th>Score</th>
                <th>Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var gameResult in _gameResults)
            {
                <tr>
                    <td>@gameResult.UserName</td>
                    <td>@gameResult.Score</td>
                    <td>@gameResult.Date</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    List<GameResult> _gameResults;

    protected override async Task OnInitializedAsync()
    {
        //db 에서 데이터 긁어와서 _gameResults 에 넣어줌
        _gameResults = await RankingService.GetGameResultAsync();
    }

}

이 페이지를 보여주기 위해 탭을 하나 추가한다

 

이때 상단에

@inject RankingService RankingService

이 코드를 통해 RankingService 를 사용 할수 있도록 Dependency injection 해준다

 

 

NavMenu.razor

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">RankingApp</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="ranking">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Ranking data
            </NavLink>
        </li>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

 

그럼 다음과 같은 결과를 볼 수 있다

 

반응형
반응형

Blazor 앱은 .NET 메서드에서 JavaScript함수를 호출하고, JavaScript함수에서 .NET메서드를 호출할 수 있다.

이것을 JavaScript interop이라고 한다. 

 

Blazor의 .NET 메서드에서 JavaScript 함수 호출

1. index.html body 태그안에 script를 추가한다. 

    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
    <script>
        window.showAlert = (comment) => {
            alert(comment);
        };
    </script>

2. index.razor에서 버튼을 클릭하면 showAlert 함수를 호출하도록 한다.

  • .NET에서 JS를 호출하려면 IJSRuntime 추상화를 삽입하고 InvokeVoidAsync 함수를 호출한다.
@page "/"
@inject IJSRuntime JS

<h1>Hello, world!</h1>

Welcome to your new app.


<p>
		<button @onclick="ShowAlert">알림창 띄우기</button>
</p>

<SurveyPrompt Title="How is Blazor working for you?" />

@code {
		private async Task ShowAlert()
		{
				await JS.InvokeVoidAsync("showAlert", "Hello, world!");
		}
}

3. 알림창 띄우기 버튼을 클릭하면 alert 을 확인할 수 있다. 

showAlert 자바스크립트 함수를 index.html에서 별도의 파일로 옮겨보자. => JS isolation

1. wwwroot/js/script.js 에 아래 함수 저장

export function showAlert(comment) {
    alert(comment);
}

2. index.razor 수정

IJSInProcessObjectReference는 함수를 동기적으로 호출할 수 있는 JS 개체에 대한 참조를 나타냅니다.

@page "/"
@inject IJSRuntime JS

<h1>Hello, world!</h1>

Welcome to your new app.


<p>
		<button @onclick="ShowAlert">알림창 띄우기</button>
</p>

<SurveyPrompt Title="How is Blazor working for you?" />

@code {

		private IJSObjectReference module;

		protected override async Task OnAfterRenderAsync(bool firstRender)
		{
				if (firstRender)
				{
						module = await JS.InvokeAsync<IJSObjectReference>("import",
								"./js/scripts.js");
				}
		}

		private async Task ShowAlert()
		{
				await module.InvokeVoidAsync("showAlert", "Hello, world!");
		}
}

3. 알림창 띄우기 버튼을 클릭하면 alert 을 확인할 수 있다. 

 

 

ref : https://bigexecution.tistory.com/49

반응형
반응형

Blazor 서버 기본 구조

Dependency Injection, Router, _imports, Layout 등을 알아본다

 

 

 

Dependency Injection

이를 간단하게 알아보기 위해서 이미 존재하고있던 Data폴더에 FoodService.cs를 생성한다.

이후 이 안에 Food class를 생성해주고 프로퍼티값으로 Name, Price를 선언해준다.

그리고 cs파일을 생성하면서 생성된 FoodService class안에

public List<Food> GetFoods()
{
List<Food> foods = new List<Food>()
{
new Food() {Name = "bap", Price = 7000},
new Food() {Name = "KimBap", Price = 3000},
new Food() {Name = "ChoBap", Price = 9000}
};
return foods;
}

위와 같이 List<Food>를 반환해주는 함수를 생성해준다.

이후에 Blazor App을 생성하면서 존재하는 Index.razor파일안에 아래와 같은 코드를 추가해주면

웹 페이지에 하드코딩해서 넣어준 서비스의 목록들이 나오긴 한다.

 

@using BlazorApp.Data;

<div>
@foreach(var food in _foodService.GetFood())
{
<div>@food.Name</div>
<div>@food.Price</div>
}

</div>
@code{
FoodSerive _foodService= new FoodService();
}

다만, 위와 같이 코드를 작성하게 된다면 코드간의 의존성이 너무 심해진다는 문제가 발생한다.

따라서 Interface을 활용하여 이를 해결해주자.

다시 FoodService.cs로 돌아와서 Interface를 선언해주자 이렇게 해주면 이 FoodService를 사용하고싶은

사람들은 이 interface를 구현해야한다는 규칙이 생기게 된다.

public interface IFoodService
{
IEnumeralbe<Food> GetFoods();
}

public class FoodService : IFoodService
{
	public IEnumeralbe<Food> GetFoods()
	{
		List<Food> foods = new List<Food>()
		{
		new Food() {Name = "bap", Price = 7000},
		new Food() {Name = "KimBap", Price = 3000},
		new Food() {Name = "ChoBap", Price = 9000}
		};
		return foods;
	}
}

하지만 아직까지 razor의 컴포넌트들마다 일일이 new FoodService()를 호출해야하는 문제가 존재한다.

이를 해결하기 위해서 Dependency Injection이다.

이는 Startup.cs안에 ConfigureServices 함수안에 service.AddSingleton<IFoodService, FoodService>();로 선언하게 되면

이제 new를 통해서 선언하는 것이아닌

@inject IFoodService foodSerivce를 통해서 FoodService를 new를 통해서 생성하지 않고 사용이 가능하다.

[예시]

@using BlazorApp.Data;

@inject IFoodService foodSerivce

<div>
@foreach(var food in foodSerivce.GetFood())
{
<div>@food.Name</div>
<div>@food.Price</div>
}

</div>

하지만 이러한 방식이 전역적으로 사용되는것은 아니다.

이를 살펴보자.

public class SingletonService : IDisposable
{
public Guid ID {get; set;} //간단하게 ID를 생성하는 인터페이스이다.

public SingletonService() //cotr + tab,tab으로 해당 class의 생성자를 간단하게 생성할 수 있다.
{
ID = Guid.NewGuid();
}

public Dispose()
{
Console.WriteLine("SingletonService Disposed!");
}
}

public class TransientService : IDisposable
{

public Guid ID {get; set;} //간단하게 ID를 생성하는 인터페이스이다.

public TransientService() //cotr + tab,tab으로 해당 class의 생성자를 간단하게 생성할 수 있다.
{
ID = Guid.NewGuid();
}

public Dispose()
{
Console.WriteLine("TransientService Disposed!");
}
}

public class ScopeService : IDisposable
{
public Guid ID {get; set;} //간단하게 ID를 생성하는 인터페이스이다.

public ScopeService() //cotr + tab,tab으로 해당 class의 생성자를 간단하게 생성할 수 있다.
{
ID = Guid.NewGuid();
}

public Dispose()
{
Console.WriteLine("ScopeService Disposed!");
}

}

[생명주기]

3가지 모드를 파악해보자.

 

다음처럼 생성

service.AddSingleton<SingletonService>();

service.AddTransient<TransientService>();

service.AddScoped<ScopedService>();

 

​다음 처럼 사용 (주로 상단에 선언)

@inject SingletonService singleton;

@inject TransientService transient;

@inject ScopedService scoped;

@inject 가 있는 동일페이지에서 다음 코드가 있을때 guid를 통해  id 가 페이지개 갱신 될때마다 재생성 되는 것을 보면 생명 주기를 파악 할 수 있다

<div>

<h1>Singleton</h1>

Guid: @singleton.ID

<h1>Singleton</h1>

Guid: @transient.ID

<h1>Singleton</h1>

Guid: @scoped.ID

<div>

이렇게 하고 생명주기를 살펴보면

singleton와scoped의 id는 변화하지 않고

transient의 id는 변화한다.

하지만 새로고침을 실행하면 singleton의 id는 바뀌지 않지만 나머지 두개는 변화한다.

(Balzor Server 프로젝트의 경우에는 변화가 없지만 클라이언트(Blazor WebAssembly) 사이드같은 경우에는 singleton 방식도 guid 도 변경 된다)

즉, 변동하지 않고 모두에게 똑같이 보여야 한다면 singleton을

유저마다 갱신되어 보여야 한다면 나머지 2개를 선택하여 사용해야 하는데

Transient는 말그래도 웹페이지의 요청이 일어날때마다 바뀌고

Scoped는 웹에 접속할때 마다 변경된다.

 

 

SPA(single Page Application)

SPA는 기존에 존재하던 정적 페이지가 아닌 동적 페이지라고 할 수 있다

기존에는 웹서버에서 전체를 실시간으로 만들어 보내주는 방식이였지만

일부 변경되는 데이터에 따라(Dom) 모습이 달라지는 것을 동적페이지 방식이라 한다=> SPA

 

 

레이아웃

다음 과 같은 레이웃의 범위를 알아보면 아래 코드로 글자가 어디에 들어 가있는지 보면 된다 a, d , c

 

MainLayout.razor

@inherits LayoutComponentBase

<div class="sidebar">
    <p style="color : #be0000;">
        a
    </p>
    
    <NavMenu />
</div>

<div class="main">
    <div class="top-row px-4">
        c
        <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
    </div>

    <div class="content px-4">
        d
        @Body
    </div>
</div>

_imports.razor 알아보기

이 컴포넌트 안에 사용되는 @키워드들은 같은 폴더안에 있는 모든 컴포넌트들에게 적용되게 된다.

imports.razor 의 내용을 ABC 라는 폴더에 넣어놓고
내용을 새로만든 레이아웃 @layout MainLayout2.razor

해놓으면 해당 폴더에 있는 razor 페이지들의 기본 레이아웃은 MainLayout 이 아닌  MainLayout2 으로 변경된다

 

 

기본 레이아웃은 자체는 다음 코드를 상속받아 레이아웃 자체를 구성한다

@inherits LayoutComponentBase

 반드시 하나는 이 것을 상속받는 레잉아웃이 있어야 한다 이것이 적용된 페이지가

MainLayout.razor 이다

 

@inherits를 사용하게되면 class를 상속해서 사용할 수 있게 된다.

@layout키워드를 사용하면 레이아웃을 바꿔서 적용할 수 있다.

키워드를 사용하지 않고 모든 레이아웃을 변경하기 위해서는 App.razor에 DefaultLayout을 변경해주면 된다.

 

 

 

Router : 주소/sports/55  과 같이 주소 뒤에 어떤 페이지를 보여줄지에 대한 것

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

 

<Router 는 다음 처럼 되어 있다 Found 가 RenderFragment 로 내용을 보여주기 위한 형태인것을 알 수있다(템플릿)

그리고 Router는 상대적 기준으로 생각하고 작성해야 한다 즉 현재 경로 위쪽이 무엇인지 기본적으로 절대경로로 생각하면 안된다는것

 

 

 

 

 아래코드에서

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorApp</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="user">
                <span class="oi oi-list-rich" aria-hidden="true"></span> User
            </NavLink>
        </li>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

 

 

 이 부분은 

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorApp</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

이 처럼 화면이 작아지는 모바일 환경에서 메뉴표현등으로 나타내기 위한 버튼으로 화면이 큰 윈도우 환경에선 기본적으로는 화면을 줄이지 않는 이상 안보일 수 있다

 

 

NavLink : 이동 시키는 <a href > 와 유사한데 비주얼 적으로 차이가 있는데 버튼을 누를때 스타일 적인 것만 다르다

상단에 Home, FetchData 등의 버튼을 참고하면 된다

 NavLink 는

<a href  를 써서 동일하게 구현할 수도 있고

NavigationManager 를 인젝션 하여

 이렇게 구현하여 구현 할 수도 있다

 

counter 페이지가 뜰때 다음처럼 값을 받을 수도 있다 값은 CurrentCount 로 들어가게 된다

 

 

https://localhost:44337/counter/30  30을 입력하면 CurrentCount 값이 30 이 된다

 

 

 

 

ref : https://blog.naver.com/whro152/223041082396

반응형
반응형

[색상 변경하기 예제]

 

 

Cascading Parameter 는 변수를 전달하는데 다른 페이지에

<Cascading Parameter 로 동일하게 선언되어 있는 변수가 있다면 그곳 까지 값이 전달된다

 

TableTemplate 은 C# 의 tempalte 같은 것인데 당연히 동일하지 않고 엇비슷하다


Cascading Parameter알아보기

만약 우리가 동일한 Parameter변수들을 넘겨주는 경우가 발생한다면 한번에 흘러내리는 것처럼

변수를 넘겨주게 하는 문법이다.

사용 방법은 매우 간단한데

<CascadingValue Name ="ThemeColor" Value="_selectedColor">

<ShowUser ...>

<ShowUser2 ...>

</CascadingValue>

태그를 사용하여 사용할 범위를 지정해 준다.

Name은 구분하기 위한 Key이며 Value는 범위내에 뿌릴 값이다.

이렇게 사용하게 되면 범위내에 있는 ShowUser, ShowUser2의 컴포넌트에 자동적으로 Value = "_selectedColor"

Parameter가 흘러내리듯이 정해지게 된다.

이를 ShowUser,Showuser2의 컴포넌트에서 사용하기 위해서는

[CascadingParameter(Name = "ThemeColor")]

string _color {get; set;}

위와 같이 사용하여 _color의 값에 Value="_selectedColor"가 Binding된것처럼 사용할 수 있게 된다.

[추가]

컴포넌트의 깊이에 상관없이 Parameter가 전달된다는 특징이 있다.

[Templated Component]알아보기

C#의 제너릭형식과 유사한 문법이다.

RenderFragment로 이루어진 Razor Component이다.

[넘어가는 이야기]

[c#에는 c++에 없는 partial class가 존재한다.

이는 a라는 파일에서 partial class a를 작성하고

추후에 b라는 파일에서 aprtial class a를 이어서 작성하게 되면 두 class를 하나로 합친 것처럼

작성하게 해주는 키워드이다. 현재 Blazor와는 상관없는 c#문법이므로 넘어가도 상관없다.] 딱히 이 부분은 크게 상관없는 부분이다.

 

정리하자면 TableTemplate.razor를 생성한다.

1) 코드 부분에 [Parameter] public RenderFragment Header {get; set;}를 작성한다.

2) HTML 부분에 사용하기 위한 장소에 @Header를 선언해준다.

3) 다른 razor 컴포넌트에서 <TableTemplate></TableTemplate>를 선언해준다.

4) <TableTemplate></TableTemplate>의 범위안에 <Header></Header>를 서언하여

RenderFragment Header를 사용한다는 것을 알려준다.

5) <Hedaer></Header>의 범위안에 사용하고 싶은 HTML의 태그들을 지정해준다.

6) 이렇게 사용하게 되면 TableTemplate.razor의 @Header부분에 지정한 태그들이 들어가게 된다.

 

 

TableTemplate 예시

노란색 화살표가 해당 부분이 .razor 페이지로 들어가는 부분이 된다

이때 해당 부분을 대체하는데 쓰이는 역할로 RenderFragment 가 사용된다 : 태그를 그리는 느낌

TableTemplate 을 통해 보여지는 부분의 개수를 변경하거나 스타일을 다르게 하는 등이 가능해질 수 있다

 

 

ref : https://blog.naver.com/whro152/223039845752

반응형
반응형

User.razor 전체 코드

 

컴포넌트 분리

저번 포스팅에 @code부분인 AddUser()와 KickUser()를 컴포넌트화 시키기 위해 새로운 Blazor파일을 생성해 분리하도록 해보자

ShowUser 블레이저 파일 생성

ShowUser.razor

ShowUser.razor파일을 생성해 User.razor에있던 리스트 생성과 유저 추가, 삭제하는 함수들을 옮겼다.

 

설명

User.razor

-<ShowUser></ShowUser> : 새로 생성한 ShowUser.razor를 사용하기 위한 처리

-<ShowUser User ="_users"> : ShowUser.razor의 User변수와 User.razor의 _user의 변수를 공유한다

-@ref ="_showUser" : 부모쪽에서 자식에 접근하는 방식(이 처리가 없으면 @code에 ShowUser는 null임)

CallbackTest="CallbackTestFunc" : 콜백을 사용할 때

 

ShowUser.razor

[Parameter] : 를 통해  다른 블레이저 파일에서 접근 가능하도록 명시한다.

(블레이저에서는 Action보다는 EventCallback을 추천한다. StateHasChanged()가 없어도 리프래시 처리 )

 

유저리스트에 요소 추가

유저리스트에 요소 삭제

"CallbackTest"문자가 보임 : 콜백 호출이 잘되고 있다

 

 

ref : https://funfunhanblog.tistory.com/419

반응형
반응형
@page "/bind"

<p>
    <input @bind="inputValue" />
</p>

<p>
    <input @bind="InputValue" />
</p>

<ul>
    <li><code>inputValue</code>: @inputValue</li>
    <li><code>InputValue</code>: @InputValue</li>
</ul>

@code {
    private string? inputValue;

    private string? InputValue { get; set; }
}

위 처럼 string 변수에 있는 문자 내용을 input 쪽 변수에  바인딩 시킬수도 있고

 

<button class="@inputValue"   type="button"

처럼 버튼의 스타일 또한 변경이 가능하다

<button class="@inputValue"   type="button" disabled="@(_users.Count() >= 5)"

이렇게 유저의 카운트가 0 보다 크면 버튼이 안보이게 처리 또한 가능하다

_users 이 글을 참고해보면 된다

https://3dmpengines.tistory.com/2375

 

만약 데이터가 html 즉 UI 상에 변경된 것이 보이지 않는 상황이 다음 time 관련 코드에서 적용이 안될 수 있는데

 

 

이때는 StateHasChanged(); 함수를 호출해주면 변경된 값이 HTML/UI 상에 보이게 된다

 

 

반응형
반응형

유저의 목록과 추가및 제거에 대한 변수와 함수를 바인딩하는 것을 알아본다

 

기본적으로 html 부분에 code 쪽의 변수가 바인딩 되어 있는 경우 변수가 바뀔때 자동으로 데이터가 html 상에서 갱신된다

 

[유저 목록과 추가하기]

유저의 목록 기본적으로 3개를 만들어 for 문으로 3개가 보여지도록 처리해놓는다

@page "/user"
@using BlazorApp.Data;

<h3>Online Users</h3>

<p>
    Users : <b>@_users.Count</b>
</p>
<br/>

<ul class="list-group">
    @foreach(var user in _users)
    {
        <li @key="user" class="list-group-item">
            <label>@user.Name</label>
        </li>
    }
</ul>


<br/>

<div class="contrainer">
    <div class="row">
        <div class="col-md-6">
            <input type="text" class="form-control" placeholder="Add User" @bind-value="_inputName"/>
        </div>
        <div class="col-md-6">
            <button class="btn btn-primary" type="button" @onclick="AddUser" > Add a user </button>
        </div>
    </div>

</div>


@code {
    List<UserData> _users = new List<UserData>();

    string _inputName;

    protected override void OnInitialized()
    {
        _users.Add(new UserData { Name = "John" });
        _users.Add(new UserData { Name = "Jane" });
        _users.Add(new UserData { Name = "Jack" });
    }

    void AddUser()
    {
        _users.Add(new UserData { Name = _inputName });
        _inputName = string.Empty;
    }

}

 

user 명을 추가 하면 li 목록에 추가 되는 코드이다

 

함수 AddUser 또한 @onclick= 에 연결 할 수 있다

 

 

 

 

[제거]

@page "/user"
@using BlazorApp.Data;

<h3>Online Users</h3>

<p>
    Users : <b>@_users.Count</b>
</p>
<br/>

<ul class="list-group">
    @foreach(var user in _users)
    {
        <li @key="user" class="list-group-item">
            <button type="button" class=" btn btn-link" @onclick="(()=>KickUser(user))" > X </button>
            <label>@user.Name</label>
        </li>
    }
</ul>


<br/>

<div class="contrainer">
    <div class="row">
        <div class="col-md-6">
            <input type="text" class="form-control" placeholder="Add User" @bind-value="_inputName"/>
        </div>
        <div class="col-md-6">
            <button class="btn btn-primary" type="button" @onclick="AddUser" > Add a user </button>
        </div>
    </div>

</div>


@code {
    List<UserData> _users = new List<UserData>();

    string _inputName;

    protected override void OnInitialized()
    {
        _users.Add(new UserData { Name = "John" });
        _users.Add(new UserData { Name = "Jane" });
        _users.Add(new UserData { Name = "Jack" });
    }

    void AddUser()
    {
        _users.Add(new UserData { Name = _inputName });
        _inputName = string.Empty;
    }

    void KickUser(UserData user)
    {
        _users.Remove(user);
    }

}

 

 

이름 앞에 X 표시 버튼을 추가하였고 x 를 누르면 유저목록에서 유저가 제거된다

기본 3개에서 하나를 제거한 상태이다

 

@onclick="(()=>KickUser(user))"  이 코드는 람다형태로 onclick="KickUser(user)" 의 형태로는 지원이 되지 않기 때문에 람다로 연결해주면 가능하다

 

반응형
반응형

 

 

Binding.razor 페이지를 만들고 다음 처럼 작성한다

@code 부분의 변수가 html 코드 중 @_value 로 연결 되게 처리 할 수 있고

html 에서는 @bind-value=  들로 @code 쪽 갑과 연결 되어 양방향 연결이 되게 할 수 있다

@page "/binding"

<h3>Binding</h3>

<p>Value : @_value</p>

<input type="range" step="1" @bind-value="_value" @bind-value:event="oninput" />

@code {
    int _value = 10;

}

 

 

 

메뉴 레이아웃중 하단에 binding 텝을 만들어주면

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorApp</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="binding">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Binding
            </NavLink>
        </li>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

 

 

환경은 Blazor Server 이다

반응형
반응형

ValuesController

using HelloEmpty.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace HelloEmpty.controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        [HttpGet]
        public List<HelloMessage> Get()
        {
            List<HelloMessage> message = new List<HelloMessage>();
            message.Add(new HelloMessage() { Message = "Hello Web API 1!" });
            message.Add(new HelloMessage() { Message = "Hello Web API 2!" });
            message.Add(new HelloMessage() { Message = "Hello Web API 3!" });
            return message;
        }
    }
}

get() 이 클라에서 서버로 api/Values 경로로 api 를 쏘면 

서버에서 보낼 내용을 message 에 담아 브라우저에 보낸다

 

 

HelloMessage

namespace HelloEmpty.Models
{
    public class HelloMessage
    {
        public string Message { get; set; }
    }
}

실제 데이터는 Models 에 들어있게 된다

 

 

program 부분인데 이 부분은 디폴트이다

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace HelloEmpty
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

 

 

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace HelloEmpty
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            //mvc 를 사용하겠다는 것
            //services.AddControllersWithViews();
            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();


            //홈페이지 실행할때 어떤 주소로 어떤 부분을 연동하여 실행할지에 대한 부분
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                //endpoints.MapRazorPages();
                //endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
                /*
                endpoints.MapGet("/", async context =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });
                */
            });
        }
    }
}

 

DotNet Core 3.1 버전으로 하면 StartUp.cs 가 생성된다

 

 

실행결과

 

실제 데이터가 JSON 형태로 온것을 알 수 있다

 

반응형
반응형

 

 

우리는 웹 어플리케이션을 개발할 때, 보통 java, .NET, Node.js 등의 framework 또는 language를 사용하여 server-side project를 만든다. 그리고 Angular, React, Vue.js와 같은 javascript framework를 사용하여 브라우저에 데이터를 보여준다.

 

하지만, 우리는 server-side application에서 사용한 언어를 client-side에는 사용할 수가 없다. server-side와 client-side는 기본적으로 단절되어있다. 

 

우리가 만약, server language를 client에도 재사용한다면 좋지 않겠는가? 우리는 WebAssembly를 사용하여 그렇게 할 수 있다. 

 

Blazor는 무엇이고, 어떻게 사용해야 하는가?

Blazor는 C#을 사용하여 interactive web application을 만들 수 있는 오픈소스 framework이다. 우리는 Blazor에서 server, client code에 모두 C#을 사용할 수 있다. .NET runtime은 브라우저에서 우리가 생성한 .NET DLL을 실행할 수 있다. 

 

추가적인 plugin없이 주요 브라우저에서 실행이 가능하다. .NET standard와 호환하는 library를 사용할 수 있다. (NuGet package 포함) 

 

Blazor application은 WebAssembly에서 실행되고, WebAssembly는 JavaScript sandbox에서 실행된다. 

JavaScript와의 연결이 되어 있으므로, web sockets, file API, DOM의 브라우저 기능을 사용할 수 있다. 

 

Blazor application에서 JavaScript를 호출할 수 있고, 그 반대도 가능하다. 

 

Blazor Application의 Different Models

Blazor Server

Blazor Server는 ASP.NET Core application의 server측에서만 동작하고, SignalR을 사용하여 HTML과 통신한다. blazor.server.js라는 파일을 통해 가능하다. 

server-side application은 메모리에 남아있기 때문에, 사용자당 instance가 생성된다. 

장점은, Blazor WebAssembly에 비해 다운로드 사이즈가 작다. older browser에서도 디버깅이 가능하다. 

 

단점은, 서버와 연결되어 있어야 하기 때문에 offline support는 없다. 확장성도 리스크가 될 수 있지만, 동시접속 천단위까지 제공이 가능하다. 마지막으로, serverless deployment는 불가능하며, ASP.NET Core server가 반드시 필요하다. 

 

Blazor WebAssembly

WebAssembly의 도움으로 브라우저 client에서 실행된다. application이 실행되면 browser로 모든 파일을 다운받는다. (HTML, CSS, JavaScript files, .NET Standard DLL files)

 

offline mode에서 동작한다. server가 필요하지 않다. static files로 구성되어 있기 때문에, CDN, Server, Azure Storage에 배포가 가능하다. 

 

장점은, native browser application의 속도와 비슷한 정도로 빠르다. offline mode에서 동작이 가능하다. serverless deployment가 가능하다. browser로 file만 받으면, server는 필요하지 않다. 추가 plugin 없이, modern browser에서 동작이 가능하다. 배포가 쉽다. 

 

브라우저에서만 실행되기 때문에, 브라우저 capability에 의해 성능이 제한된다. 

 

단점은 최초 로딩시간이 길다. older browser에서는 동작하지 않는다. older browser에서 동작해야 한다면 Blazor server-side application이 더 나은 선택이 되겠다. 

 

Blazor Server와 Blazor WebAssembly Application의 차이점

두 application의 차이점을 살펴보자. 

 

1. index.html, _Host.cshtml

 wwwroot 폴더를 보면 Blazor WebAssembly App은 index.html 파일이 없고, Blazor 서버 앱은 없다. 

index.html파일은 blazor.webassembly.js 파일을 참조하고, application의 모든 파일을 다운로드 한다. 

이와 비슷한 일은 Blazor 서버 앱 Page/_Host.cshtml 파일에서 발생한다. 이 파일에서 참조하는 blazor.server.js 파일은 client WebSocket connection을 담당한다. 

 

그 외 Counter, Index, FetchData 파일이 동일하게 들어가 있으며, 이것은 component files를 재사용 할 수 있다는 뜻이다. 

 

2. setting

Blazor 서버 앱은 Startup.cs, appsettings.json 파일이 있고, Blazor WebAssembly App에는 없다. 

Blazor WebAssembly App에서 서비스를 등록하기 위해서는 Program.cs 파일을 사용해야 한다. 

 

 

ref : https://bigexecution.tistory.com/65

 

반응형
반응형

SPA 중심에는 대표적인 라이브러리/프론트프레임워크 React, vue/Angular, Blazor WebAssembly가 있다.

📖 SPA (Single Page Application)

기존의 웹서비스는 클라이언트(사용자)가 요청하면 서버가 해당 웹 페이지를 HTML 코드로 계산하고 반환하며, 많은 요청이 이루어지면 계산이 더욱 오래 걸립니다.

SPA는 모든 처리가 단일 페이지에서 수행되는 방식입니다. 첫 로드시에만 웹페이지를 계산하여 HTML로 반환합니다. 그 이후에는 필요한 데이터를 HTML로 전달받지 않고 (서버에서 렌더링 X) 필요한 데이터만 서버로부터 JSON으로 전달받아 동적으로 렌더링 합니다.

장점

1. 빠른 로딩 시간 (Quick Loading Time)

  • 단일 페이지에서 웹 앱에서 페이지가 로드되면 서버는 더 이상 HTML 또는 CSS를 보내지 않기 때문입니다.
    (Google Reasearch에 따르면 페이지를 로드하는 데 200 밀리 초 이상 걸리면 비즈니스와 판매에 큰 영향을 미침)

2. 좋은 캐싱 능력 (Good Caching Abilities)

  • 로컬 데이터를 효율적으로 캐시 할 수 있습니다. SPA는 하나의 요청 만 보내고 서버의 모든 데이터를 저장, 데이터에 대한 지속적인 액세스가 있기 때문에 사용자는 오프라인으로 작업할 수도 있습니다. (방문자에게 데이터비용을 요구하지 않음)

3. 신속한 프런트 엔드 개발 (Rapid Front-End Developement)

  • 단일 페이지 애플리케이션 아키텍쳐이기 때문

4. 향상된 사용자 경험 (Improved User Experience)

  • 굳이 새 페이지가 로드 될 때까지 기다릴 필요가 없음, 콘텐츠의 동적인 로드는 사용자에게 좋은 경험을 제공

주의

초기 구동속도가 느릴 수 있음.

검색엔진 최적화(SEO)

 

왜 SPA를 사용하나요?

단일 페이지 웹 앱 (SPA)는 웹 개발을 고려할 때 이상적인 선택입니다. Saas플랫폼, SEO가 중요하지 않은 커뮤니티에 쓰입니다.

프로그램과 사용자간의 훌륭하고 풍부한 통신을 원한다면 SPA 접근 방식이 필요합니다.

 

 

 

 

기존 MVC 등은 페이지 단위를 받는 형태였다고 함..  비효율적인 구조..

 

ref : https://dhddl.tistory.com/230

반응형
반응형

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApp2
{

    public enum ClassType
    {
        Knight,
        Archer,
        Mage
    }

    public class Player
    {
        public ClassType ClassType { get; set; }
        public int Level { get; set; }
        public int HP { get; set; }
        public int Attack { get; set; }
        public List<int> Items { get; set; } = new List<int>();
    }

    class Linq
    {
        static List<Player> _players = new List<Player>();

        static void Main(string[] args)
        {

            Random rand = new Random();

            for(int i=0;i<100;++i)
            {
                ClassType type = ClassType.Knight;
                switch(rand.Next(0, 3))
                {
                    case 0:
                        type = ClassType.Knight;
                        break;
                    case 1:
                        type = ClassType.Archer;
                        break;
                    case 2:
                        type = ClassType.Mage;
                        break;
                }

                Player player = new Player()
                {
                        ClassType = type,
                        Level = rand.Next(1, 100),
                        HP = rand.Next(100, 1000),
                        Attack = rand.Next(5, 50)
                };

                for(int j=0;j<5;++j)
                {
                    player.Items.Add(rand.Next(1, 101));
                }
                

                _players.Add(player);
            }

            //join
            {

                List<int> levels = new List<int>() { 1, 5, 9};

                var playerLevels = 
                    from p in _players
                    join l in levels            //player와 levels를 조인하는데
                    on p.Level equals l     //레벨 i 와 player.level 이 같은것만 조인한다
                    select p;

                foreach(var p in playerLevels)
                {
                    Console.WriteLine(p.Level);
                }
                int test = 0;
            }

            GetHightLevelKnights();


        }

        //레벨이 50 이상인 knight 만 추려서 레벨을 낮음->놎음 순으로 정렬
        private static void GetHightLevelKnights()
        {
            //linq 문법으로 db 쿼리문 처럼 조회 할 수 있다
            //from 은 foreach 로 생각해도 괜찮다
            //실행 순서는 from where orderby select 순으로실행된다 생각하면 된다
            var players =
                from p in _players
                where p.ClassType == ClassType.Knight && p.Level >= 50
                orderby p.Level
                select p;

            foreach (Player p in players)
            {
                Console.WriteLine($"{ p.Level} {p.HP} {p.ClassType}");
            }
        }
    }
}

 

player와 levels를 조인하는데 레벨 i 와 player.level 이 같은것만 조인하게 한다

 

                 List<int> levels = new List<int>() { 1, 5, 9};

                var playerLevels = 
                    from p in _players
                    join l in levels            
                    on p.Level equals l    

                    select p;

 

 

 

 

 

표준 Linq 방식

    public enum ClassType
    {
        Knight,
        Archer,
        Mage
    }

    public class Player
    {
        public ClassType ClassType { get; set; }
        public int Level { get; set; }
        public int HP { get; set; }
        public int Attack { get; set; }
        public List<int> Items { get; set; } = new List<int>();
    }

    class Linq
    {
        static List<Player> _players = new List<Player>();

        static void Main(string[] args)
        {

            Random rand = new Random();

            for(int i=0;i<100;++i)
            {
                ClassType type = ClassType.Knight;
                switch(rand.Next(0, 3))
                {
                    case 0:
                        type = ClassType.Knight;
                        break;
                    case 1:
                        type = ClassType.Archer;
                        break;
                    case 2:
                        type = ClassType.Mage;
                        break;
                }

                Player player = new Player()
                {
                        ClassType = type,
                        Level = rand.Next(1, 100),
                        HP = rand.Next(100, 1000),
                        Attack = rand.Next(5, 50)
                };

                for(int j=0;j<5;++j)
                {
                    player.Items.Add(rand.Next(1, 101));
                }
                

                _players.Add(player);
            }

            //중첩 from , ex : 모든 아이템 목록을 추출할때
            {
                var items = from p in _players
                            from i in p.Items
                            where i > 95
                            select new { p, i };

                var li = items.ToList();
                foreach(var elem in li)
                {
                    Console.WriteLine(elem.i +" : " + elem.p);
                }
            }


            //linq 표준연산자
            {
                var players =
               from p in _players
               where p.ClassType == ClassType.Knight && p.Level >= 50
               orderby p.Level
               select p;

                //위 결과와 아래 결과는 같다

                //from 은 생략 가능
                var sameResult = _players
                    .Where(p => p.ClassType == ClassType.Knight && p.Level >= 50)
                    .OrderBy(p => p.Level)
                    .Select(p => p);


                int iii = 0;
            }

두개의 결과가 같은 것을 볼 수 있다

 

            //linq 표준연산자
            {
                var players =
               from p in _players
               where p.ClassType == ClassType.Knight && p.Level >= 50
               orderby p.Level
               select p;

                //위 결과와 아래 결과는 같다

                //from 은 생략 가능
                var sameResult = _players
                    .Where(p => p.ClassType == ClassType.Knight && p.Level >= 50)
                    .OrderBy(p => p.Level)
                    .Select(p => p);

 

결과 : 

세부적인 기능을 사용하고 싶다면 함수형 linq 를 사용하면 된다

 

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

default 연산자  (0) 2023.04.25
linq (2)  (0) 2023.04.09
Linq (1)  (0) 2023.04.08
async/await & 커피와 베이컨  (0) 2023.04.07
C# - ArraySegment  (0) 2023.01.04

+ Recent posts