1. 序章

このチュートリアルでは、クライアントデータの作成、取得、更新、削除(CRUD)が可能なアプリケーションを作成する方法を学習します。 このアプリケーションは、シンプルな Spring Boot RESTful API と、 React JavaScriptライブラリで実装されたユーザーインターフェイス(UI)で構成されます。

2. スプリングブーツ

2.1. Mavenの依存関係

pom.xmlファイルにいくつかの依存関係を追加することから始めましょう。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.4.4</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
        <version>2.4.4</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>2.4.4</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.200</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

ここでは、アプリケーションにH2インメモリデータベースがあるため、Web、テスト、JPA永続化スターター、およびH2依存関係を追加しました。

2.2. モデルの作成

次に、データモデルを表すために、nameおよびemailプロパティを使用して、 Cliententityクラスを作成しましょう。

@Entity
@Table(name = "client")
public class Client {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private String email;

    // getter, setters, contructors
}

2.3. リポジトリの作成

次に、JPACRUD機能を提供するためにJpaRepositoryから拡張するClientRepositoryクラスを作成します

public interface ClientRepository extends JpaRepository<Client, Long> {
}

2.4. RESTコントローラーの作成

最後に、 ClientRepository と対話するコントローラーを作成して、RESTAPIを公開しましょう。

@RestController
@RequestMapping("/clients")
public class ClientsController {

    private final ClientRepository clientRepository;

    public ClientsController(ClientRepository clientRepository) {
        this.clientRepository = clientRepository;
    }

    @GetMapping
    public List<Client> getClients() {
        return clientRepository.findAll();
    }

    @GetMapping("/{id}")
    public Client getClient(@PathVariable Long id) {
        return clientRepository.findById(id).orElseThrow(RuntimeException::new);
    }

    @PostMapping
    public ResponseEntity createClient(@RequestBody Client client) throws URISyntaxException {
        Client savedClient = clientRepository.save(client);
        return ResponseEntity.created(new URI("/clients/" + savedClient.getId())).body(savedClient);
    }

    @PutMapping("/{id}")
    public ResponseEntity updateClient(@PathVariable Long id, @RequestBody Client client) {
        Client currentClient = clientRepository.findById(id).orElseThrow(RuntimeException::new);
        currentClient.setName(client.getName());
        currentClient.setEmail(client.getEmail());
        currentClient = clientRepository.save(client);

        return ResponseEntity.ok(currentClient);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity deleteClient(@PathVariable Long id) {
        clientRepository.deleteById(id);
        return ResponseEntity.ok().build();
    }
}

2.5. APIの開始

これで、Spring BootAPIを開始する準備が整いました。 これは、spring-boot-maven-pluginを使用して実行できます。

mvn spring-boot:run

次に、 http:// localhost:8080 / clients にアクセスして、クライアントリストを取得できます。

2.6. クライアントの作成

さらに、Postmanを使用していくつかのクライアントを作成できます。

curl -X POST http://localhost:8080/clients -d '{"name": "John Doe", "email": "[email protected]"}'

3. React

Reactは、ユーザーインターフェイスを作成するためのJavaScriptライブラリです。 Reactを使用するには、Node.jsがインストールされている必要があります。 インストール手順は、Node.jsダウンロードページにあります。

3.1. ReactUIの作成

Create React App は、がReactプロジェクトを生成するコマンドユーティリティです。 次のコマンドを実行して、Spring Bootアプリケーションベースディレクトリにフロントエンドアプリを作成しましょう。

npx create-react-app frontend

アプリの作成プロセスが完了したら、 Bootstrap React Router 、およびreactstrapfrontendディレクトリにインストールします。

npm install --save bootstrap@5.1 react-cookie@4.1.1 react-router-dom@5.3.0 reactstrap@8.10.0

BootstrapのCSSとreactstrapのコンポーネントを使用して見栄えの良いUIを作成し、ReactRouterコンポーネントを使用してアプリケーション内のナビゲーションを処理します。

BootstrapのCSSファイルをapp/ src /index.jsのインポートとして追加しましょう。

import 'bootstrap/dist/css/bootstrap.min.css';

3.2. ReactUIの開始

これで、フロントエンドアプリケーションを起動する準備が整いました。

npm start

ブラウザでhttp:// localhost:3000 にアクセスすると、Reactのサンプルページが表示されます。

 

3.3. SpringBootAPIの呼び出し

Spring Boot APIを呼び出すには、APIを呼び出すときにプロキシを構成するためにReactアプリケーションのpackage.jsonファイルを設定する必要があります。

そのために、APIのURLをpackage.jsonに含めます。

...
"proxy": "http://localhost:8080",
...

次に、 frontend / src / App.js を編集して、APIを呼び出し、nameおよびemailプロパティを持つクライアントのリストを表示します。

class App extends Component {
  state = {
    clients: []
  };

  async componentDidMount() {
    const response = await fetch('/clients');
    const body = await response.json();
    this.setState({clients: body});
  }

  render() {
    const {clients} = this.state;
    return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <div className="App-intro">
              <h2>Clients</h2>
              {clients.map(client =>
                  <div key={client.id}>
                    {client.name} ({client.email})
                  </div>
              )}
            </div>
          </header>
        </div>
    );
  }
}
export default App;

componentDidMount 関数では、クライアントAPI をフェッチし、クライアント変数に応答本文を設定します。 render 関数では、APIで見つかったクライアントのリストを含むHTMLを返します。

クライアントのページが表示されます。これは次のようになります。

注:UIがAPIを呼び出せるように、SpringBootアプリケーションが実行されていることを確認してください。

3.4. ClientListコンポーネントの作成

UIを改善して、APIを使用してクライアントを一覧表示、編集、削除、作成するためのより洗練されたコンポーネントを表示できるようになりました。 後で、このコンポーネントの使用方法と、Appコンポーネントからクライアントリストを削除する方法を説明します。

frontend / src /ClientList.jsにファイルを作成しましょう。

import React, { Component } from 'react';
import { Button, ButtonGroup, Container, Table } from 'reactstrap';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';

class ClientList extends Component {

    constructor(props) {
        super(props);
        this.state = {clients: []};
        this.remove = this.remove.bind(this);
    }

    componentDidMount() {
        fetch('/clients')
            .then(response => response.json())
            .then(data => this.setState({clients: data}));
    }
}
export default ClientList;

App.js と同様に、componentDidMount関数はAPIを呼び出してクライアントリストを読み込みます。

また、クライアントを削除するときにAPIへの DELETE 呼び出しを処理するために、remove関数を含めます。 さらに、 render 関数を作成します。この関数は、 Edit Delete 、および AddClientアクションでHTMLをレンダリングします。

async remove(id) {
    await fetch(`/clients/${id}`, {
        method: 'DELETE',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        }
    }).then(() => {
        let updatedClients = [...this.state.clients].filter(i => i.id !== id);
        this.setState({clients: updatedClients});
    });
}

render() {
    const {clients, isLoading} = this.state;

    if (isLoading) {
        return <p>Loading...</p>;
    }

    const clientList = clients.map(client => {
        return <tr key={client.id}>
            <td style={{whiteSpace: 'nowrap'}}>{client.name}</td>
            <td>{client.email}</td>
            <td>
                <ButtonGroup>
                    <Button size="sm" color="primary" tag={Link} to={"/clients/" + client.id}>Edit</Button>
                    <Button size="sm" color="danger" onClick={() => this.remove(client.id)}>Delete</Button>
                </ButtonGroup>
            </td>
        </tr>
    });

    return (
        <div>
            <AppNavbar/>
            <Container fluid>
                <div className="float-right">
                    <Button color="success" tag={Link} to="/clients/new">Add Client</Button>
                </div>
                <h3>Clients</h3>
                <Table className="mt-4">
                    <thead>
                    <tr>
                        <th width="30%">Name</th>
                        <th width="30%">Email</th>
                        <th width="40%">Actions</th>
                    </tr>
                    </thead>
                    <tbody>
                    {clientList}
                    </tbody>
                </Table>
            </Container>
        </div>
    );
}

3.5. ClientEditコンポーネントの作成

ClientEdit コンポーネントは、クライアントの作成と編集を担当します。

frontend / src /ClientEdit.jsにファイルを作成しましょう。

import React, { Component } from 'react';
import { Link, withRouter } from 'react-router-dom';
import { Button, Container, Form, FormGroup, Input, Label } from 'reactstrap';
import AppNavbar from './AppNavbar';

class ClientEdit extends Component {

    emptyItem = {
        name: '',
        email: ''
    };

    constructor(props) {
        super(props);
        this.state = {
            item: this.emptyItem
        };
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }
}
export default withRouter(ClientEdit);

componentDidMount 関数を追加して、作成機能と編集機能のどちらを扱っているかを確認しましょう。 編集の場合、APIからクライアントをフェッチします。

async componentDidMount() {
    if (this.props.match.params.id !== 'new') {
        const client = await (await fetch(`/clients/${this.props.match.params.id}`)).json();
        this.setState({item: client});
    }
}

次に、 handleChange 関数で、フォームの送信時に使用されるコンポーネント状態アイテムのプロパティを更新します。

handleChange(event) {
    const target = event.target;
    const value = target.value;
    const name = target.name;
    let item = {...this.state.item};
    item[name] = value;
    this.setState({item});
}

handeSubmit では、APIを呼び出し、呼び出している機能に応じてPUTまたはPOSTメソッドにリクエストを送信します。 そのために、idプロパティが入力されているかどうかを確認できます。

async handleSubmit(event) {
    event.preventDefault();
    const {item} = this.state;

    await fetch('/clients' + (item.id ? '/' + item.id : ''), {
        method: (item.id) ? 'PUT' : 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(item),
    });
    this.props.history.push('/clients');
}

最後になりましたが、render関数がフォームを処理します。

render() {
    const {item} = this.state;
    const title = <h2>{item.id ? 'Edit Client' : 'Add Client'}</h2>;

    return <div>
        <AppNavbar/>
        <Container>
            {title}
            <Form onSubmit={this.handleSubmit}>
                <FormGroup>
                    <Label for="name">Name</Label>
                    <Input type="text" name="name" id="name" value={item.name || ''}
                           onChange={this.handleChange} autoComplete="name"/>
                </FormGroup>
                <FormGroup>
                    <Label for="email">Email</Label>
                    <Input type="text" name="email" id="email" value={item.email || ''}
                           onChange={this.handleChange} autoComplete="email"/>
                </FormGroup>
                <FormGroup>
                    <Button color="primary" type="submit">Save</Button>{' '}
                    <Button color="secondary" tag={Link} to="/clients">Cancel</Button>
                </FormGroup>
            </Form>
        </Container>
    </div>
}

注:キャンセルボタンをクリックすると/クライアントに戻るように構成されたルートを持つリンクもあります。

3.6. AppNavbarコンポーネントの作成

アプリケーションのナビゲーション性を向上させるために、 frontend / src /AppNavbar.jsにファイルを作成しましょう。

import React, {Component} from 'react';
import {Navbar, NavbarBrand} from 'reactstrap';
import {Link} from 'react-router-dom';

export default class AppNavbar extends Component {
    constructor(props) {
        super(props);
        this.state = {isOpen: false};
        this.toggle = this.toggle.bind(this);
    }

    toggle() {
        this.setState({
            isOpen: !this.state.isOpen
        });
    }

    render() {
        return <Navbar color="dark" dark expand="md">
            <NavbarBrand tag={Link} to="/">Home</NavbarBrand>
        </Navbar>;
    }
}

レンダリング機能では、react-router-dom機能を使用してリンクを作成し、アプリケーションホームにルーティングします。ページ。

 3.7. ホームコンポーネントの作成

このコンポーネントは、アプリケーションのホームページになり、以前に作成したClientListコンポーネントへのボタンがあります。

frontend / src /Home.jsにファイルを作成しましょう。

import React, { Component } from 'react';
import './App.css';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
import { Button, Container } from 'reactstrap';

class Home extends Component {
    render() {
        return (
            <div>
                <AppNavbar/>
                <Container fluid>
                    <Button color="link"><Link to="/clients">Clients</Link></Button>
                </Container>
            </div>
        );
    }
}
export default Home;

注:このコンポーネントには、react-router-domから/clientsにつながるLinkもあります。 このルートは次のステップで構成されます。

3.8. Reactルーターの使用

次に、 ReactRouterを使用してコンポーネント間を移動します。

App.jsを変更しましょう。

import React, { Component } from 'react';
import './App.css';
import Home from './Home';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import ClientList from './ClientList';
import ClientEdit from "./ClientEdit";

class App extends Component {
  render() {
    return (
        <Router>
          <Switch>
            <Route path='/' exact={true} component={Home}/>
            <Route path='/clients' exact={true} component={ClientList}/>
            <Route path='/clients/:id' component={ClientEdit}/>
          </Switch>
        </Router>
    )
  }
}

export default App;

ご覧のとおり、作成したコンポーネントごとにアプリケーションルートが定義されています。

localhost:3000 にアクセスすると、[X52X]ホームページにクライアントリンクが表示されます。

クライアントリンクをクリックすると、クライアントのリストが表示され、編集削除、およびクライアントの追加機能が表示されます。

4. 構築とパッケージング

ReactアプリケーションをMavenビルドしてパッケージ化するには、frontend-maven-pluginを使用します。

このプラグインは、フロントエンドアプリケーションをパッケージ化してSpringBootAPIビルドフォルダーにコピーする役割を果たします。

<properties>
    ...
    <frontend-maven-plugin.version>1.6</frontend-maven-plugin.version>
    <node.version>v14.8.0</node.version>
    <yarn.version>v1.12.1</yarn.version>
    ...
</properties>
...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <version>3.1.0</version>
            <executions>
                ...
            </executions>
        </plugin>
        <plugin>
            <groupId>com.github.eirslett</groupId>
            <artifactId>frontend-maven-plugin</artifactId>
            <version>${frontend-maven-plugin.version}</version>
            <configuration>
                ...
            </configuration>
            <executions>
                ...
            </executions>
        </plugin>
        ...
    </plugins>
</build>

maven-resources-plugin を詳しく見てみましょう。これは、フロントエンドソースをアプリケーションtargetフォルダーにコピーする役割を果たします。

...
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
        <execution>
            <id>copy-resources</id>
            <phase>process-classes</phase>
            <goals>
                <goal>copy-resources</goal>
            </goals>
            <configuration>
                <outputDirectory>${basedir}/target/classes/static</outputDirectory>
                <resources>
                    <resource>
                        <directory>frontend/build</directory>
                    </resource>
                </resources>
            </configuration>
        </execution>
    </executions>
</plugin>
...

次に、フロントエンド-maven-プラグインが、Node.jsYarnをインストールし、フロントエンドをビルドしてテストします。 ] 応用:

...
<plugin>
    <groupId>com.github.eirslett</groupId>
    <artifactId>frontend-maven-plugin</artifactId>
    <version>${frontend-maven-plugin.version}</version>
    <configuration>
        <workingDirectory>frontend</workingDirectory>
    </configuration>
    <executions>
        <execution>
            <id>install node</id>
            <goals>
                <goal>install-node-and-yarn</goal>
            </goals>
            <configuration>
                <nodeVersion>${node.version}</nodeVersion>
                <yarnVersion>${yarn.version}</yarnVersion>
            </configuration>
        </execution>
        <execution>
            <id>yarn install</id>
            <goals>
                <goal>yarn</goal>
            </goals>
            <phase>generate-resources</phase>
        </execution>
        <execution>
            <id>yarn test</id>
            <goals>
                <goal>yarn</goal>
            </goals>
            <phase>test</phase>
            <configuration>
                <arguments>test</arguments>
                <environmentVariables>
                    <CI>true</CI>
                </environmentVariables>
            </configuration>
        </execution>
        <execution>
            <id>yarn build</id>
            <goals>
                <goal>yarn</goal>
            </goals>
            <phase>compile</phase>
            <configuration>
                <arguments>build</arguments>
            </configuration>
        </execution>
    </executions>
</plugin>
...

注:別のNode.jsバージョンを指定するには、pom.xmlnode.versionプロパティを編集するだけです。

5. Spring BootReactCRUDアプリケーションの実行

最後に、プラグインを追加することで、次のコマンドを実行してアプリケーションにアクセスできます。

mvn spring-boot:run

Reactアプリケーションは、 http:// localhost:8080 /URLでAPIに完全に統合されます。

6. 結論

この記事では、Spring BootandReactを使用してCRUDアプリケーションを作成する方法を検討しました。 そのために、最初にデータベースと対話するためのいくつかのRESTAPIエンドポイントを作成しました。 次に、APIを使用してデータをフェッチおよび書き込みするためのReactコンポーネントをいくつか作成しました。 また、ReactUIを使用してSpringBootアプリケーションを単一のアプリケーションパッケージにパッケージ化する方法も学びました。

このアプリケーションのソースコードは、GitHubから入手できます。