2つのプログラムを比較するアプリを作成しよう！

## はじめに
初めましての人もそうでない人もこんにちは！

皆さんはプログラムの勉強を勉強をしていますでしょうか？

私は動画サイトで勉強したりとか学校の課題で行う場面が多いですね！

私の最近の悩みとしては資料通りにプログラムを入力したつもりがスペルミスをしていたり大文字と小文字を間違えてしまったりとかがありますね〜

これらはエラーが出るので良心的ですがエラーが出ていないのに動いてしまうパターンがいちばん厄介でどこが違うのか本当にわかりにくいんですよね〜

絶対にどこかのコードが間違っているのでよくよく探してみると抜けているコードがあったりなど本当に厄介でした！

もちろんパソコンのショートカットキーを使ってコピペすれば手っ取り早いですがそれでは自分の成長には繋がりません！

どこを間違えてどうして間違えたのか理解しないといけないのでそれは避けたいです！

なのでどこの行が違うのか探すことが容易になればやりやすいと思ったので今回は2つのプログラムを比較できるアプリを作成していきたいと思います！

## 今回の目標
1.どの文章が違うのか行で判断させる！
2.違う文章があった場合はその行全体に赤でマーカーを引く！

この2点を意識して作成していきます！

## 扱う言語・技術
### フロントエンド
React + TypeScript

### バックエンド
Go

## ディレクトリ構成
今回作成するディレクトリ構成です！
```
hikaku/
│
├── frontend/
│    ├── src/
│    │   ├── App.tsx
│    │   ├── App.css
│    │   ├── components/
│    │   │    └── TextComparison.tsx
│    │   └── ...
│    └── ...
└── backend/
     ├── main.go
     ├── go.sum
     └── go.mod
```

## 準備しよう！
それではhikakuというフォルダを作成してhikakuディレクトリに移動したら以下のターミナルコマンドを入力してください！
### フロントエンドの準備
```
npx create-react-app frontend --template typescript
cd frontend
npm install axios
```
### バックエンドの準備
もうひとつターミナルを作成してhikakuディレクトリに戻ったら以下のコマンドを入力してください!
```
mkdir backend
cd backend
go mod init backend
go get github.com/gin-contrib/cors
go get github.com/gin-gonic/gin
```
これでフロントエンドとバックエンドの開発準備が完了しました！次から作成していきましょう！

## 作っていこう!
### フロントエンド
以下のコードをコピペしてください！
```src/App.tsx
import React from 'react';
import TextComparison from './components/TextComparison';

const App: React.FC = () => {
  return (
    <div className="App">
      <h1>比較アプリ</h1>
      <TextComparison />
    </div>
  );
};

export default App;
```

```src/components/TextComparison.tsx
import React, { useState } from 'react';
import axios from 'axios';

const TextComparison: React.FC = () => {
  const [text1, setText1] = useState('');
  const [text2, setText2] = useState('');
  const [comparison, setComparison] = useState('');
  const [text1Html, setText1Html] = useState('');
  const [text2Html, setText2Html] = useState('');

  const handleCompare = async () => {
    try {
      const response = await axios.post('http://localhost:8080/compare', { text1, text2 });
      setComparison(response.data.comparison);
      setText1Html(response.data.text1Html);
      setText2Html(response.data.text2Html);
    } catch (error) {
      console.error('Error comparing texts:', error);
      setComparison('Error occurred while comparing texts');
      setText1Html('');
      setText2Html('');
    }
  };

  return (
    <div>
      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
        <textarea
          value={text1}
          onChange={(e) => setText1(e.target.value)}
          placeholder="文章を入れてください"
          style={{ width: '45%', height: '150px' }}
        />
        <textarea
          value={text2}
          onChange={(e) => setText2(e.target.value)}
          placeholder="文章を入れてください"
          style={{ width: '45%', height: '150px' }}
        />
      </div>
      <button onClick={handleCompare} style={{ margin: '10px 0' }}>比較する</button>
      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
        <div style={{ width: '45%' }}>
          <h4>文章1:</h4>
          <div 
            dangerouslySetInnerHTML={{ __html: text1Html }} 
            style={{ whiteSpace: 'pre-wrap', fontFamily: 'monospace' }}
          />
        </div>
        <div style={{ width: '45%' }}>
          <h4>文章2:</h4>
          <div 
            dangerouslySetInnerHTML={{ __html: text2Html }} 
            style={{ whiteSpace: 'pre-wrap', fontFamily: 'monospace' }}
          />
        </div>
      </div>
    </div>
  );
};

export default TextComparison;
```

このようになるようにコピペしてくd・・・

(心の声：今回の導入でコピペしたら成長しないって書いたよな〜。なのにコピペしてくださいっていうのはおかしい気が・・・)

頑張って手を動かして作成してください！！

### バックエンド
お疲れ様です！おそらくこれをみている頃には手がガタガタになって辛いとは思いますがこの調子で次のコードも"手打ちで!"頑張ってください
```backend/main.go
package main

import (
	"html"
	"net/http"
	"strings"

	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
)

type CompareRequest struct {
	Text1 string `json:"text1"`
	Text2 string `json:"text2"`
}

type CompareResponse struct {
	Comparison  string `json:"comparison"`
	Text1HTML   string `json:"text1Html"`
	Text2HTML   string `json:"text2Html"`
	Differences int    `json:"differences"`
}

func main() {
	r := gin.Default()

	config := cors.DefaultConfig()
	config.AllowOrigins = []string{"http://localhost:3000"}
	r.Use(cors.New(config))

	r.POST("/compare", compareTexts)

	r.Run(":8080")
}

func compareTexts(c *gin.Context) {
	var req CompareRequest
	if err := c.BindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	lines1 := strings.Split(req.Text1, "\n")
	lines2 := strings.Split(req.Text2, "\n")

	comparison := "The texts are "
	if len(lines1) != len(lines2) {
		comparison += "different in number of lines. "
	} else {
		comparison += "of the same number of lines. "
	}

	differences := 0
	text1HTML := ""
	text2HTML := ""

	maxLines := len(lines1)
	if len(lines2) > maxLines {
		maxLines = len(lines2)
	}

	for i := 0; i < maxLines; i++ {
		if i < len(lines1) && i < len(lines2) {
			words1 := strings.Fields(lines1[i])
			words2 := strings.Fields(lines2[i])

			lineHTML1 := ""
			lineHTML2 := ""

			maxWords := len(words1)
			if len(words2) > maxWords {
				maxWords = len(words2)
			}

			for j := 0; j < maxWords; j++ {
				if j < len(words1) && j < len(words2) {
					if words1[j] != words2[j] {
						differences++
						lineHTML1 += "<span style='background-color: #ffcccb'>" + html.EscapeString(words1[j]) + "</span> "
						lineHTML2 += "<span style='background-color: #ffcccb'>" + html.EscapeString(words2[j]) + "</span> "
					} else {
						lineHTML1 += html.EscapeString(words1[j]) + " "
						lineHTML2 += html.EscapeString(words2[j]) + " "
					}
				} else if j < len(words1) {
					differences++
					lineHTML1 += "<span style='background-color: #ffcccb'>" + html.EscapeString(words1[j]) + "</span> "
				} else if j < len(words2) {
					differences++
					lineHTML2 += "<span style='background-color: #ffcccb'>" + html.EscapeString(words2[j]) + "</span> "
				}
			}

			text1HTML += strings.TrimSpace(lineHTML1) + "<br>"
			text2HTML += strings.TrimSpace(lineHTML2) + "<br>"
		} else if i < len(lines1) {
			differences++
			text1HTML += "<span style='background-color: #ffcccb'>" + html.EscapeString(lines1[i]) + "</span><br>"
		} else if i < len(lines2) {
			differences++
			text2HTML += "<span style='background-color: #ffcccb'>" + html.EscapeString(lines2[i]) + "</span><br>"
		}
	}

	comparison += "There are " + string(differences) + " differences between the texts."

	response := CompareResponse{
		Comparison:  comparison,
		Text1HTML:   strings.TrimSpace(text1HTML),
		Text2HTML:   strings.TrimSpace(text2HTML),
		Differences: differences,
	}

	c.JSON(http.StatusOK, response)
}
```

お疲れ様でした！後は実行するだけとなります！

## 実行！
2つのターミナルを用意してそれぞれfrontendとbackendのディレクトリに移動して
```
npm start
```
と
```
go run main.go
```
を入力して実行してみてください！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3631203/dad22513-f327-d182-b924-cfcc74223b2b.png)

入力して試しにApp.tsxの内容を少し変えて試してみると直感的に異なるところがわかるようになりました！

## 終わりに
みなさんいかがだったでしょうか？
自分は英語がそこまで得意ではないのでちょくちょくスペルミスをしでかしてしまいますw
こうゆうのがあれば本当に頼もしいです！
みなさんのプログラミング技術が向上することを願っています！

これで今回の記事は終了です！
またどこかの記事でお会いしましょう！

## Github URL
https://github.com/FrohleinYoshie/Hikaku