人類はジョギングしながらRestAPIのアプリが開発できるのか？

# この記事はなに？
ChatGPT の「**Advanced Voice**」（高度音声機能）が話題となっていますね。
これが僕のアカウントでも使えるようになったので早速試してみようと思いました。
しかし、少々寝坊してしまったので、**朝のトレーニング（ジョギング）しながら使ってみました。**

:::note info
なぜわざわざジョギングしながら？という疑問があると思いますが、ジョギング中って色々と思いつくんですよね。
そういう時、ボイスメモ代わりにChatGPTが使えるのではないか？という実験も兼ねています。
:::

# 題材とフレームワーク

従業員が部署に多対多で所属するというケースを、JavaのSpringBootフレームワークで作ってもらいました。

# 会話の流れ（抜粋）

## 出だし
まずは全体的なやりたいことを伝え、Entity層から作ってもらいます。
出だしの会話はまずまず順調です。ちなみに走りながらしゃべるのが案外しんどかったです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130262/c18590cf-d6fe-31ad-a2b6-c17547e01b09.png)

## Entityの要件を伝える
要件をちょっと伝えると、GPTがエンティティ層について説明をしてくれます。
但し、これを聞く必要がなさそうなので、強引に割り込んで会話を進めます。

途中で、人間（僕）が「Les éléctions, c'est quoi ?」というフランス語？を話していますが、これは、当然誤変換です…笑
![entity.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130262/52073ea5-a4e3-9f11-429d-cbaf5be9e96a.png)


## 何とかEntityに関する会話が成立したので、RepositoryとServiceに移ります。

少々うまく変換されていませんが、何とか伝わっているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130262/232592c2-6d7c-e9ce-e117-d9d000ac25f0.png)

# 最終成果物

## ドキュメント
プロジェクト構造やアーキテクチャに関するドキュメントを依頼したのでそのアウトプット

RestControllerを作ってもらうところが、うまく伝わらず、コマンドラインアプリケーションになっているので、ここは後で修正が必要。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130262/926e979d-7830-fd43-8a87-abc7992e0208.png)

## ソースコード

<details><summary>コードは量が多いので折りたたみにしました。</summary>

### Entity

- Employee.java
```java:Emplyee.java
import javax.persistence.*;
import java.util.Set;

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToMany
    @JoinTable(
        name = "employee_department",
        joinColumns = @JoinColumn(name = "employee_id"),
        inverseJoinColumns = @JoinColumn(name = "department_id")
    )
    private Set<Department> departments;

    // コンストラクター、ゲッター、セッター
}
```
- Department.java
```java:Deparment.java
import javax.persistence.*;
import java.util.Set;

@Entity
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToMany(mappedBy = "departments")
    private Set<Employee> employees;

    // コンストラクター、ゲッター、セッター
}

```

### Repository
- EmplyeeRepository.java
```java:EmplyeeRepository.java
import org.springframework.data.repository.CrudRepository;

public interface EmployeeRepository extends CrudRepository<Employee, Long> {
}

```

-  DepartmentRepository.java

```java:DepartmentRepository.java
import org.springframework.data.repository.CrudRepository;

public interface DepartmentRepository extends CrudRepository<Department, Long> {
}

```

### Service

- EmployeeService.java

```java:EmployeeService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class EmployeeService {
    @Autowired
    private EmployeeRepository employeeRepository;

    public Employee createEmployee(Employee employee) {
        return employeeRepository.save(employee);
    }

    public Optional<Employee> getEmployeeById(Long id) {
        return employeeRepository.findById(id);
    }

    public Iterable<Employee> getAllEmployees() {
        return employeeRepository.findAll();
    }

    public Employee updateEmployee(Employee employee) {
        return employeeRepository.save(employee);
    }

    public void deleteEmployeeById(Long id) {
        employeeRepository.deleteById(id);
    }
}

```

- DepartmenService.java
```java:DepartmentService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class DepartmentService {
    @Autowired
    private DepartmentRepository departmentRepository;

    public Department createDepartment(Department department) {
        return departmentRepository.save(department);
    }

    public Optional<Department> getDepartmentById(Long id) {
        return departmentRepository.findById(id);
    }

    public Iterable<Department> getAllDepartments() {
        return departmentRepository.findAll();
    }

    public Department updateDepartment(Department department) {
        return departmentRepository.save(department);
    }

    public void deleteDepartmentById(Long id) {
        departmentRepository.deleteById(id);
    }
}

```

### RestController

- EmployeeController.java

```java:EmployeeController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Optional;

@RestController
@RequestMapping("/employees")
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;

    @PostMapping
    public ResponseEntity<Employee> createEmployee(@RequestBody Employee employee) {
        Employee createdEmployee = employeeService.createEmployee(employee);
        return ResponseEntity.ok(createdEmployee);
    }

    @GetMapping("/{id}")
    public ResponseEntity<Employee> getEmployeeById(@PathVariable Long id) {
        Optional<Employee> employee = employeeService.getEmployeeById(id);
        return employee.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
    }

    @GetMapping
    public ResponseEntity<Iterable<Employee>> getAllEmployees() {
        Iterable<Employee> employees = employeeService.getAllEmployees();
        return ResponseEntity.ok(employees);
    }

    @PutMapping("/{id}")
    public ResponseEntity<Employee> updateEmployee(@PathVariable Long id, @RequestBody Employee employee) {
        Optional<Employee> existingEmployee = employeeService.getEmployeeById(id);
        if (existingEmployee.isPresent()) {
            employee.setId(id);  // Ensure the ID is correct
            Employee updatedEmployee = employeeService.updateEmployee(employee);
            return ResponseEntity.ok(updatedEmployee);
        }
        return ResponseEntity.notFound().build();
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteEmployee(@PathVariable Long id) {
        Optional<Employee> existingEmployee = employeeService.getEmployeeById(id);
        if (existingEmployee.isPresent()) {
            employeeService.deleteEmployeeById(id);
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.notFound().build();
    }
}
```

- DepartmentController.java

```java:DepartmentController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Optional;

@RestController
@RequestMapping("/departments")
public class DepartmentController {

    @Autowired
    private DepartmentService departmentService;

    @PostMapping
    public ResponseEntity<Department> createDepartment(@RequestBody Department department) {
        Department createdDepartment = departmentService.createDepartment(department);
        return ResponseEntity.ok(createdDepartment);
    }

    @GetMapping("/{id}")
    public ResponseEntity<Department> getDepartmentById(@PathVariable Long id) {
        Optional<Department> department = departmentService.getDepartmentById(id);
        return department.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
    }

    @GetMapping
    public ResponseEntity<Iterable<Department>> getAllDepartments() {
        Iterable<Department> departments = departmentService.getAllDepartments();
        return ResponseEntity.ok(departments);
    }

    @PutMapping("/{id}")
    public ResponseEntity<Department> updateDepartment(@PathVariable Long id, @RequestBody Department department) {
        Optional<Department> existingDepartment = departmentService.getDepartmentById(id);
        if (existingDepartment.isPresent()) {
            department.setId(id);  // Ensure the ID is correct
            Department updatedDepartment = departmentService.updateDepartment(department);
            return ResponseEntity.ok(updatedDepartment);
        }
        return ResponseEntity.notFound().build();
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteDepartment(@PathVariable Long id) {
        Optional<Department> existingDepartment = departmentService.getDepartmentById(id);
        if (existingDepartment.isPresent()) {
            departmentService.deleteDepartmentById(id);
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.notFound().build();
    }
}

```
</details>

# 感想

わざわざ走りながらChatGPT使わんでもいいだろ？という感じがしましたが、やってみると意外と楽しかったです。
はっきり言うと**コーディングタスクは走りながらやるべきではない**と思います。（生成されたコードを読まれても困るので、依頼タスクが終わったら教えてもらう形にしたので、あとで帰宅後にPCで会話履歴からコードは確認しています。）

しかしながら、野外でChatGPTを使うというのは、
例えば、「登山で天気予報及び雲の様子の画像から、天候の今後を予測してアドバイスもらう。（何なら山岳保険にその場で申し込んだりする。登山計画のアップデートする。）」といった野外アクティビティーとの連動。
など活用のフィールドが結構あるのではないか？と実感しました。

# おまけ
今日のランニングのスタッツは、距離2㎞、ペースは7分弱／km といったところでした。