이번에는 test.php 파일을 만들어서 잘 동작하는지 테스트해본다.

 

먼저 리눅스에서 텍스트 파일을 사용하기 위해서 텍스트 편집기가 필요한데 일반적으로 vim이나 nano를 사용한다.

 

vim이 좀 더 복잡하지만 기능이 많고 nano가 더 단순하면서 간단하게 쓰는 도구라고 하는데 

 

일단 서버에 깔려있는 건 vim이라서 그대로 vim을 쓰기로 한다.

 

vim 버전확인

vim --version

 

파일을 생성하기 위해서는 경로가 필요한데 생각해 보니 아직 리눅스 서버의 폴더 구조를 모른다.

 

일단 폴더가 뭐가 있는지 확인해 본다.

 

ls /

 

ls 명령어는 자주 사용되는 것 중 하나로 list를 의미한다.

 

리눅스의 ls는 디렉터리 내의 파일과 폴더 목록을 나열하는 데 사용되며 / 를 통해서 루트 디렉터리에서 목록을 나열해 본다.

 

 

- bin : 시스템 부팅 및 작업에 필수적인 명령어들이 위치한다. 

- dev : 하드웨어 장치와 관련된 파일들이 들어 있다.

- home : 일반 사용자 계정의 홈 디렉터리가 위치한다. 각 사용자의 개인 디렉토리

- lib64 : 64비트 시스템 및 프로그램에 필요한 라이브러리  위치

- mnt : 일반적으로 임시로 다른 파일 시스템을 마운트 하는 디렉터리

- proc : 가상 파일 시스템으로, 시스템 상태나 프로세스 정보를 제공

- run : 시스템의 현재 실행 중인 프로세스와 관련된 정보가 저장

- srv : 서버가 제공하는 서비스에 관련된 데이터를 저장 (웹 서버, FTP 서버 등)

- tmp : 임시 파일들이 저장되는 경로, 시스템이 재부팅되면 지워진다.

- var : 로그 파일, 캐시 파일, 스풀 파일 등 변동하는 데이터가 저장되는 경로

- boot : 커널 파일 및 부트로더 관련 파일이 있다.

- etc : 시스템 설정 파일들이 모여 있는 경로이며 네트워크 설정, 사용자 설정 등이 포함된다.

- lib : 32비트 프로그램 실행에 필요한 필수적인 라이브러리 파일

- media : usb 드라이브나 cd/dvd 등 이동식 미디어 장치가 마운트 되는 곳

- opt : 추가적인 소프트웨어 패키지들이 설치되는 디렉터리

- root : 시스템의 루트 사용자 홈 디렉토리

- sbin : 시스템 관리자용 명령어들이 위치하는 경로

- sys : 시스템 관련 정보를 제공하는 가상 파일 시스템

- usr : 사용자 프로그램과 관련된 파일들이 위치로 프로그램, 라이브러리, 문서 등이 포함

 

Apache 웹 서버의 경우 보통 /var/www/html/ 경로에서 파일을 처리한다고 하는데 먼저 이 경로에서 파일을 만들고 테스트했을 파일을 못 찾는 문제가 있었다.

 

 

어떤 문제인지 찾아보면서 Apache 설치와 실행 상태, 찾으려는 파일의 디렉터리 권한도 변경해보고 했지만 해결이 안 되었다.

 

그리고 Apache 로그를 조회해 보았는데 경로에 문제가 있었다는 걸 알게 되었다.

 

$sudo tail -f /var/log/httpd/error_log

 

/var/www/html/ 가 아닌 /home/bakcoding/html/ 에서 test.php 파일을 찾고 있어서 Not Found 에러가 뜬 것

 

서버 환경이나 세팅에 따라서 달라지는 부분인가 보다.

 

처음부터 에러 로그를 찾았다면 더 빨리 해결됐을 문제였다.

 

이제 정확한 경로를 알게 됐으니 다시 파일 생성으로 돌아간다.

 

vim을 사용해서 파일을 생성해 보기로 한다.

 

$sudo vim /home/username/html/index.php

 

sudo는 Super Do로 이걸 붙이면 관리자 권한으로 실행과 마찬가지로 볼 수 있다.

 

권한에 따라 sudo 없이 vim만 실행하면 파일을 열어볼 수 만 있으므로 sudo를 붙여서 명령어를 실행해 준다.

 

위 명령어를 실행하면 파일이 있는 경우 해당 파일이 열리고 없으면 새로 생성되면서 콘솔창이 vim 창으로 바뀐다.

 

 

이 상태에서는 특정 키워드를 입력하면 동작으로 이어지게 되는데 먼저 파일을 작성할 때는 명령어 입력 상태에서 i를 입력하면 된다.

 

그리고 php 내용을 채워주는데 간단하게 db에 연결하고 성공과 실패 시 리턴 그리고 테이블에 값도 넣어보도록 한다.

 

<?php
// MySQL Connection
$servername = "localhost";
$username = "userid";
$password = "password";
$dbname = "testdb";

//conn
$conn = new mysqli($servername, $username, $password, $dbname);

//conn check
if ($conn->connect_error){
        die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully";

// data insert test
$sql = "INSERT INTO users (username, email) VALUES ('testuser', 'test@example.com')";
if ($conn->query($sql) === TRUE){
        echo "New record created successfully";
} else{
        echo "Error: " . $sql . "<br>" . $conn->error;      
}
$conn->close();
?>

 

이렇게만 해놓고 

 

이제 http://내 서버 주소/test.php를 주소창에 입력했을 때 아무 문제가 없다면 성공했다는 텍스트가 뜨고 db에 값이 제대로 들어가 있는지 보면 된다.

 

 

 

php 테스트 일단은 문제가 없는 것으로 보인다.

728x90
반응형

'Develop > Server' 카테고리의 다른 글

vim 명령어  (0) 2025.03.01
리눅스 명령어 - ls  (0) 2025.03.01
[NHN Cloud Hosting - Linux] mysql 테스트  (0) 2025.03.01
[NHN Cloud Hosting - Linux] SSH로 서버 접속  (0) 2025.03.01
SSH와 RDP  (0) 2025.03.01

 

이번에는 mysql에 로그인하고 테스트해본다.

제품 설명

 

내가 결제한 서버의 설명을 보면 DB는 MySQL 8.0이라고 표기되어있다.

 

먼저 mysql이 잘 설치되어 있는지와 버전도 일치하는지 확인해 본다.

 

$mysql --version

 

 

문제가 없어 보이니 이제 서버 신청 시 입력했던 DB 계정으로 로그인해본다.

 

$mysql -u 'userid' -p

 

userid에 db 계정을 입력하고 엔터를 치면 패스워드를 입력하는 단계로 넘어가고 패스워드까지 문제없이 입력하고 나면 명령어 창이 mysql>로 바뀌고 mysql 명령어를 입력할 수 있는 상태로 된다.

 

테스트 용으로 데이터베이스를 하나 만들어 본다.

 

CREATE DATABASE testdb;

 

명령어를 입력하니 권한이 없다는 에러가 발생한다.

 

일단 mysql 콘솔창을 나간다. 이때 명령어 창에 quit 또는 exit을 입력하면 나올 수 있다.

 

이번엔 root 계정으로 로그인한 다음 내 계정에 권한을 주기로 한다.

GRANT ALL PRIVILEGES ON *.* TO 'userid'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;

 

권한을 준 다음 즉시 변경 사항이 반영되도록 한다.

 

그리고 다시 내 계정으로 로그인 후 testdb를 생성하고 잘 생성됐는지 목록도 뽑아 본다.

 

SHOW DATABASES;

 

이번엔 해당 데이터베이스에 테이블도 만들어 본다.

 

USE testdb;
CREATE TABLE users (
	id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL
);

 

 

 

생성된 테이블을 목록에 뽑아 본다.

 

SHOW TABLES;

 

생성한 users 테이블이 잘 보이는 게 확인된다.

 

테이블의 자세한 정보도 확인한다.

 

DESCRIBE users;

 

 

입력한 대로 잘 만들어졌다.

728x90
반응형

'Develop > Server' 카테고리의 다른 글

리눅스 명령어 - ls  (0) 2025.03.01
[NHN Cloud Hosting - Linux] php 테스트  (1) 2025.03.01
[NHN Cloud Hosting - Linux] SSH로 서버 접속  (0) 2025.03.01
SSH와 RDP  (0) 2025.03.01
NHN 리눅스 서버 개설  (0) 2025.03.01

리눅스에서도 Xrdp를 사용하면 GUI가 있는 RDP를 사용할 수 있다고 한다.

 

그럼에도 SSH를 사용하기로 한 이유는 서버 사양을 고려한 부분이 크고 다른 이유는 다양한 명령어들을 이번 기회에 공부할 수 있을 것 같아서이다.

 

PuTTY

SSH 클라이언트로 사용할 프로그램은 대표적인 PuTTY로 정했다.

 

PuTTY

 

Download PuTTY: release 0.83

0.83, released on 2025-02-08, is the latest release. You can also find it at the Latest Release page, which will update when new releases are made (and so is a better page to bookmark or link to). Release versions of PuTTY are versions we think are reasona

www.chiark.greenend.org.uk

 

PuTTY 최신 버전을 다운로드하여 설치하고 puttygen.exe 도 다운로드해 놓는다.

 

SSH로 접속할 때 서버 접속 인증키가 필요한데 NHN에서 서버를 생성할 때 만든 키 파일은. pem 확장자로 PuTTY에서는 이 파일을 그대로 사용할 수 없기 때문에. ppk로 변환하는 과정이 필요하다.

 

먼저 서버를 신청할 때 생성하고 다운해 놓은 서버 접속 인증키를 준비해 놓고 puttygen.exe를 실행시킨다.

 

puttygen.exe

 

Load 버튼을 클릭하면 탐색기가 열리는데 여기서 All Files 설정으로 모든 파일이 보이게 한 다음 내 서버 접속 인증키. pem 파일을 불러온다. 

 

불러온 후에 알림 창이 뜨는데 성공적으로 불러오게 되면 Successfully imported ~ 내용을 확인할 수 있다.

 

이제 활성화된 버튼 Save private key를 클릭하고 알아볼 수 있는 이름으로 저장한다.

 

이제 putty를 실행한 다음 서버와 연결에 필요한 정보들을 채워 넣는다.

 

Session 항목

Host Name (or IP address) : 서버 이름이나 주소

Port : 기본 22

Connection type : SSH

 

PuTTY - configration

 

 

Connection > SSH > Auth > Credential 항목에서 Private key file for authentication에 생성해 놓은. ppk 파일을 선택한다.

PuTTY - configration

 

이렇게 세팅해 놓고 다시 Session 항목으로 이동해서 현재 설정해 놓은 session 정보를 저장해 두면 나중에 다시 접속할 때 편하다.

 

PuTTY - session save

 

Saved Sessions 아래에 세션이름을 입력하고 Save 클릭하면 해당 이름으로 세션이 저장된다.

 

이제 내 서버에 SSH 연결할 준비가 끝났다.

 

Open을 눌러주면 터미널 창이 뜬다.

 

PuTTY - login as :

 

창에는 login as :라는 문구만 보이고 무언가 입력을 기다리고 있다.

 

이는 어떤 사용자 계정으로 로그인할지에 대한 입력을 기다리는 것으로 이 계정은 서버에 따라 다르기 때문에 확인을 해서 입력한다.

 

내가 사용 중인 서버는 CentOS이며 사용자 계정이름은 centos이다.

 

centos를 입력하고 엔터를 치면

 

PuTTY

 

성공적으로 서버에 접속했다.

728x90
반응형

'Develop > Server' 카테고리의 다른 글

리눅스 명령어 - ls  (0) 2025.03.01
[NHN Cloud Hosting - Linux] php 테스트  (1) 2025.03.01
[NHN Cloud Hosting - Linux] mysql 테스트  (0) 2025.03.01
SSH와 RDP  (0) 2025.03.01
NHN 리눅스 서버 개설  (0) 2025.03.01

서버가 만들어지고 가장 처음 해야 할 작업은 내 서버에 접속을 하는 것이다.

 

접속하는 방법에는 SSH와 RDP 크게 두 가지 방식이 있다.

 

RDP (Remote Dsktop Protocol)

원격 데스크톱 프로토콜 

다양한 종류의 RDP 소프트웨어들이 있는데 Microsoft에서 개발한 RDP 프로토콜을 확장하거나 개선한 형태로 제공되며 기본적으로 원격 컴퓨터의 화면을 클라이언트에게 전송하고 클라이언트의 입력을 서버로 전달한다.

 

사용자가 그래픽 환경을 원격으로 사용할 수 있도록 즉, 데스크톱 화면을 그대로 보면서 마우스와 키보드로 제어할 수 있는 방식으로 일반적으로 윈도우 환경에서 많이 사용된다. GUI를 그대로 볼 수 있기 때문에 사용이 편리하고 직관적이라 원격으로 그래픽 환경이 필요한 작업을 할 때 유용하다.

 

SSH (Sercure Shell)

보안 쉘

 

주로 터미널 기반의 명령어를 사용하여 원격 시스템에 접속하는 데 사용되며 이는 GUI가 따로 없으며 명령줄 인터페이스(CLI)만을 사용하여 서버에 접속하여 제어할 수 있다.

 

SSH는 암호화된 통신을 제공하는 프로토콜이며, 서버와 클라이언트 간에 안전한 데이터 전송을 보장한다. 이 접속 방식은 서버의 텍스트 기반 셸에 접근할 수 있으며 일반적으로 리눅스나 유닉스 시스템에서 많이 사용된다.

 

결론적으로 두 방식의 가장 큰 차이점은 GUI 유무에 있고 그 안에서 각각의 장단점이 존재하게 된다.

 

 

728x90
반응형

항상 무료 플랜만 찾아다니며 사용했는데 그러다 보니 제약도 많고 내가 원하는 것들을 써보지도 못하고 끝나는 경우가 많았다.

 

그래서 테스트나 간단하게 사용하기 위한 용도로 저렴한 서버를 찾다가 NHN에 적당한 가격을 찾게 되어 사용해 보기로 한다.

 

 

서버의 사양은 가격만큼 낮지만 간단하게 개발용으로 쓰면서 공부하기에는 부족하지 않을 것이라고 생각된다.

 

일단 큰 금액이 아니기 때문에 즉시 결제하고 바로 활용해 보기로 한다.

 

 

웹 환경은 php가 필요하기 때문에 선택한다. 그에 필요한 Apache도 함께 설치된다.

 

 

접근 포트를 미리 선택해 놓으면 기본 보안 그룹 설정이 반영된다고 하는데 리눅스 터미널은 무조건 필요하지 않을까 싶어서 선택해 주었고 FTP 포트는 쓰긴 할 것 같아서 선택했다.

 

이 부분은 나중에 보안그룹에서 변경이나 추가해서 설정이 가능하다고 하니 일단 이렇게 진행하기로 한다.

 

이후로 FTP 정보, DB 정보를 입력하고 혹시 모르니 따로 메모해 둔다.

 

 

마지막으로 키페어(서버 접속 인증 키)에 대한 안내가 나오는데 이게 있어야 서버에 SSH 연결을 할  수 있고 보안을 위해서 신청 시 잘 보관하지 않았다가 이걸 잃어버리면 서버에 연결할 수 없기 때문에 주의해야 한다.

 

기존 키페어가 없으니 적당한 이름을 넣어주고 새로 만들어 생성된 키를 다운로드하여서 잘 보관하도록 한다.

 

 

 

신청하고 나면 5분 이내로 서버가 생성되고 클라우드 호스팅 콘솔 페이지에서 서버의 ip나 사용량 등 세부적인 사항들을 확인할 수 있다.

 

윈도우 서버를 사용해 본 경험이 있긴 하지만 그건 이미 세팅이 완료된 상태에서 원격으로 접속한 것뿐이었다.

이렇게 서버를 열어본 게 처음이고 거기다 리눅스를 사용해 본 적도 없기 때문에 앞으로 서버를 사용하면서 다양하고 새로운 정보와 문제들과 마주할 것으로 생각되는데 나중에 이 정보들이 다시 필요한 상황이 왔을 때 참고가 되도록 자세하게 잘 정리해 보도록 해야겠다.

728x90
반응형

'Develop > Server' 카테고리의 다른 글

리눅스 명령어 - ls  (0) 2025.03.01
[NHN Cloud Hosting - Linux] php 테스트  (1) 2025.03.01
[NHN Cloud Hosting - Linux] mysql 테스트  (0) 2025.03.01
[NHN Cloud Hosting - Linux] SSH로 서버 접속  (0) 2025.03.01
SSH와 RDP  (0) 2025.03.01

버전 : Unity 6

 

구글 계정에 연동과정

 

Google Sign In SDK에서 ID 토큰, 액세스 토큰을 받는다.

 

Firebase Authentication으로 Google ID 토큰을 Firebase로 전달하고 이를 인증한다.

 

Google Sign In SDK는 구글 계정으로 로그인할 수 있지만 이후 로그인한 사용자를 앱의 인증 시스템에서 관리할 방법이 없다.

 

또한 Google ID 토큰을 앱에서 직접 검증하는 것은 보안상 위험하기 때문에 파이어베이스를 통해서 토큰을 검증하고 관리하여 안전하게 관리할 수 있고 파이어베이스의 다양한 기능과 연계하여 사용할 수도 있다.

 

1. 파이어베이스 인증 SDK 설치

Firebase Authentication SDK

 

Google 로그인과 Unity를 사용하여 인증하기  |  Firebase

의견 보내기 Google 로그인과 Unity를 사용하여 인증하기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Google 로그인을 앱에 통합하여 사용자가 Google 계정으로

firebase.google.com

 

Firebase Authentication

 

다운로드한 압축 파일에서 FirebaseAuth.unitypackage 패키지를 프로젝트에 임포트

 

FirebaseAuth

 

2. Google-Signin-Unity

Google-SignIn-Unity

 

GitHub - googlesamples/google-signin-unity: Google Sign-In API plugin for Unity game engine. Works with Android and iOS.

Google Sign-In API plugin for Unity game engine. Works with Android and iOS. - googlesamples/google-signin-unity

github.com

 

최신버전 1.0.4 사용

 

* 프로젝트에 임포트하면 Unity.Task 관련해서 충돌 에러가 발생하는데 Assets > Parse  폴더를 지우면 해결 가능하다.

 

설치 후 패키지 문제 없는지 테스트 빌드를 진행

 

gradle 에러가 발생해서 빌드에 실패한다. 안드로이드 빌드 시 자주 발생하는 에러로 이런 경우 프로젝트 세팅의 최소 타겟 API를 올리면 해결되는 경우가 많은데 먼저 이 부분을 확인해서 다시 빌드해 본다.

 

Minimum API Level 23 -> 24로 변경 후 다시 빌드하니 해결되었다.

 

3. Key Store 생성

파이어베이스에서 인증 정보를 저장하고 관리할 프로젝트를 생성해야 하는데 이때 유니티 프로젝트의 SHA 키가 필요하다.

 

먼저 유니티에서 키스토어를 생성하고 SHA를 확인해 둔다.

 

1. 키스토어 생성

 

 

2. SHA 확인

키스토어를 열어보기 위해서는 keytool을 사용해야 하는데 유니티 에디터를 설치하면 포함되어 있기 때문에 에디터 설치 경로에서 

keytool.exe 파일이 위치한 경로에서 명령 프롬프터를 켜서 'keytool -list -keystore [키스토어 경로]' 를 실행한다.

 

 


* 구글 계정 인증에는 SHA-1 이 필요한데 Unity 6 버전의 keytool을 사용했더니 256만 뜨고 나머지 지문들은 생략된다.

   다른 에디터 버전의 keytool을 사용해서 SHA-1을 확인하고 메모해 둔다.

 

해당 키는 잠시 메모해 둔다.

 

4. 파이어베이스 프로젝트 세팅

Firebase

 

Firebase | Google's Mobile and Web App Development Platform

개발자가 사용자가 좋아할 만한 앱과 게임을 빌드하도록 지원하는 Google의 모바일 및 웹 앱 개발 플랫폼인 Firebase에 대해 알아보세요.

firebase.google.com

 

로그인 인증을 처리할 프로젝트를 생성한다.

 

 

유니티 플랫폼을 선택해서 앱을 추가한다.

 

 

여기서 디지털 지문 추가에서 키스토어의 SHA 키를 입력한다.

 

Authentication 항목으로 들어가서 로그인 제공업체를 추가한다.

 

 

Google을 선택하고 사용설정을 해준다.

 

5. 클라이언트 ID 확인

Google Cloud

 

Google 클라우드 플랫폼

로그인 Google 클라우드 플랫폼으로 이동

accounts.google.com

 

구글 클라우드에 접속하면 파이어베이스에서 생성했던 프로젝트와 동일한 정보로 프로젝트가 생성되어 있다.

 

이 중에서 사용자 인증 정보 항목에서 OAuth 2.0 클라이언트 ID를 사용해서 유니티에서 접속을 시도한다.

 

 

6. 로그인 스크립트

유니티로 돌아가서 로그인 스크립트를 구현한다.

 

* 에러

SignIn 함수 호출 시 계정 선택 UI 팝업이 뜬 후 계정을 선택하고 나서 반응이 없는 현상이 있었는데 결과를 받아서 처리하는 콜백에서 에러가 발생했었다.

 

이때 메인스레드에서 처리되도록 Dispatcher 함수를  구현해서 처리하니 문제가 해결되긴 했는데 정확한 원인은 확인을 못한 부분이다.

 

using System.Collections.Generic;
using UnityEngine;

public class MainThreadDispatcher : MonoBehaviour
{
    private static MainThreadDispatcher instance;
    private readonly Queue<System.Action> executionQueue = new Queue<System.Action>();

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    private void Update()
    {
        lock (executionQueue)
        {
            while (executionQueue.Count > 0)
            {
                executionQueue.Dequeue()?.Invoke();
            }
        }
    }

    public static void RunOnMainThread(System.Action action)
    {
        if (instance != null)
        {
            lock (instance.executionQueue)
            {
                instance.executionQueue.Enqueue(action);
            }
        }
    }
}

 

 

디스패처를 사용해 SignIn 함수 결과를 처리한다.

 

using Google;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;

public class GoogleLogin : MonoBehaviour
{
    private string web_client_id = "구글 클라우드의 클라이언트 ID";

    private void Awake()
    {
        Init();
    }

    private void Init()
    {
        GoogleSignIn.Configuration = new GoogleSignInConfiguration
        {
            WebClientId = web_client_id,
            UseGameSignIn = false,
            RequestEmail = true,
            RequestIdToken = true
        };
    }

    public void SignIn()
    {
        DebugMessage.Instance.ShowMessage("Calling SignIn");
        Debug.Log("Calling SignIn");
        GoogleSignIn.DefaultInstance.SignIn().ContinueWith(
          OnAuthenticationFinished);
    }

    internal void OnAuthenticationFinished(Task<GoogleSignInUser> task)
    {
        Debug.Log("Authentication finished, processing on main thread");
        MainThreadDispatcher.RunOnMainThread(() => ProcessAuthResult(task));
    }

    private void ProcessAuthResult(Task<GoogleSignInUser> task)
    {
        Debug.Log("Auth Result");
        if (task.IsFaulted)
        {
            using (IEnumerator<System.Exception> enumerator = task.Exception.InnerExceptions.GetEnumerator())
            {
                if (enumerator.MoveNext())
                {
                    GoogleSignIn.SignInException error = (GoogleSignIn.SignInException)enumerator.Current;
                    DebugMessage.Instance.ShowMessage("Got Error: " + error.Status + " " + error.Message);
                    Debug.Log("Got Error: " + error.Status + " " + error.Message);
                }
                else
                {
                    DebugMessage.Instance.ShowMessage("Got Unexpected Exception?!?" + task.Exception);
                    Debug.Log("Got Unexpected Exception?!?" + task.Exception);
                }
            }
        }
        else if (task.IsCanceled)
        {
            DebugMessage.Instance.ShowMessage("Canceled");
            Debug.Log("Canceled");
        }
        else
        {
            DebugMessage.Instance.ShowMessage("Welcome: " + task.Result.DisplayName + "!");
            DebugMessage.Instance.ShowMessage(task.Result.Email + "!");
            DebugMessage.Instance.ShowMessage(task.Result.IdToken + "!");

            Debug.Log("Welcome: " + task.Result.DisplayName + "!");
            Debug.Log(task.Result.Email + "!");
            Debug.Log(task.Result.IdToken + "!");
        }
    }
}

 

 

폰트에 한글이 지원되지 않아서 깨진 것 빼고는 로그인 성공 후 들어오는 리턴 정보를 로그로 찍은 값들이 잘 출력되는 게 확인된다.

 

 

728x90
반응형

'Develop > Unity' 카테고리의 다른 글

InputSystem 기본 사용법  (0) 2025.03.25
유니티 기본 물리 샘플  (0) 2025.03.21
2D 애니메이션, 이펙트  (0) 2024.12.03
Input System 사용시 UI 상호작용 안될때  (0) 2024.11.18
유니티 프로젝트 창 검색 활용  (0) 2024.06.30

최근에 UI 애니메이션을 작업하다 정리가 필요한 부분이 있어서 작성한다.

 

2D Sprite

2D 스프라이트의 애니메이션은 리소스와 Animator를 가지고 만들 수 있다.

Sprite Renderer Animation

 

원하는 프레임마다 스프라이트를 변경하는 방식으로 애니메이션을 만들 수 있다.

 

UI Animation

UI 경우에도 애니메이션으로 컨트롤하는 경우에는 동일한 방식으로 처리된다.

UI Animation

 

Effect

Sprite Renderer를 사용하는 상황에서 뭔가 발산하는 이펙트를 사용한다면 어떻게 하는 게 좋을까 생각하면서 파티클 시스템과 오브젝트를 직접 제어하는 방법 두 가지를 사용해 보았다.

 

 

있는 리소소 가지고 대충 테스트만 하려고 만들다 보니 별로 이뻐 보이진 않는다.

 

이러한 상황에서는 오브젝트를 가지고 직접 제어하는 게 나은 방식인 거 같다.

 

 

실제로 사용한다면 풀링을 해서 쓰겠지만 그럼에도 상당히 많은 오브젝트를 뿜어낸다면 파티클을 쓰는 게 적합하다고 보인다.

 

상황에 맞게 잘 조율하는 게 필요하다.

 

위에서 사용한 코드

 

using UnityEngine;

public class Coin : MonoBehaviour
{
    private const float power = 10f;
    private void OnEnable()
    {
        transform.position = Vector3.zero;
        ApplyForce();
    }

    private void ApplyForce()
    {
        var rb = GetComponent<Rigidbody2D>();
        float randomHorizontal = Random.Range(-1f, 1f); // -1 to 1 for left/right
        Vector2 direction = new Vector2(randomHorizontal, 1f).normalized;
        rb.AddForce(direction * power, ForceMode2D.Impulse);
    }
}

using UnityEngine;

public class Coin : MonoBehaviour
{
    private const float power = 10f;
    private void OnEnable()
    {
        transform.position = Vector3.zero;
        ApplyForce();
    }

    private void ApplyForce()
    {
        var rb = GetComponent<Rigidbody2D>();
        float randomHorizontal = Random.Range(-1f, 1f); // -1 to 1 for left/right
        Vector2 direction = new Vector2(randomHorizontal, 1f).normalized;
        rb.AddForce(direction * power, ForceMode2D.Impulse);
    }
}

 

대충 동전이 뿜어지는 듯한 모습을 중점으로 만들었다.

 

Coin 간에는 충돌처리하면 초기 한 위치에 모여있을 때 서로 부딪혀서 Physics2D > Layer Collision Matrix를 꺼두었다.

 

바닥에 충돌 후 튕기는 것과 미끄러지는 정도는 Phsics Material로 조절한다 (Friction 마찰력, Bounciness 탄성력)

 

UI Effect

위 내용들은 겸사겸사로 같이 정리한 내용이고 이 글을 쓰기 시작한 이유는 이 부분 때문이었다.

 

상황은 대충 이렇다.

 

간단한 퍼즐 게임을 만드는 중에 스프라이트 렌더러를 쓰기에는 귀찮은 부분들이 있어서 간단하게 만들려고 UI를 베이스로 해서 게임 로직들을 만들었고 그렇게 진행하다 보니 효과를 추가하는 과정에서 물리적인 부분을 사용할 수 없어 직접 위치를 이동시켜서 유사하게 재현할 수밖에 없었다.

 

UI Animation

 

using UnityEngine;

public class UICoinMaster : MonoBehaviour
{
    [SerializeField] UICoin[] uiCoins;

    bool isOn = false;
    public void OnClick_UICoinMaster()
    {
        if (isOn)
        {
            foreach( var coin in uiCoins)
            {
                coin.gameObject.SetActive(false);
            }
            isOn = false;
        }
        else
        {
            foreach (var coin in uiCoins)
            {
                coin.gameObject.SetActive(true);
            }
            isOn = true;
        }
    }
}


using UnityEngine;

public class UICoin : MonoBehaviour
{
    [SerializeField] private float initialForce = 10f;
    [SerializeField] private float gravity = 9.8f;
    [SerializeField] private float bounceForce = 0.5f;
    [SerializeField] private bool isMoving = false;

    private RectTransform rect;
    private Vector2 velocity;
    private Vector2 originPos;
    private void Awake()
    {
        rect = GetComponent<RectTransform>();
        originPos = rect.position;
    }

    private void OnEnable()
    {
        ApplyPower();
    }

    private void OnDisable()
    {
        rect.position = originPos;
    }

    private void ApplyPower()
    {
        float randomX = Random.Range(-1f, 1f);
        velocity = new Vector2(randomX, 1f).normalized * initialForce;
        isMoving = true;
    }

    private void Update()
    {
        if (!isMoving) return;

        velocity.y -= gravity * Time.deltaTime;

        rect.anchoredPosition += velocity * Time.deltaTime;

        if (rect.anchoredPosition.y < 0)
        {
            rect.anchoredPosition = new Vector2(rect.anchoredPosition.x, 0);
            velocity.y = -velocity.y * bounceForce;
            velocity.x *= 0.8f;
            
            if (Mathf.Abs(velocity.y) < 0.1f)
            {
                isMoving = false;
            }
        }
    }
}

 

UI 크기에 맞춰서 값들을 적절하게 세팅하면 그럴듯해 보인다. 구현하는 데는 크게 무리가 없지만 UI 가지고 이래도 되나 싶은 생각이 들기도 한다.

 

개인적으로는 필요한 기능만 직접 구현해서 쓰는 걸 선호하는 편이라 잘 쓰진 않지만 위처럼 UI를 제어하는 데는 DoTween을 사용하는 게 더 간단하고 다양한 동작들도 처리할 수 있을 것이다.

 

UI Particle System

한 번쯤은 'UI위에 파티클 뿌리기'에 대해서 많은 고민과 탐구를 해보았을 것이다. 이 경우 나는 주로 카메라의 렌더링 모드를 Screen Space - Camera로 세팅해서 파티클을 보이게 하는 방법을 사용했다. 그런데 이 방법은 원하는 파티클을 사용할 때나 좌표계를 다룰 때 은근히 귀찮고 까로운면이 있었다.

 

그래서 이번에 좀 더 찾다 보니 좋은 방법을 알게 되었다.

 

https://github.com/Unity-UI-Extensions/com.unity.uiextensions.git

 

GitHub - Unity-UI-Extensions/com.unity.uiextensions

Contribute to Unity-UI-Extensions/com.unity.uiextensions development by creating an account on GitHub.

github.com

 

 

UI Extensions 라이브러리로 여기에서 UIParticle System을 사용하면 파티클을 UI 위에 렌더링 할 수 있는 상태로 만들 수 있다.

 

해당 라이브러리에 대해서는 알고는 있었는데 파티클 관련 기능이 있었다는 건 이번에 알게 되었다.

 

이와 관련해서 UIParticle System 사용방법과 파티클의 기본 사용방법을 배우기 좋은 영상을 메모해 둔다.

 

https://www.youtube.com/watch?v=hiRdux33UCs

 

파티클을 조금 참고해서 만들어 적용시켜 본다.

 

효과를 주고 싶은 UI의 자식에 파티클을 할당하면 UI와 동일한 렌더링 우선순위로 처리가 된다는 점에서 원하는 기능 그 자체였다.

 

 

Performance

겸사겸사로 추가된 내용들이 많지만 결국 UI로 동작들을 구현해 버리면 성능상에 좋지 않은 건 사실이고 권장되는 방식도 아니다.

가장 큰 이유는 UI는 변경될 때마다 캔버스가 리빌드를 돌리기 때문에 이러한 동작이 과도하게 발생되며 이 연산은 CPU에서 처리하기 때문에 신경이 안 쓰일 수 없지만 어느 정도 타협해서 사용한다면 괜찮지 않을까 생각도 든다.

 

UI로 만들어봤자 엄청나게 복잡한 게임도 아닐 것이고 퍼즐 게임 정도면 성능상에 이슈를 발생시킬 만큼은 아닐 것이라고 감히 예상한다.

 

정말 괜찮은지는 프로파일링을 해봐야겠지만 궁금하기도 하니 나중에 시간 날 때 게임 오브젝트와 UI로 구현한 것을 각각 최대한 비슷하게 만들어 놓고 성능을 한 번 비교해 보는 것이 괜찮을 것 같다.

 

728x90
반응형

Player 세팅에서 Input System Package만 사용하는 상태

 

게임 뷰 창에서 버튼이 클릭되지 않는 문제가 발생했다.

 

하지만 시뮬레이터 창, 빌드 후에는 정상적으로 동작이 되는 문제가 있었다.

 

이런저런 방법을 시도해 보다가 혹시나 해서 시뮬레이터 창을 닫고 다시 플레이해 보니

 

정상적으로 동작이 되었다.

 

두 창이 모두 활성화된 상태에서 플레이될 때 에디터루프에서 인풋시스템이 초기화하면서 문제가 생긴 건지 별거 아닌 문제 때문에 꽤 시간을 빼앗긴 일이 생겨서 메모해 둔다.

728x90
반응형

'Develop > Unity' 카테고리의 다른 글

구글 계정 연동  (1) 2025.02.28
2D 애니메이션, 이펙트  (0) 2024.12.03
유니티 프로젝트 창 검색 활용  (0) 2024.06.30
Project Settings - Editor #2 (Asset Pipeline ~ Graphics)  (0) 2024.02.15
Asset Serialization, Binary and Text  (0) 2024.02.14

충돌 처리

BP_Bullet의 Event Graph로 넘어가서 충돌을 처리하는 로직을 만든다.

 

OnComponentHit 이벤트를 추가하고 Other Actor에서 BP_Enemy를 찾아 충돌한 적을 제거하고 총알도 제거한다.

 

Unreal Engine - BP_Bullet On Component Hit

 

추가로 총알이 충돌하지 않으면 계속 남아 있게 되는데 일정시간 지나면 알아서 제거되도록 한다.

 

Unreal Engine - BP_Bullet Event BeginPlay

 

총알은 생성되고 3초 뒤에 사라지게 된다.

 

Unreal Engine - Test Play

 

기초적인 게임 플레이는 구현이 된듯하다.

728x90
반응형

적을 생성하고 총알과 상호작용할 수 있도록 만들어 본다.

적은 Character를 상속하여 구현하고 리소스는 언리얼엔진의 다른 샘플에서 가져다 쓰기로 한다.

 

플레이어를 만들 때처럼 콜라이더 크기와 메시의 위치를 조절하고 정면을 방향을 맞춘다.


그리고 BP_Enemy는 플레이어와 동일한 스켈레탈 메시를 사용하고 구분하기 위해서 머티리얼은 복사하여 새로 생성하고 색만 바꾸어서 적용했다.

 

애니메이션은 언리얼에서 제공하는 3인칭 시점 게임 샘플에서 Idle과  RunForward만 가져다 사용했다.


가지고 온 애니메이션을 적용하니 메시가 이상하게 변형되었다. 아마도 리깅이 달라서 생긴 문제로 보인다. 

 

Unreal Engine - Enemy Mesh

 

이 상태로 작업해도 크게 문제는 없을 것 같아 그대로 진행한다.

 

애니메이션 에셋을 더블클릭하여 에셋의 디테일창을 연다. Idle과 Run 모두 Loop는 체크해 준다.

 

RunForward의 경우 애니메이션이 위치를 수정하는 게 포함되어 있어서 Root Motion > Force Root Lock을 체크해 준다.

 

Unreal Engine - Root Motion

 

보통 Enable Root Motion이 체크된 경우 애니메이션 동작이 위치 변형이 반영되는데 이 애니메이션은 RootMotion은 비활성화되어 있어도 위치가 변형되어 Force Root Lock을 체크했더니 해결되었다.

 

BP_EnemyAnimation

플레이어와 마찬가지로 Speed 변수를 만들어서 에너미의 Velocity의 크기를 가져다 상태를 전환하는 값으로 사용했다.

 

BP_Enemy의 Anim Class에 BP_EnemyAnimation을 적용시킨다.

Unreal Engine - Anim Graph
Unreal Engine - Anim State
Unreal Engine - Run to Idle
Unreal Engine - Idle to Run

 

적의 인공지능

적은 플레이어를 추적해서 공격하는 기능이 필요한데 우선 플레이어를 타깃으로 추적하는 부분까지만 구현하기로 한다.

 

AIController 블루프린트를 상속한 BP_EnemyAIController를 생성한다.

 

먼저 BP_Player 타입의 Target 변수를 만들고 시작할 때 GetPlayerCharacter를 캐스트 해서 변수를 저장한다.

 

Unreal Engine - BP_EnemyAIController > BeginePlay

 

타겟을 향해 이동하는 함수는 Move to Location을 사용한다.

 

타겟을 향해 이동할 때 타겟의 방향으로 회전하면서 이동하기 때문에 따로 회전하는 기능은 추가할 필요가 없다.

 

Unreal Engine - Event Tick > MovetoLocation

 

저장해 놓은 Target에서 위치를 가져와서 Move to Location의 Dest, 목표로 설정해 준다.

 

이렇게 만들어 놓은 AI 컨트롤러는 BP_Enemy > Details > Pawn > AI Controller Class에 할당한다.

 

적 스폰

이제 만들어 놓은 Enemy를 스폰해 본다.

 

스폰을 어디서 처리할지가 애매했는데 일단 게임의 전반적인 기능을 담당하는 BP_MyGameMode에서 스폰 기능을 구현하기로 한다.

 

BP_MyGameMode에 Spawn Enemy 함수를 추가한다.

 

적은 플레이어 위치를 기준으로 일정한 범위 내에서 랜덤 한 위치에 생성되도록 한다.

 

먼저 플레이어 위치 벡터를 가지고 와서 일정 범위 안에서 랜덤 한 값만큼을 오프셋으로 사용해서 생성될 위치를 정한다.

 

Unreal Engine - BP_MyGameMode > Spawn Enemy Func

 

Random Float in Range를 사용해서 Min ~ Max 범위 안의 값을 오프셋으로 사용한다.

 

Spawn Actor BP Enemy의 Spawn Transform에 생성한 무작위 위치를 할당하고 class는 BP_Enemy로 설정해 준다.

 

이제 이 함수를 호출해서 적을 생성하면 된다.

 

방식은 일정 시간 간격으로 생성되도록 할 것이다.

 

BP_MyGameMode의 Event Begin Play에서 Set Timer by Event를 호출하고 Custom Event인 EnemySpawnEvent를 만든다. EnemySpawnEvent는 SpawnEnemy를 호출하는 기능만 하며 Set Timer by Event의 Event 파라미터에 등록한다.

 

Time에 적을 생성할 주기를 정해주는데 일단 3초 간격으로 적이 생성되도록 한다.

 

그리고 3초마다 계속 생성되도록 Looping은 True로 체크해 준다.

 

Unreal Engine - BP_MyGameMode > EventGraph

 

여기까지 진행하고 테스트를 해보는데 적이 플레이어 주변 랜덤한 위치에 생성은 되지만 움직이지 않는다.

 

Auto Possess AI

BP_Enemy의 디테일 창으로 다시 돌아가서 Pawn 탭의 Auto Possess AI 옵션을 확인한다.

 

Unreal Engine - Auto Possess AI

 

해당 옵션은 기본 값으로 Placed in World로 되어있는데 이 옵션은 미리 맵에 생성된 액터에게만 해당하며 플레이 도중에 생성된 액터는 동작하지 않는다.

 

각 옵션의 사용법을 알아둔다.

 

Disabled

AI 컨트롤러가 캐릭터를 자동으로 제어하지 않는다. AI 컨트롤러를 수동으로 할당해한다.

 

Placed in World

맵에 배치된 AI 캐릭터가 게임 시작 시 AI 컨트롤러에 의해 자동으로 제어된다. 주로 에디터에서 맵에 직접 배치된 AI 캐릭터에 사용된다.

 

Spawned

게임 도중 스폰된 AI 캐릭터가 AI 컨트롤러에 의해 자동으로 제어된다. 런타임 중에 생성된 AI 캐릭터에 사용된다.

 

Placed in World or Spawned

맵에 배치된 AI 캐릭터와 게임 도중 스폰된 AI 캐릭터 모두 AI 컨트롤러에 의해 자동으로 제어된다. 모든 상황에서 AI 캐릭터를 자동으로 제어할 때 사용한다.

 

이 옵션을 적의 배치 방식에 맞는 Spawned로 설정한다.

 

이제 다시 플레이해 보면 여전히 동작하지 않는다.

 

NavMeshBoundsVolume

만들어놓은 지형에 AI가 돌아다닐 수 있는 범위를 정해주어야 한다.

 

해당 범위만큼 내비게이션의 길 찾기 연산에 포함되기 때문에 복잡하고 넓을수록 많은 연산을 차지하게 된다.

 

Place Actor에서 NavMeshBoundsVolume을 찾아서 레벨에 생성한다. 그리고 AI가 돌아다닐 수 있는 범위만큼 크기와 높이를 조절한다.

 

범위가 원하는 대로 지정됐는지 확인하려면 플레이하지 않고 뷰포트에서 키보드 'P'를 누르면 지정된 범위만큼 녹색으로 표시가 된다.

Unreal Engine - NavMeshBoundsVolume

 

이제 진짜로 테스트 플레이를 해본다.

 

Unreal Engine - Test Play

 

랜덤 한 위치에서 적이 생성되고 생성된 적은 플레이어를 추적하기 시작한다.

 

추적하는 동안 이동하는 애니메이션이 재생되고 목적지에 도착하면 일반 상태의 애니메이션이 재생된다.

 

플레이어가 이동하면 변경된 위치를 계속해서 따라가게 된다. 여기까지 원하는 대로 잘 동작하게 되었다.

 

 

728x90
반응형

+ Recent posts