add user agent parser and update build configuration
This commit is contained in:
357
docs/jgit.md
Normal file
357
docs/jgit.md
Normal file
@@ -0,0 +1,357 @@
|
||||
자바의 JGit 라이브러리에 대해 설명하자면, JGit은 Git 버전 관리 시스템을 자바 애플리케이션에서 사용할 수 있도록 해주는 순수 자바로 작성된 오픈소스 라이브러리입니다. 이 라이브러리는 Eclipse 재단에서 개발되었으며, Git의 기능을 프로그래밍 방식으로 활용할 수 있게 설계되었습니다. JGit을 사용하면 별도의 Git 클라이언트 설치 없이 자바 코드로 Git 저장소를 생성, 조작, 관리할 수 있습니다.
|
||||
|
||||
### 주요 특징
|
||||
1. **순수 자바 구현**: JGit은 네이티브 Git 명령어에 의존하지 않고 자바로 작성되었기 때문에 플랫폼 독립적입니다. Windows, Linux, macOS 등 어디서나 동일하게 동작합니다.
|
||||
2. **Git 기능 지원**: JGit은 Git의 주요 기능을 대부분 지원합니다. 예를 들어, 저장소 초기화, 커밋, 브랜치 관리, 머지, 푸시/풀, 로그 조회 등이 가능합니다.
|
||||
3. **임베디드 사용**: 외부 Git 설치 없이 애플리케이션에 임베드하여 사용할 수 있어, Git 기반 도구나 워크플로우를 자바로 쉽게 통합할 수 있습니다.
|
||||
4. **Eclipse와의 통합**: JGit은 Eclipse IDE의 Git 플러그인(EGit)의 핵심 구성 요소로 사용되며, IDE 내에서 Git 작업을 지원합니다.
|
||||
|
||||
### 주요 기능 예시
|
||||
JGit을 사용하면 다음과 같은 작업을 자바 코드로 수행할 수 있습니다:
|
||||
- **저장소 초기화**:
|
||||
```java
|
||||
Git.init().setDirectory(new File("/path/to/repo")).call();
|
||||
```
|
||||
- **파일 추가 및 커밋**:
|
||||
```java
|
||||
Git git = Git.open(new File("/path/to/repo/.git"));
|
||||
git.add().addFilepattern("file.txt").call();
|
||||
git.commit().setMessage("Initial commit").call();
|
||||
```
|
||||
- **브랜치 생성**:
|
||||
```java
|
||||
git.branchCreate().setName("feature-branch").call();
|
||||
```
|
||||
- **원격 저장소에서 풀**:
|
||||
```java
|
||||
git.pull().call();
|
||||
```
|
||||
|
||||
### 사용 사례
|
||||
- **자동화 도구**: CI/CD 파이프라인에서 Git 작업을 스크립트로 자동화.
|
||||
- **커스텀 Git 클라이언트**: GUI나 CLI 기반의 자체 Git 도구 개발.
|
||||
- **프로젝트 관리**: 소스 코드 관리나 버전 관리 기능을 애플리케이션에 통합.
|
||||
|
||||
### 장단점
|
||||
#### 장점
|
||||
- 설치 필요 없이 자바 환경에서 바로 사용 가능.
|
||||
- Git 명령어를 호출하는 대신 API로 세밀한 제어 가능.
|
||||
- 크로스 플랫폼 호환성.
|
||||
|
||||
#### 단점
|
||||
- 네이티브 Git에 비해 일부 고급 기능이 제한적이거나 느릴 수 있음.
|
||||
- 대규모 저장소에서 성능 최적화가 필요할 수 있음.
|
||||
|
||||
### 의존성 추가 (Maven 예시)
|
||||
JGit을 프로젝트에 추가하려면 Maven을 사용하는 경우 `pom.xml`에 다음 의존성을 추가하면 됩니다:
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>6.8.0.202311291450-r</version> <!-- 최신 버전 확인 필요 -->
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 참고 자료
|
||||
- 공식 사이트: [JGit GitHub](https://github.com/eclipse/jgit)
|
||||
- 문서: [JGit User Guide](https://www.eclipse.org/jgit/documentation/)
|
||||
|
||||
추가로 궁금한 점이 있거나 특정 기능에 대해 더 알고 싶다면 말씀해주세요!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
서버의 컨트롤러에서 Git 저장소를 제공한다는 것은 웹 애플리케이션의 백엔드에서 Git 저장소를 생성하고, 클라이언트가 이를 HTTP(S)를 통해 접근하거나 다운로드할 수 있도록 하는 것을 의미합니다. 여기서는 Spring Boot 기반 서버에서 JGit을 활용해 Git 저장소를 제공하는 방법을 단계별로 설명하겠습니다. 클라이언트가 저장소를 클론(clone)하거나 파일을 다운로드할 수 있는 시나리오를 중심으로 구성합니다.
|
||||
|
||||
---
|
||||
|
||||
### 목표
|
||||
- 서버에서 Git 저장소를 동적으로 생성하거나 관리.
|
||||
- HTTP 엔드포인트를 통해 저장소에 접근하거나 `.git` 디렉토리를 제공.
|
||||
- JGit을 사용해 저장소 초기화 및 기본 파일 추가.
|
||||
|
||||
---
|
||||
|
||||
### 1. **프로젝트 설정**
|
||||
Spring Boot와 JGit을 사용해 기본 환경을 설정합니다.
|
||||
|
||||
#### (1) 의존성 추가 (Maven)
|
||||
`pom.xml`에 필요한 의존성을 추가:
|
||||
```xml
|
||||
<dependencies>
|
||||
<!-- Spring Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!-- Thymeleaf (선택적, UI용) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<!-- JGit -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>6.8.0.202311291450-r</version>
|
||||
</dependency>
|
||||
<!-- Lombok (선택적) -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
#### (2) 프로젝트 구조
|
||||
```
|
||||
src/
|
||||
├── main/
|
||||
│ ├── java/
|
||||
│ │ └── com/
|
||||
│ │ └── example/
|
||||
│ │ └── gitserver/
|
||||
│ │ ├── controller/ # 컨트롤러
|
||||
│ │ ├── service/ # Git 로직
|
||||
│ │ └── GitServerApplication.java
|
||||
│ └── resources/
|
||||
│ └── application.properties # 설정
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. **Git 서비스 구현**
|
||||
`GitService` 클래스에서 저장소를 생성하고 관리하는 로직을 작성합니다.
|
||||
|
||||
```java
|
||||
package com.example.gitserver.service;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
@Service
|
||||
public class GitService {
|
||||
|
||||
// 저장소 생성 및 초기화
|
||||
public File createRepository(String repoName) throws GitAPIException, IOException {
|
||||
File repoDir = new File("./repos/" + repoName); // 서버 내 저장소 디렉토리
|
||||
if (!repoDir.exists()) {
|
||||
repoDir.mkdirs();
|
||||
}
|
||||
|
||||
try (Git git = Git.init().setDirectory(repoDir).call()) {
|
||||
// 초기 파일 추가 (선택적)
|
||||
File readme = new File(repoDir, "README.md");
|
||||
Files.write(readme.toPath(), "# Sample Repository".getBytes());
|
||||
git.add().addFilepattern("README.md").call();
|
||||
git.commit().setMessage("Initial commit").call();
|
||||
}
|
||||
return repoDir;
|
||||
}
|
||||
|
||||
// 저장소 디렉토리 반환
|
||||
public File getRepository(String repoName) {
|
||||
return new File("./repos/" + repoName);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. **컨트롤러에서 Git 저장소 제공**
|
||||
`GitController`에서 HTTP 엔드포인트를 통해 저장소를 제공합니다. 두 가지 방식으로 접근할 수 있습니다:
|
||||
1. **저장소 전체 다운로드**: `.git` 디렉토리를 ZIP 파일로 제공.
|
||||
2. **Git 프로토콜 지원**: HTTP를 통해 `git clone` 가능하도록 설정 (추가 설정 필요).
|
||||
|
||||
#### (1) 저장소 생성 및 ZIP 다운로드 방식
|
||||
```java
|
||||
package com.example.gitserver.controller;
|
||||
|
||||
import com.example.gitserver.service.GitService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/git")
|
||||
public class GitController {
|
||||
|
||||
@Autowired
|
||||
private GitService gitService;
|
||||
|
||||
// 저장소 생성
|
||||
@PostMapping("/create/{repoName}")
|
||||
public ResponseEntity<String> createRepository(@PathVariable String repoName) {
|
||||
try {
|
||||
gitService.createRepository(repoName);
|
||||
return ResponseEntity.ok("Repository '" + repoName + "' created successfully");
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(500).body("Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 저장소 다운로드 (ZIP 형식)
|
||||
@GetMapping("/download/{repoName}")
|
||||
public ResponseEntity<Resource> downloadRepository(@PathVariable String repoName) throws Exception {
|
||||
File repoDir = gitService.getRepository(repoName);
|
||||
if (!repoDir.exists()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
// .git 디렉토리만 압축
|
||||
File gitDir = new File(repoDir, ".git");
|
||||
Path zipPath = Files.createTempFile(repoName, ".zip");
|
||||
try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipPath))) {
|
||||
zipDirectory(gitDir, gitDir.getName(), zos);
|
||||
}
|
||||
|
||||
Resource resource = new FileSystemResource(zipPath.toFile());
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + repoName + ".zip")
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.body(resource);
|
||||
}
|
||||
|
||||
// 디렉토리를 ZIP으로 압축하는 헬퍼 메서드
|
||||
private void zipDirectory(File folder, String parentFolder, ZipOutputStream zos) throws IOException {
|
||||
for (File file : folder.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
zipDirectory(file, parentFolder + "/" + file.getName(), zos);
|
||||
continue;
|
||||
}
|
||||
ZipEntry ze = new ZipEntry(parentFolder + "/" + file.getName());
|
||||
zos.putNextEntry(ze);
|
||||
Files.copy(file.toPath(), zos);
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### (2) 실행 및 테스트
|
||||
- 애플리케이션 실행 후:
|
||||
- `POST /git/create/myrepo`: 저장소 생성.
|
||||
- `GET /git/download/myrepo`: `.git` 디렉토리를 ZIP으로 다운로드.
|
||||
- 클라이언트는 다운로드한 ZIP을 풀고 `git clone --bare`로 로컬 저장소로 변환 가능.
|
||||
|
||||
---
|
||||
|
||||
### 4. **HTTP를 통한 Git 프로토콜 제공 (고급)**
|
||||
ZIP 다운로드 대신 클라이언트가 `git clone https://your-server/git/myrepo`로 직접 클론하도록 하려면 Git HTTP 백엔드를 구현해야 합니다. JGit은 기본적으로 Git 서버 프로토콜을 제공하지 않으므로 추가 설정이 필요합니다.
|
||||
|
||||
#### (1) JGit Servlet 사용
|
||||
JGit은 `GitServlet`을 제공해 HTTP 기반 Git 프로토콜을 지원합니다.
|
||||
|
||||
- **의존성 추가**: JGit HTTP 서버 의존성 추가.
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.http.server</artifactId>
|
||||
<version>6.8.0.202311291450-r</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
- **Servlet 설정**:
|
||||
```java
|
||||
package com.example.gitserver.config;
|
||||
|
||||
import org.eclipse.jgit.http.server.GitServlet;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import java.io.File;
|
||||
|
||||
@Configuration
|
||||
public class GitServletConfig {
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean<Servlet> gitServlet() {
|
||||
GitServlet gitServlet = new GitServlet();
|
||||
gitServlet.setRepositoryResolver((req, name) -> {
|
||||
File repoDir = new File("./repos/" + name + "/.git");
|
||||
if (!repoDir.exists()) {
|
||||
throw new IllegalStateException("Repository not found: " + name);
|
||||
}
|
||||
return new org.eclipse.jgit.internal.storage.file.FileRepository(repoDir);
|
||||
});
|
||||
|
||||
ServletRegistrationBean<Servlet> bean = new ServletRegistrationBean<>(gitServlet, "/git/*");
|
||||
bean.setLoadOnStartup(1);
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **저장소 준비**: 저장소는 **bare repository** 형식이어야 합니다.
|
||||
```java
|
||||
public File createBareRepository(String repoName) throws GitAPIException {
|
||||
File repoDir = new File("./repos/" + repoName);
|
||||
if (!repoDir.exists()) {
|
||||
repoDir.mkdirs();
|
||||
}
|
||||
try (Git git = Git.init().setDirectory(repoDir).setBare(true).call()) {
|
||||
System.out.println("Bare repository created: " + repoName);
|
||||
}
|
||||
return repoDir;
|
||||
}
|
||||
```
|
||||
|
||||
- **컨트롤러 수정**:
|
||||
```java
|
||||
@PostMapping("/create-bare/{repoName}")
|
||||
public ResponseEntity<String> createBareRepository(@PathVariable String repoName) {
|
||||
try {
|
||||
gitService.createBareRepository(repoName);
|
||||
return ResponseEntity.ok("Bare repository '" + repoName + "' created successfully");
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(500).body("Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### (2) 클라이언트에서 클론
|
||||
- 서버가 `http://localhost:8080`에서 실행 중이라면:
|
||||
```bash
|
||||
git clone http://localhost:8080/git/myrepo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. **추가 고려사항**
|
||||
- **보안**: 인증(예: Spring Security)을 추가해 저장소 접근을 제어.
|
||||
- **저장소 경로**: `./repos/`는 서버의 파일 시스템에 저장되므로, 실제 배포 시 경로를 환경 변수로 관리.
|
||||
- **성능**: 대규모 저장소의 경우 JGit 대신 네이티브 Git 서버(GitLab, Gitea 등)를 고려.
|
||||
|
||||
---
|
||||
|
||||
### 결론
|
||||
- **ZIP 방식**: 간단히 `.git` 디렉토리를 제공하며, 클라이언트가 수동으로 처리.
|
||||
- **Git 프로토콜 방식**: `GitServlet`을 사용해 HTTP로 직접 클론 가능하지만 설정이 복잡.
|
||||
필요에 따라 두 방식 중 하나를 선택하거나 혼합해 사용할 수 있습니다. 추가 질문이나 특정 기능 확장이 필요하면 말씀해주세요!
|
||||
Reference in New Issue
Block a user