diff --git a/src/components/NavigationDrawer.vue b/src/components/NavigationDrawer.vue
index 3d4202e4a4749b7d5490b7c2eb48587965ae64c0..c247b9728334a28b0fc29cb34b310d69d183113f 100644
--- a/src/components/NavigationDrawer.vue
+++ b/src/components/NavigationDrawer.vue
@@ -48,6 +48,22 @@
           </v-list-item-content>
         </v-list-item>
       </v-list-item-group>
+      <v-subheader>일정 관리</v-subheader>
+      <v-list-item-group
+        color="primary"
+      >
+        <v-list-item
+          link
+          :to="{ name: 'ScheduleList' }"
+        >
+          <v-list-item-icon>
+            <v-icon>mdi-shape</v-icon>
+          </v-list-item-icon>
+          <v-list-item-content>
+            <v-list-item-title>일정 목록</v-list-item-title>
+          </v-list-item-content>
+        </v-list-item>
+      </v-list-item-group>
       <v-subheader>매장 관리</v-subheader>
       <v-list-item-group
         color="primary"
diff --git a/src/router/index.js b/src/router/index.js
index 5829299ef8df2b987ba2e634334520b7e90f226a..21b2f13764fc21f7eaa54e93dd7520d5e2946c23 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -33,6 +33,11 @@ const routes = [
     name: 'ProgramDetail',
     component: () => import('@/views/ProgramDetail.vue'),
   },
+  {
+    path: '/schedule',
+    name: 'ScheduleList',
+    component: () => import('@/views/ScheduleList.vue'),
+  },
   {
     path: '/trainee',
     name: 'TraineeList',
diff --git a/src/settings/api.js b/src/settings/api.js
index f1779dd8ce3b0c2943a587ca19db920f24f01ef9..df8a2ad7109002b5acfb10feea4e2130f5a0c2e3 100644
--- a/src/settings/api.js
+++ b/src/settings/api.js
@@ -14,6 +14,10 @@ export default {
       list: `${host}/program`,
       detail: id => (`${host}/program/${id}`),
     },
+    schedule: {
+      list: `${host}/schedule`,
+      detail: id => (`${host}/schedule/${id}`),
+    },
     trainee: {
       list: `${host}/trainee`,
       detail: id => (`${host}/trainee/${id}`),
diff --git a/src/views/ScheduleList.vue b/src/views/ScheduleList.vue
new file mode 100644
index 0000000000000000000000000000000000000000..eaee0d0a570e3936528b69212aa7b28876c8065b
--- /dev/null
+++ b/src/views/ScheduleList.vue
@@ -0,0 +1,199 @@
+<template>
+  <div class="schedule-list">
+    <v-card>
+      <v-card-title>일정 목록</v-card-title>
+      <v-card-text>
+        <v-row
+          justify="end"
+        >
+          <v-col
+            align-self="center"
+            cols="12"
+            sm="8"
+            md="9"
+            lg="9"
+            xl="10"
+          >
+            <v-btn
+              color="success"
+              outlined
+              @click="createSchedule"
+            >
+              <v-icon
+                left
+              >
+                mdi-plus
+              </v-icon>
+              새 일정 등록
+            </v-btn>
+          </v-col>
+          <v-col
+            cols="12"
+            sm="4"
+            md="3"
+            lg="3"
+            xl="2"
+          >
+            <v-text-field
+              append-icon="mdi-magnify"
+              clearable
+              label="일정 검색"
+              v-model="scheduleList.searchKeyword"
+            ></v-text-field>
+          </v-col>
+        </v-row>
+        <v-data-table
+          :headers="scheduleList.headers"
+          item-key="id"
+          :items="scheduleList.data"
+          :loading="isProcessing"
+          loading-text="데이터를 불러오는 중입니다."
+          :search="scheduleList.searchKeyword"
+          no-results-text="일치하는 일정을 찾지 못했습니다."
+        >
+          <template v-slot:item.action="{ item }">
+            <v-icon
+              small
+              class="mr-2"
+              @click="editSchedule(item.id)"
+            >
+              mdi-pencil
+            </v-icon>
+            <v-icon
+              small
+              @click="deleteSchedule(item.id)"
+            >
+              mdi-delete
+            </v-icon>
+          </template>
+        </v-data-table>
+      </v-card-text>
+    </v-card>
+  </div>
+</template>
+
+<script>
+import moment from 'moment';
+
+import APISetting from '@/settings/api';
+
+export default {
+  name: 'ScheduleList',
+
+  data: () => ({
+    isProcessing: false,
+    error: {
+      isError: false,
+      message: '',
+    },
+    scheduleList: {
+      headers: [
+        {
+          text: '#',
+          value: 'id',
+        },
+        {
+          text: '프로그램',
+          value: 'program.title',
+        },
+        {
+          text: '트레이너',
+          value: 'trainer.name',
+        },
+        {
+          text: '시작 시각',
+          value: 'startAt',
+        },
+        {
+          text: '소요시간(분)',
+          value: 'program.durationTime',
+        },
+        {
+          text: '최대 예약 가능 인원',
+          value: 'maxTrainee',
+        },
+        {
+          text: '현재 예약 인원',
+          value: 'trainees.length',
+        },
+        {
+          text: '현재 예약 가능 인원',
+          value: 'remainTrainee',
+        },
+        {
+          text: '',
+          value: 'action',
+          sortable: false,
+        },
+      ],
+      data: [],
+      searchKeyword: '',
+    },
+  }),
+
+  methods: {
+    getScheduleList() {
+      this.error.isError = false;
+      this.error.message = '';
+      this.isProcessing = true;
+
+      fetch(APISetting.endpoints.schedule.list, APISetting.settings.get)
+        .then((res) => {
+          if (res.status === 200) return res.json();
+          throw new Error('알 수 없는 응답입니다.');
+        })
+        .then((json) => {
+          const { schedules } = json;
+          schedules.forEach((schedule) => {
+            // eslint-disable-next-line no-param-reassign
+            schedule.startAt = moment(schedule.startAt).format('YYYY-MM-DD HH:mm');
+            // eslint-disable-next-line no-param-reassign
+            schedule.remainTrainee = schedule.maxTrainee - schedule.trainees.length;
+          });
+          this.scheduleList.data = schedules;
+        })
+        .catch((e) => {
+          this.error.message = e.message;
+          this.error.isError = true;
+        })
+        .finally(() => {
+          this.isProcessing = false;
+        });
+    },
+    createSchedule() {
+      this.$router.push('/schedule/create');
+    },
+    editSchedule(id) {
+      this.$router.push(`/schedule/${id}`);
+    },
+    deleteSchedule(id) {
+      this.error.isError = false;
+      this.error.message = '';
+      this.isProcessing = true;
+
+      fetch(APISetting.endpoints.schedule.detail(id), APISetting.settings.delete)
+        .then((res) => {
+          if (res.status === 204) return Promise.all([null, res]);
+          if ([400, 404, 500].includes(res.status)) return Promise.all([res.json(), res]);
+          throw new Error('알 수 없는 응답입니다.');
+        })
+        .then((values) => {
+          const [json, res] = values;
+          if (res.status === 204) return this.getScheduleList();
+          throw new Error(json.message);
+        })
+        .catch((e) => {
+          this.error.message = e.message;
+          this.error.isError = true;
+        })
+        .finally(() => {
+          this.isProcessing = false;
+        });
+    },
+  },
+
+  created() {
+    this.getScheduleList();
+  },
+};
+</script>