Skip to content
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ repositories {

dependencies {
implementation 'org.springframework.boot:spring-boot-starter'

// basic spring web package dependency
implementation 'org.springframework.boot:spring-boot-starter-web'
// dependency for dynamic webpage
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.softgallery.profilegameserverdemo.controller;

import com.softgallery.profilegameserverdemo.service.GithubAPIService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RestController는 어떤 역할을 하는 코드인가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spring에게 해당 클래스가 Rest API를 처리하는 Controller의 역할을 한다고 알려주는 Annotation 입니다. 이걸 붙이면 자동으로 Spring Bean으로 등록됩니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spring Bean이 뭔가요? 재사용 가능한 소프트웨어 컴포넌트라고 나오는데, 해당 컨트롤러가 Spring Bean으로 등록이되면 어떤 이점이 있나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bean은 스프링에서 직접 관리해주는 객체라고 보시면 됩니다. 자동 의존관계 주입(Dependency Injection, DI)이라는 스프링의 핵심 기능을 사용하기 위해 등록하여야 하며, 등록이 되게 되면 한 종류의 객체는 전체 프로그램 내에서 하나만 생성되어 (Singleton) 재사용이 가능합니다. 여기서는 GithubAPIController 자체가 하나의 Bean이므로 프로그램 내에 다른 클래스에서 새로운 GithubAPIController 필드를 선언해도 처음에 생성된 것과 똑같은 Controller 객체가 필드에 주입되게 됩니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

자동 의존관계 주입이 뭔지 혹시 설명해주실 수 있을까요? 해당 용어를 제외하고는, 어떤 역할을 하는지 이해는 갑니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제대로 설명하기엔 어려운 개념이라 간단하게만 설명하자면, A클래스에서 B 타입의 필드를 생성할 때 선언을 한 뒤 new 키워드를 이용하여 B 객체를 할당을 받게 되면 A는 B에 의존한다고 표현합니다. A클래스는 B 객체 필드 없이는 동작하지 못하기 때문입니다. 여기서 자동으로 의존관계를 주입한다는 것은 객체의 생성자를 이용하여 스프링이 객체 내 필드를 자동으로 생성시켜준다는 것을 의미합니다. Int a = new Int()와 같은 형식으로 직접 할당해 줄 필요가 없습니다.

public class GithubAPIController {
private final GithubAPIService githubAPIService = new GithubAPIService();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 final 키워드를 쓴 이유가 뭔지 알려주실 수 있나요? 또한, GithubAPIService는 어떤 객체인지 궁금합니다!

Copy link
Contributor Author

@SHKim55 SHKim55 Mar 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이름에서 알 수 있듯이 Github API를 활용할 수 있게 만들어주는 서비스를 담당하는 객체 필드를 의미합니다. 실제로 API에서 데이터를 불러오는 로직은 Controller가 아닌 Service에서 처리합니다. 해당 객체는 이후에 다른 객체로 변환될 일 없이 쭉 불변이므로 final access modifier를 사용했습니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하 이해했습니다! 객체 필드에 대한 로직은 모두 Service에서 처리하기 때문이군요! 좋은 설명 감사합니다!


@GetMapping("/commit/{name}")
public String getCommitInfoByName(@PathVariable("name") String name) {
System.out.println(name);
String repo = "spring-roomescape-playground"; // repo 이름을 수정하여 넣을 것

return githubAPIService.getCommits(name, repo);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.softgallery.profilegameserverdemo.controller;

import com.softgallery.profilegameserverdemo.domain.Profile;
import com.softgallery.profilegameserverdemo.service.ProfileService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@Controller
public class MainController {
private final ProfileService profileService = new ProfileService();

@GetMapping("/")
public String test() { return "test"; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String이라는 타입의 변수명을 test()로 지정할 수 있나요? 저 이 코드가 잘 이해가 안갑니다. String인데 함수형으로 오른쪽에 test() { return "test"; }이런식으로 선언되어있어요. public String test = "test";랑 뭐가 다른거죠 😢

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그리고 이 코드는 왜 있는건가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(저도 저 코드 이해하는데 참~ 오래 걸렸는데요~~)
괄호 안의 URL이 "/" 하나만으로 이루어진 것에서 알 수 있듯이, 이 코드는 URL에 다른 값들이 붙지 않아 서비스의 가장 기본이 되는 페이지(home 등)를 불러올 수 있는 코드입니다. 이 함수에서 "test"라는 String 값을 반환하게 되면 Spring 자체에서 "test"라는 이름의 HTML 파일을 프로젝트 내에서 찾아 서버로 띄워주고 GET 요청을 마무리합니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이야 그러한 기능이라면, 서비스 단계에서는 test라는 명칭 말고, 다른 명칭으로 많이 구현이 될 것 같은데 제 추측이 맞을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실제 서비스를 해보지 않아서 잘은 모르겠지만, 가장 기본이자 메인이 되는 페이지이기 때문에 home, main, 서비스 이름 등으로 명칭을 정하는 것으로 알고 있습니다.


@GetMapping("/{name}")
@ResponseBody
public Profile searchProfile(@PathVariable("name") String name) {
System.out.println(name);

return profileService.getProfile(name);
}

@GetMapping("/profile")
@ResponseBody
public Profile printProfile() {
return new Profile(0L, "Jaehoon", "00:01:02", "2024-01-01");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.softgallery.profilegameserverdemo.domain;

import java.time.LocalDate;
import java.time.LocalTime;

public class Profile {
private final Long id;
private final String name;
private final LocalTime recentTime;
private final LocalDate recentDate;

public Profile(final Long id, final String name, final String time, final String date) {
this.id = id;
this.name = name;
this.recentTime = LocalTime.parse(time);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LocalTime.parse(time)을 하면, recentTime은 어떻게 초기화 되나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HH:mm:ss 형식으로 들어온 String 데이터를 시간 처리 객체인 LocalTime 객체로 변환합니다.

this.recentDate = LocalDate.parse(date);
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public LocalTime getRecentTime() {
return recentTime;
}

public LocalDate getRecentDate() {
return recentDate;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.softgallery.profilegameserverdemo.service;

import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class GithubAPIService {
private final String GITHUB_API_URL = "https://api.github.com";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기본 baseURL이 되는 코드인가보네요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞습니다!


private final RestTemplate restTemplate = new RestTemplate();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RestTemplate()는 어떤 객체인지 설명해주실 수 있나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REST API에서 보내주는 값을 저장하기 위해 사용해야되는 템플릿입니다. 하단의 코드를 보시면 restTemplate.queryForObject() 메서드로 데이터를 Object 형태로 받아오는 것을 알 수 있습니다.


public GithubAPIService() { }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이후에는 어떤 기능이 해당 함수에 추가될 가능성이 있나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클래스 생성자라 특정한 기능을 하는 함수가 추가되지는 않겠지만, 스프링이 RestTemplate 필드에 의존성을 추가하는 코드가 들어갈 예정입니다.


public String getCommits(String owner, String repo) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commit을 가져오는 함수인걸까요? 뭔가 getUrlString이런 함수명은 url의 string값을 가져온다... 이런식으로 이해가 되는데 커밋을 가져온다는건 뭔지 모르겠어서 어떤 기능을 하는지 잘 이해가 가지 않습니다! 설명해주실 수 있나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(함수명은 GPT가 짰어요...)
REST API를 통해 owner가 가지고 있는 특정 repo의 commit 정보를 모두 가져옵니다. commit 정보에는 일시, 사용자, 커밋 메시지 등 다양한 내용이 포함되어 있습니다. 실제 형식은 시연 때 보여드리겠습니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다~

String url = GITHUB_API_URL + "/repos/" + owner + "/" + repo + "/commits";
return restTemplate.getForObject(url, String.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.softgallery.profilegameserverdemo.service;

import com.softgallery.profilegameserverdemo.domain.Profile;
import java.util.ArrayList;

public class ProfileService {
private final ArrayList<Profile> profiles = new ArrayList<>();

public ProfileService() {
initialize();
}

// Initialization with test-cases
private void initialize() {
profiles.add(new Profile(1L, "Jaehoon", "00:01:02", "2024-01-01"));
profiles.add(new Profile(2L, "SHKim55", "23:00:00", "2024-12-23"));
profiles.add(new Profile(3L, "Sumin", "22:00:00", "2023-12-23"));
profiles.add(new Profile(4L, "YongWoo", "21:00:00", "2023-11-23"));
profiles.add(new Profile(5L, "Hangyul", "20:00:00", "2023-10-23"));
}

public ArrayList<Profile> getAllProfiles() {
return profiles;
}

// Find profile by name
public Profile getProfile(String name) {
for(Profile profile : profiles) {
if (profile.getName().equals(name))
return profile;
}
return new Profile(0L, "No Such User", "00:00:00", "0000-00-00");
}
}
10 changes: 10 additions & 0 deletions src/main/resources/templates/test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Test Page</title>
</head>
<body>
Testing
</body>
</html>