# STEP 8 — 컨트롤러 추가 (`ExperionFastController`)
## 사전 확인 (작업 전 반드시 수행)
1. `src/Web/Controllers/ExperionControllers.cs` 파일을 열어 전체 내용을 읽는다.
2. 아래 항목을 확인하고 기록한다:
- [x] STEP 7이 완료되어 `ExperionFastService`가 빌드되는가?
- [x] `ExperionFastController` 클래스가 이미 존재하는가? → 있으면 내용 비교 후 누락 엔드포인트만 추가
- [x] `PinRequest` record가 이미 존재하는가?
- [x] 파일 상단에 `using Microsoft.AspNetCore.Mvc;` 가 있는가?
---
## 작업 내용
**파일**: `src/Web/Controllers/ExperionControllers.cs`
**위치**: 파일 하단 (기존 컨트롤러 마지막 클래스 아래)
```csharp
// ── FastTable / FastRecord ────────────────────────────────────────────────────
[ApiController]
[Route("api/fast")]
public class ExperionFastController : ControllerBase
{
private readonly IExperionFastService _fastSvc;
public ExperionFastController(IExperionFastService fastSvc)
=> _fastSvc = fastSvc;
/// 새 fastSession 시작
[HttpPost("start")]
public async Task Start([FromBody] FastSessionStartRequest request)
{
try
{
var session = await _fastSvc.StartSessionAsync(request);
return Ok(new { id = session.Id, name = session.Name, status = session.Status, startedAt = session.StartedAt });
}
catch (ArgumentException ex) { return BadRequest(new { error = ex.Message }); }
catch (InvalidOperationException ex) { return Conflict(new { error = ex.Message }); }
}
/// 세션 중지
[HttpPost("{id:int}/stop")]
public async Task Stop(int id)
{
try
{
await _fastSvc.StopSessionAsync(id);
return Ok(new { success = true, message = "세션이 중지되었습니다." });
}
catch (InvalidOperationException ex) { return NotFound(new { error = ex.Message }); }
}
/// 세션 목록 조회
[HttpGet("sessions")]
public async Task GetSessions()
{
var sessions = await _fastSvc.GetSessionsAsync();
return Ok(new
{
total = sessions.Count(),
items = sessions.Select(s => new
{
id = s.Id,
name = s.Name,
status = s.Status,
samplingMs = s.SamplingMs,
durationSec = s.DurationSec,
tagCount = s.TagList.Length,
rowCount = s.RowCount,
startedAt = s.StartedAt,
endedAt = s.EndedAt,
retentionDays = s.RetentionDays,
pinned = s.Pinned
})
});
}
/// 세션 상세 정보
[HttpGet("{id:int}")]
public async Task GetSession(int id)
{
var session = await _fastSvc.GetSessionAsync(id);
if (session == null) return NotFound();
return Ok(new
{
id = session.Id,
name = session.Name,
status = session.Status,
samplingMs = session.SamplingMs,
durationSec = session.DurationSec,
tagList = session.TagList,
rowCount = session.RowCount,
startedAt = session.StartedAt,
endedAt = session.EndedAt,
retentionDays = session.RetentionDays,
pinned = session.Pinned
});
}
/// 레코드 조회 (Long 포맷)
[HttpGet("{id:int}/records")]
public async Task GetRecords(int id,
[FromQuery] DateTime? from,
[FromQuery] DateTime? to,
[FromQuery] string format = "long")
{
var result = await _fastSvc.GetRecordsAsync(id, from, to, format);
return Ok(new
{
sessionId = result.SessionId,
from = result.From,
to = result.To,
tagNames = result.TagNames,
total = result.TotalCount,
items = result.Items.Select(r => new
{
sessionId = r.SessionId,
recordedAt = r.RecordedAt,
tagName = r.TagName,
value = r.Value
})
});
}
/// CSV Export (스트리밍)
[HttpGet("{id:int}/csv")]
public async Task ExportCsv(int id,
[FromQuery] DateTime? from,
[FromQuery] DateTime? to)
{
var ms = new MemoryStream();
await _fastSvc.ExportCsvAsync(id, ms, from, to);
ms.Position = 0;
return File(ms, "text/csv", $"fast-{id}-{DateTime.Now:yyyyMMddHHmm}.csv");
}
/// 세션 삭제
[HttpDelete("{id:int}")]
public async Task Delete(int id)
{
try
{
await _fastSvc.DeleteSessionAsync(id);
return Ok(new { success = true, message = "세션이 삭제되었습니다." });
}
catch (InvalidOperationException ex) { return NotFound(new { error = ex.Message }); }
}
/// 세션 고정/해제
[HttpPost("{id:int}/pin")]
public async Task Pin(int id, [FromBody] PinRequest request)
{
try
{
await _fastSvc.PinSessionAsync(id, request.Pinned);
return Ok(new { success = true, pinned = request.Pinned });
}
catch (InvalidOperationException ex) { return NotFound(new { error = ex.Message }); }
}
}
public record PinRequest(bool Pinned);
```
---
## 사후 확인 (작업 후 반드시 수행)
1. `ExperionControllers.cs` 파일을 다시 열어 추가된 컨트롤러를 읽는다.
2. 아래 항목을 하나씩 확인한다:
- [x] `[Route("api/fast")]` 라우트가 맞는가?
- [x] `POST /start` 엔드포인트 존재
- [x] `POST /{id}/stop` 엔드포인트 존재
- [x] `GET /sessions` 엔드포인트 존재
- [x] `GET /{id}` 엔드포인트 존재
- [x] `GET /{id}/records` 엔드포인트 존재
- [x] `GET /{id}/csv` 엔드포인트 존재
- [x] `DELETE /{id}` 엔드포인트 존재
- [x] `POST /{id}/pin` 엔드포인트 존재 (총 8개 엔드포인트)
- [x] `PinRequest` record 존재 (중복 선언 아닌지 확인)
3. `dotnet build src/Web` 실행 → 에러/경고 0개 확인
4. 문제가 있으면 수정 후 다시 빌드 확인
---
## 완료 조건
- `dotnet build src/Web` 결과: 에러 0, 경고 0
- `ExperionFastController` 8개 엔드포인트 모두 존재